/* * Copyright (C) 2007-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #if PLATFORM(IOS_FAMILY) #import "DOMUIKitExtensions.h" #import "DOM.h" #import "DOMCore.h" #import "DOMExtensions.h" #import "DOMHTML.h" #import "DOMHTMLAreaElementInternal.h" #import "DOMHTMLElementInternal.h" #import "DOMHTMLImageElementInternal.h" #import "DOMHTMLSelectElementInternal.h" #import "DOMInternal.h" #import "DOMNodeInternal.h" #import "DOMRangeInternal.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import using WebCore::Node; using WebCore::Position; using WebCore::Range; using WebCore::RenderBlock; using WebCore::RenderBox; using WebCore::RenderObject; using WebCore::RenderText; using WebCore::VisiblePosition; @implementation DOMRange (UIKitExtensions) - (void)move:(UInt32)amount inDirection:(WebTextAdjustmentDirection)direction { auto& range = *core(self); WebCore::FrameSelection frameSelection; frameSelection.setSelection(makeSimpleRange(range)); WebCore::TextGranularity granularity = WebCore::TextGranularity::CharacterGranularity; // Until WebKit supports vertical layout, "down" is equivalent to "forward by a line" and // "up" is equivalent to "backward by a line". if (direction == WebTextAdjustmentDown) { direction = WebTextAdjustmentForward; granularity = WebCore::TextGranularity::LineGranularity; } else if (direction == WebTextAdjustmentUp) { direction = WebTextAdjustmentBackward; granularity = WebCore::TextGranularity::LineGranularity; } for (UInt32 i = 0; i < amount; i++) frameSelection.modify(WebCore::FrameSelection::AlterationMove, (WebCore::SelectionDirection)direction, granularity); Position start = frameSelection.selection().start().parentAnchoredEquivalent(); Position end = frameSelection.selection().end().parentAnchoredEquivalent(); if (start.containerNode()) range.setStart(*start.containerNode(), start.offsetInContainerNode()); if (end.containerNode()) range.setEnd(*end.containerNode(), end.offsetInContainerNode()); } - (void)extend:(UInt32)amount inDirection:(WebTextAdjustmentDirection)direction { auto& range = *core(self); WebCore::FrameSelection frameSelection; frameSelection.setSelection(makeSimpleRange(range)); for (UInt32 i = 0; i < amount; i++) frameSelection.modify(WebCore::FrameSelection::AlterationExtend, (WebCore::SelectionDirection)direction, WebCore::TextGranularity::CharacterGranularity); Position start = frameSelection.selection().start().parentAnchoredEquivalent(); Position end = frameSelection.selection().end().parentAnchoredEquivalent(); if (start.containerNode()) range.setStart(*start.containerNode(), start.offsetInContainerNode()); if (end.containerNode()) range.setEnd(*end.containerNode(), end.offsetInContainerNode()); } // FIXME: Refactor to share code with intersectingNodesWithDeprecatedZeroOffsetStartQuirk. static WebCore::Node* firstNodeAfter(const WebCore::BoundaryPoint& point) { if (point.container->isCharacterDataNode()) return point.container.ptr(); if (auto child = point.container->traverseToChildAt(point.offset)) return child; if (!point.offset) return point.container.ptr(); return WebCore::NodeTraversal::nextSkippingChildren(point.container); } - (DOMNode *)firstNode { return kit(firstNodeAfter(makeSimpleRange(*core(self)).start)); } @end //------------------- @implementation DOMNode (UIKitExtensions) // NOTE: Code blatantly copied from [WebInspector _hightNode:] in WebKit/WebInspector/WebInspector.m@19861 - (NSArray *)boundingBoxes { NSArray *rects = nil; NSRect bounds = [self boundingBox]; if (!NSIsEmptyRect(bounds)) { if ([self isKindOfClass:[DOMElement class]]) { DOMDocument *document = [self ownerDocument]; DOMCSSStyleDeclaration *style = [document getComputedStyle:(DOMElement *)self pseudoElement:@""]; if ([[style getPropertyValue:@"display"] isEqualToString:@"inline"]) rects = [self lineBoxRects]; } else if ([self isKindOfClass:[DOMText class]]) rects = [self lineBoxRects]; } if (![rects count]) rects = @[[NSValue valueWithRect:bounds]]; return rects; } - (NSArray *)absoluteQuads { NSArray *quads = nil; NSRect bounds = [self boundingBox]; if (!NSIsEmptyRect(bounds)) { if ([self isKindOfClass:[DOMElement class]]) { DOMDocument *document = [self ownerDocument]; DOMCSSStyleDeclaration *style = [document getComputedStyle:(DOMElement *)self pseudoElement:@""]; if ([[style getPropertyValue:@"display"] isEqualToString:@"inline"]) quads = [self lineBoxQuads]; } else if ([self isKindOfClass:[DOMText class]]) quads = [self lineBoxQuads]; } if (![quads count]) quads = @[adoptNS([[WKQuadObject alloc] initWithQuad:[self absoluteQuad]]).get()]; return quads; } - (NSArray *)borderRadii { RenderObject* renderer = core(self)->renderer(); if (is(renderer)) { WebCore::RoundedRect::Radii radii = downcast(*renderer).borderRadii(); return @[[NSValue valueWithSize:(WebCore::FloatSize)radii.topLeft()], [NSValue valueWithSize:(WebCore::FloatSize)radii.topRight()], [NSValue valueWithSize:(WebCore::FloatSize)radii.bottomLeft()], [NSValue valueWithSize:(WebCore::FloatSize)radii.bottomRight()]]; } NSValue *emptyValue = [NSValue valueWithSize:CGSizeZero]; return @[emptyValue, emptyValue, emptyValue, emptyValue]; } - (BOOL)containsOnlyInlineObjects { RenderObject* renderer = core(self)->renderer(); return renderer && renderer->childrenInline() && (is(*renderer) && !downcast(*renderer).inlineContinuation()) && !renderer->isTable(); } - (BOOL)isSelectableBlock { RenderObject* renderer = core(self)->renderer(); return renderer && (is(*renderer) || (is(*renderer) && downcast(*renderer).inlineContinuation() != nil)); } - (DOMRange *)rangeOfContainingParagraph { VisiblePosition position(makeContainerOffsetPosition(core(self), 0)); return kit(makeSimpleRange(startOfParagraph(position), endOfParagraph(position))); } - (CGFloat)textHeight { RenderObject* renderer = core(self)->renderer(); if (is(renderer)) return downcast(*renderer).style().computedLineHeight(); return CGFLOAT_MAX; } - (DOMNode *)findExplodedTextNodeAtPoint:(CGPoint)point { auto* renderer = core(self)->renderer(); if (!is(renderer)) return nil; auto* renderText = downcast(*renderer).findClosestTextAtAbsolutePoint(point); if (renderText && renderText->textNode()) return kit(renderText->textNode()); return nil; } @end //----------------- @implementation DOMElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return 0; } @end @implementation DOMHTMLElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { int result = 0; RenderObject * renderer = core(self)->renderer(); if (renderer) { if (renderer->isFloatingOrOutOfFlowPositioned() || renderer->isWidget()) { result = INT_MAX; } else if (!renderer->firstChildSlow()) { result = 0; } else if (is(*renderer) || (is(*renderer) && downcast(*renderer).inlineContinuation())) { BOOL noCost = NO; if (auto renderBox = dynamicDowncast(*renderer)) { auto* parentRenderBox = dynamicDowncast(renderBox->parent()); if (parentRenderBox && renderBox->width() == parentRenderBox->width()) noCost = YES; } result = (noCost ? 0 : 1); } else if (renderer->hasTransform()) { result = INT_MAX; } } return result; } @end @implementation DOMHTMLBodyElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return 0; } @end // Maximally complex elements @implementation DOMHTMLFormElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end @implementation DOMHTMLTableElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end @implementation DOMHTMLFrameElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end @implementation DOMHTMLIFrameElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end @implementation DOMHTMLButtonElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end @implementation DOMHTMLTextAreaElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end @implementation DOMHTMLInputElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end @implementation DOMHTMLSelectElement (DOMUIKitComplexityExtensions) - (int)structuralComplexityContribution { return INT_MAX; } @end //----------------- @implementation DOMHTMLAreaElement (DOMUIKitExtensions) - (CGRect)boundingBoxWithOwner:(DOMNode *)owner { // ignores transforms return owner ? snappedIntRect(core(self)->computeRect(core(owner)->renderer())) : CGRectZero; } - (WKQuad)absoluteQuadWithOwner:(DOMNode *)owner { if (owner) { WebCore::IntRect rect = snappedIntRect(core(self)->computeRect(core(owner)->renderer())); WKQuad quad; quad.p1 = CGPointMake(rect.x(), rect.y()); quad.p2 = CGPointMake(rect.maxX(), rect.y()); quad.p3 = CGPointMake(rect.maxX(), rect.maxY()); quad.p4 = CGPointMake(rect.x(), rect.maxY()); return quad; } WKQuad zeroQuad = { CGPointZero, CGPointZero, CGPointZero, CGPointZero }; return zeroQuad; } - (NSArray *)boundingBoxesWithOwner:(DOMNode *)owner { return @[[NSValue valueWithRect:[self boundingBoxWithOwner:owner]]]; } - (NSArray *)absoluteQuadsWithOwner:(DOMNode *)owner { return @[adoptNS([[WKQuadObject alloc] initWithQuad:[self absoluteQuadWithOwner:owner]]).get()]; } @end @implementation DOMHTMLSelectElement (DOMUIKitExtensions) - (unsigned)completeLength { return core(self)->listItems().size(); } - (DOMNode *)listItemAtIndex:(int)anIndex { return kit(core(self)->listItems()[anIndex]); } @end @implementation DOMHTMLImageElement (WebDOMHTMLImageElementOperationsPrivate) - (NSData *)dataRepresentation:(BOOL)rawImageData { auto* cachedImage = core(self)->cachedImage(); if (!cachedImage) return nil; auto* image = cachedImage->image(); if (!image) return nil; auto* data = rawImageData ? cachedImage->resourceBuffer() : image->data(); if (!data) return nil; return data->makeContiguous()->createNSData().autorelease(); } - (NSString *)mimeType { auto* cachedImage = core(self)->cachedImage(); if (!cachedImage || !cachedImage->image()) return nil; return cachedImage->response().mimeType(); } @end #endif