/* * Copyright (C) 2007, 2008, 2009, 2013 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. * */ #import "config.h" #if PLATFORM(IOS) #import "DOMUIKitExtensions.h" #import "CachedImage.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 "FloatPoint.h" #import "FontCascade.h" #import "FrameSelection.h" #import "HTMLAreaElement.h" #import "htmlediting.h" #import "HTMLImageElement.h" #import "HTMLInputElement.h" #import "HTMLSelectElement.h" #import "HTMLTextAreaElement.h" #import "Image.h" #import "InlineBox.h" #import "Node.h" #import "Range.h" #import "RenderBlock.h" #import "RenderBlockFlow.h" #import "RenderBox.h" #import "RoundedRect.h" #import "RenderObject.h" #import "RenderStyleConstants.h" #import "RenderText.h" #import "SharedBuffer.h" #import "VisiblePosition.h" #import "VisibleUnits.h" #import "WAKAppKitStubs.h" using namespace WebCore; using WebCore::FloatPoint; using WebCore::FontCascade; using WebCore::HTMLAreaElement; using WebCore::HTMLImageElement; using WebCore::HTMLSelectElement; using WebCore::InlineBox; using WebCore::IntRect; using WebCore::Node; using WebCore::Position; using WebCore::Range; using WebCore::RenderBlock; using WebCore::RenderBox; using WebCore::RenderObject; using WebCore::RenderStyle; using WebCore::RenderText; using WebCore::RootInlineBox; using WebCore::VisiblePosition; @implementation DOMRange (UIKitExtensions) - (void)move:(UInt32)amount inDirection:(WebTextAdjustmentDirection)direction { Range *range = core(self); FrameSelection frameSelection; frameSelection.moveTo(range); TextGranularity granularity = 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 = LineGranularity; } else if (direction == WebTextAdjustmentUp) { direction = WebTextAdjustmentBackward; granularity = LineGranularity; } for (UInt32 i = 0; i < amount; i++) frameSelection.modify(FrameSelection::AlterationMove, (SelectionDirection)direction, granularity); ExceptionCode ignored; Position start = frameSelection.selection().start().parentAnchoredEquivalent(); Position end = frameSelection.selection().end().parentAnchoredEquivalent(); range->setStart(start.containerNode(), start.offsetInContainerNode(), ignored); range->setEnd(end.containerNode(), end.offsetInContainerNode(), ignored); } - (void)extend:(UInt32)amount inDirection:(WebTextAdjustmentDirection)direction { Range *range = core(self); FrameSelection frameSelection; frameSelection.moveTo(range); for (UInt32 i = 0; i < amount; i++) frameSelection.modify(FrameSelection::AlterationExtend, (SelectionDirection)direction, CharacterGranularity); ExceptionCode ignored; Position start = frameSelection.selection().start().parentAnchoredEquivalent(); Position end = frameSelection.selection().end().parentAnchoredEquivalent(); range->setStart(start.containerNode(), start.offsetInContainerNode(), ignored); range->setEnd(end.containerNode(), end.offsetInContainerNode(), ignored); } - (DOMNode *)firstNode { return kit(core(self)->firstNode()); } @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 = [NSArray arrayWithObject:[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]) { WKQuadObject* quadObject = [[WKQuadObject alloc] initWithQuad:[self absoluteQuad]]; quads = [NSArray arrayWithObject:quadObject]; [quadObject release]; } return quads; } - (NSArray *)borderRadii { RenderObject* renderer = core(self)->renderer(); if (is(renderer)) { RoundedRect::Radii radii = downcast(*renderer).borderRadii(); return @[[NSValue valueWithSize:(FloatSize)radii.topLeft()], [NSValue valueWithSize:(FloatSize)radii.topRight()], [NSValue valueWithSize:(FloatSize)radii.bottomLeft()], [NSValue valueWithSize:(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).inlineElementContinuation()) && !renderer->isTable(); } - (BOOL)isSelectableBlock { RenderObject* renderer = core(self)->renderer(); return renderer && (is(*renderer) || (is(*renderer) && downcast(*renderer).inlineElementContinuation() != nil)); } - (DOMRange *)rangeOfContainingParagraph { DOMRange *result = nil; Node *node = core(self); VisiblePosition visiblePosition(createLegacyEditingPosition(node, 0), WebCore::DOWNSTREAM); VisiblePosition visibleParagraphStart = startOfParagraph(visiblePosition); VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePosition); Position paragraphStart = visibleParagraphStart.deepEquivalent().parentAnchoredEquivalent(); Position paragraphEnd = visibleParagraphEnd.deepEquivalent().parentAnchoredEquivalent(); if (paragraphStart.isNotNull() && paragraphEnd.isNotNull()) { PassRefPtr range = Range::create(*node->ownerDocument(), paragraphStart, paragraphEnd); result = kit(range.get()); } return result; } - (CGFloat)textHeight { RenderObject* renderer = core(self)->renderer(); if (is(renderer)) return downcast(*renderer).style().computedLineHeight(); return CGFLOAT_MAX; } - (DOMNode *)findExplodedTextNodeAtPoint:(CGPoint)point { // A light, non-recursive version of RenderBlock::positionForCoordinates that looks at // whether a point lies within the gaps between its root line boxes, to be called against // a node returned from elementAtPoint. We make the assumption that either the node or one // of its immediate children contains the root line boxes in question. // See for context. RenderObject* renderer = core(self)->renderer(); if (!is(renderer)) return nil; RenderBlock* block = downcast(renderer); FloatPoint absPoint(point); FloatPoint localPoint = block->absoluteToLocal(absPoint); if (!block->childrenInline()) { // Look among our immediate children for an alternate box that contains the point. for (RenderBox* child = block->firstChildBox(); child; child = child->nextSiblingBox()) { if (child->height() == 0 || child->style().visibility() != WebCore::VISIBLE || child->isFloatingOrOutOfFlowPositioned()) continue; float top = child->y(); RenderBox* nextChild = child->nextSiblingBox(); while (nextChild && nextChild->isFloatingOrOutOfFlowPositioned()) nextChild = nextChild->nextSiblingBox(); if (!nextChild) { if (localPoint.y() >= top) { block = downcast(child); break; } continue; } float bottom = nextChild->y(); if (localPoint.y() >= top && localPoint.y() < bottom && is(*child)) { block = downcast(child); break; } } if (!block->childrenInline()) return nil; localPoint = block->absoluteToLocal(absPoint); } RenderBlockFlow& blockFlow = downcast(*block); // Only check the gaps between the root line boxes. We deliberately ignore overflow because // experience has shown that hit tests on an exploded text node can fail when within the // overflow region. for (RootInlineBox* current = blockFlow.firstRootBox(); current && current != blockFlow.lastRootBox(); current = current->nextRootBox()) { float currentBottom = current->y() + current->logicalHeight(); if (localPoint.y() < currentBottom) return nil; RootInlineBox* next = current->nextRootBox(); float nextTop = next->y(); if (localPoint.y() < nextTop) { InlineBox* inlineBox = current->closestLeafChildForLogicalLeftPosition(localPoint.x()); if (inlineBox && inlineBox->behavesLikeText() && is(inlineBox->renderer())) { RenderText& renderText = downcast(inlineBox->renderer()); if (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->isEmpty()) { result = 0; } else if (is(*renderer) || (is(*renderer) && downcast(*renderer).inlineElementContinuation())) { BOOL noCost = NO; if (is(*renderer)) { RenderBox& asBox = renderer->enclosingBox(); RenderObject* parent = asBox.parent(); RenderBox* parentRenderBox = is(parent) ? downcast(parent) : nullptr; if (parentRenderBox && asBox.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 *)anOwner { // ignores transforms return anOwner ? snappedIntRect(core(self)->computeRect(core(anOwner)->renderer())) : CGRectZero; } - (WKQuad)absoluteQuadWithOwner:(DOMNode *)anOwner { if (anOwner) { // FIXME: ECLAIR //WebCore::FloatQuad theQuad = core(self)->getAbsoluteQuad(core(anOwner)->renderer()); WebCore::IntRect rect = snappedIntRect(core(self)->computeRect(core(anOwner)->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 *)anOwner { return [NSArray arrayWithObject:[NSValue valueWithRect:[self boundingBoxWithOwner:anOwner]]]; } - (NSArray *)absoluteQuadsWithOwner:(DOMNode *)anOwner { WKQuadObject* quadObject = [[WKQuadObject alloc] initWithQuad:[self absoluteQuadWithOwner:anOwner]]; NSArray* quadArray = [NSArray arrayWithObject:quadObject]; [quadObject release]; return quadArray; } @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 { WebCore::CachedImage* cachedImage = core(self)->cachedImage(); if (!cachedImage) return nil; WebCore::Image* image = cachedImage->image(); if (!image) return nil; WebCore::SharedBuffer* data = rawImageData ? cachedImage->resourceBuffer() : image->data(); if (!data) return nil; return data->createNSData().autorelease(); } - (NSString *)mimeType { WebCore::CachedImage *cachedImage = core(self)->cachedImage(); if (!cachedImage || !cachedImage->image()) return nil; return cachedImage->response().mimeType(); } @end #endif