| /* |
| * Copyright (C) 2004-2019 Apple Inc. All rights reserved. |
| * Copyright (C) 2006 James G. Speth (speth@end.com) |
| * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) |
| * |
| * 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 "DOM.h" |
| |
| #import "ExceptionHandlers.h" |
| #import "DOMElementInternal.h" |
| #import "DOMHTMLCanvasElement.h" |
| #import "DOMHTMLTableCellElementInternal.h" |
| #import "DOMHTMLVideoElement.h" |
| #import "DOMInternal.h" |
| #import "DOMNodeInternal.h" |
| #import "DOMPrivate.h" |
| #import "DOMRangeInternal.h" |
| #import <JavaScriptCore/APICast.h> |
| #import <WebCore/CachedImage.h> |
| #import <WebCore/DragImage.h> |
| #import <WebCore/FocusController.h> |
| #import <WebCore/FontCascade.h> |
| #import <WebCore/Frame.h> |
| #import <WebCore/GeometryUtilities.h> |
| #import <WebCore/HTMLLinkElement.h> |
| #import <WebCore/HTMLNames.h> |
| #import <WebCore/HTMLParserIdioms.h> |
| #import <WebCore/HTMLTableCellElement.h> |
| #import <WebCore/Image.h> |
| #import <WebCore/JSNode.h> |
| #import <WebCore/KeyboardEvent.h> |
| #import <WebCore/MediaList.h> |
| #import <WebCore/MediaQueryEvaluator.h> |
| #import <WebCore/NodeFilter.h> |
| #import <WebCore/NodeRenderStyle.h> |
| #import <WebCore/Page.h> |
| #import <WebCore/Range.h> |
| #import <WebCore/RenderImage.h> |
| #import <WebCore/RenderView.h> |
| #import <WebCore/ScriptController.h> |
| #import <WebCore/SimpleRange.h> |
| #import <WebCore/TextIndicator.h> |
| #import <WebCore/Touch.h> |
| #import <WebCore/WebScriptObjectPrivate.h> |
| #import <wtf/HashMap.h> |
| #import <wtf/cocoa/VectorCocoa.h> |
| |
| #if PLATFORM(IOS_FAMILY) |
| #import <WebCore/WAKAppKitStubs.h> |
| #import <WebCore/WAKWindow.h> |
| #import <WebCore/WebCoreThreadMessage.h> |
| #endif |
| |
| using namespace JSC; |
| using namespace WebCore; |
| |
| //------------------------------------------------------------------------------------------ |
| // DOMNode |
| |
| typedef HashMap<const QualifiedName::QualifiedNameImpl*, Class> ObjCClassMap; |
| static ObjCClassMap* elementClassMap; |
| |
| static void addElementClass(const QualifiedName& tag, Class objCClass) |
| { |
| elementClassMap->set(tag.impl(), objCClass); |
| } |
| |
| static void createElementClassMap() |
| { |
| // Create the table. |
| elementClassMap = new ObjCClassMap; |
| |
| addElementClass(HTMLNames::aTag, [DOMHTMLAnchorElement class]); |
| addElementClass(HTMLNames::appletTag, [DOMHTMLAppletElement class]); |
| addElementClass(HTMLNames::areaTag, [DOMHTMLAreaElement class]); |
| addElementClass(HTMLNames::baseTag, [DOMHTMLBaseElement class]); |
| addElementClass(HTMLNames::basefontTag, [DOMHTMLBaseFontElement class]); |
| addElementClass(HTMLNames::bodyTag, [DOMHTMLBodyElement class]); |
| addElementClass(HTMLNames::brTag, [DOMHTMLBRElement class]); |
| addElementClass(HTMLNames::buttonTag, [DOMHTMLButtonElement class]); |
| addElementClass(HTMLNames::canvasTag, [DOMHTMLCanvasElement class]); |
| addElementClass(HTMLNames::captionTag, [DOMHTMLTableCaptionElement class]); |
| addElementClass(HTMLNames::colTag, [DOMHTMLTableColElement class]); |
| addElementClass(HTMLNames::colgroupTag, [DOMHTMLTableColElement class]); |
| addElementClass(HTMLNames::delTag, [DOMHTMLModElement class]); |
| addElementClass(HTMLNames::dirTag, [DOMHTMLDirectoryElement class]); |
| addElementClass(HTMLNames::divTag, [DOMHTMLDivElement class]); |
| addElementClass(HTMLNames::dlTag, [DOMHTMLDListElement class]); |
| addElementClass(HTMLNames::embedTag, [DOMHTMLEmbedElement class]); |
| addElementClass(HTMLNames::fieldsetTag, [DOMHTMLFieldSetElement class]); |
| addElementClass(HTMLNames::fontTag, [DOMHTMLFontElement class]); |
| addElementClass(HTMLNames::formTag, [DOMHTMLFormElement class]); |
| addElementClass(HTMLNames::frameTag, [DOMHTMLFrameElement class]); |
| addElementClass(HTMLNames::framesetTag, [DOMHTMLFrameSetElement class]); |
| addElementClass(HTMLNames::h1Tag, [DOMHTMLHeadingElement class]); |
| addElementClass(HTMLNames::h2Tag, [DOMHTMLHeadingElement class]); |
| addElementClass(HTMLNames::h3Tag, [DOMHTMLHeadingElement class]); |
| addElementClass(HTMLNames::h4Tag, [DOMHTMLHeadingElement class]); |
| addElementClass(HTMLNames::h5Tag, [DOMHTMLHeadingElement class]); |
| addElementClass(HTMLNames::h6Tag, [DOMHTMLHeadingElement class]); |
| addElementClass(HTMLNames::headTag, [DOMHTMLHeadElement class]); |
| addElementClass(HTMLNames::hrTag, [DOMHTMLHRElement class]); |
| addElementClass(HTMLNames::htmlTag, [DOMHTMLHtmlElement class]); |
| addElementClass(HTMLNames::iframeTag, [DOMHTMLIFrameElement class]); |
| addElementClass(HTMLNames::imgTag, [DOMHTMLImageElement class]); |
| addElementClass(HTMLNames::inputTag, [DOMHTMLInputElement class]); |
| addElementClass(HTMLNames::insTag, [DOMHTMLModElement class]); |
| addElementClass(HTMLNames::labelTag, [DOMHTMLLabelElement class]); |
| addElementClass(HTMLNames::legendTag, [DOMHTMLLegendElement class]); |
| addElementClass(HTMLNames::liTag, [DOMHTMLLIElement class]); |
| addElementClass(HTMLNames::linkTag, [DOMHTMLLinkElement class]); |
| addElementClass(HTMLNames::listingTag, [DOMHTMLPreElement class]); |
| addElementClass(HTMLNames::mapTag, [DOMHTMLMapElement class]); |
| addElementClass(HTMLNames::marqueeTag, [DOMHTMLMarqueeElement class]); |
| addElementClass(HTMLNames::menuTag, [DOMHTMLMenuElement class]); |
| addElementClass(HTMLNames::metaTag, [DOMHTMLMetaElement class]); |
| addElementClass(HTMLNames::objectTag, [DOMHTMLObjectElement class]); |
| addElementClass(HTMLNames::olTag, [DOMHTMLOListElement class]); |
| addElementClass(HTMLNames::optgroupTag, [DOMHTMLOptGroupElement class]); |
| addElementClass(HTMLNames::optionTag, [DOMHTMLOptionElement class]); |
| addElementClass(HTMLNames::pTag, [DOMHTMLParagraphElement class]); |
| addElementClass(HTMLNames::paramTag, [DOMHTMLParamElement class]); |
| addElementClass(HTMLNames::preTag, [DOMHTMLPreElement class]); |
| addElementClass(HTMLNames::qTag, [DOMHTMLQuoteElement class]); |
| addElementClass(HTMLNames::scriptTag, [DOMHTMLScriptElement class]); |
| addElementClass(HTMLNames::selectTag, [DOMHTMLSelectElement class]); |
| addElementClass(HTMLNames::styleTag, [DOMHTMLStyleElement class]); |
| addElementClass(HTMLNames::tableTag, [DOMHTMLTableElement class]); |
| addElementClass(HTMLNames::tbodyTag, [DOMHTMLTableSectionElement class]); |
| addElementClass(HTMLNames::tdTag, [DOMHTMLTableCellElement class]); |
| addElementClass(HTMLNames::textareaTag, [DOMHTMLTextAreaElement class]); |
| addElementClass(HTMLNames::tfootTag, [DOMHTMLTableSectionElement class]); |
| addElementClass(HTMLNames::thTag, [DOMHTMLTableCellElement class]); |
| addElementClass(HTMLNames::theadTag, [DOMHTMLTableSectionElement class]); |
| addElementClass(HTMLNames::titleTag, [DOMHTMLTitleElement class]); |
| addElementClass(HTMLNames::trTag, [DOMHTMLTableRowElement class]); |
| addElementClass(HTMLNames::ulTag, [DOMHTMLUListElement class]); |
| addElementClass(HTMLNames::videoTag, [DOMHTMLVideoElement class]); |
| addElementClass(HTMLNames::xmpTag, [DOMHTMLPreElement class]); |
| } |
| |
| static Class lookupElementClass(const QualifiedName& tag) |
| { |
| // Do a special lookup to ignore element prefixes |
| if (tag.hasPrefix()) |
| return elementClassMap->get(QualifiedName(nullAtom(), tag.localName(), tag.namespaceURI()).impl()); |
| |
| return elementClassMap->get(tag.impl()); |
| } |
| |
| static Class elementClass(const QualifiedName& tag, Class defaultClass) |
| { |
| if (!elementClassMap) |
| createElementClassMap(); |
| Class objcClass = lookupElementClass(tag); |
| if (!objcClass) |
| objcClass = defaultClass; |
| return objcClass; |
| } |
| |
| #if PLATFORM(IOS_FAMILY) |
| |
| static WKQuad wkQuadFromFloatQuad(const FloatQuad& inQuad) |
| { |
| return { inQuad.p1(), inQuad.p2(), inQuad.p3(), inQuad.p4() }; |
| } |
| |
| static NSArray *kit(const Vector<FloatQuad>& quads) |
| { |
| return createNSArray(quads, [] (auto& quad) { |
| return adoptNS([[WKQuadObject alloc] initWithQuad:wkQuadFromFloatQuad(quad)]); |
| }).autorelease(); |
| } |
| |
| static inline WKQuad zeroQuad() |
| { |
| return { CGPointZero, CGPointZero, CGPointZero, CGPointZero }; |
| } |
| |
| @implementation WKQuadObject { |
| WKQuad _quad; |
| } |
| |
| - (id)initWithQuad:(WKQuad)quad |
| { |
| if ((self = [super init])) |
| _quad = quad; |
| return self; |
| } |
| |
| - (WKQuad)quad |
| { |
| return _quad; |
| } |
| |
| - (CGRect)boundingBox |
| { |
| float left = std::min({ _quad.p1.x, _quad.p2.x, _quad.p3.x, _quad.p4.x }); |
| float top = std::min({ _quad.p1.y, _quad.p2.y, _quad.p3.y, _quad.p4.y }); |
| |
| float right = std::max({ _quad.p1.x, _quad.p2.x, _quad.p3.x, _quad.p4.x }); |
| float bottom = std::max({ _quad.p1.y, _quad.p2.y, _quad.p3.y, _quad.p4.y }); |
| |
| return CGRectMake(left, top, right - left, bottom - top); |
| } |
| |
| @end |
| |
| #endif |
| |
| @implementation DOMNode (WebCoreInternal) |
| |
| IGNORE_WARNINGS_BEGIN("objc-protocol-method-implementation") |
| |
| - (NSString *)description |
| { |
| if (!_internal) |
| return [NSString stringWithFormat:@"<%@: null>", [[self class] description]]; |
| |
| NSString *value = [self nodeValue]; |
| if (value) |
| return [NSString stringWithFormat:@"<%@ [%@]: %p '%@'>", [[self class] description], [self nodeName], _internal, value]; |
| |
| return [NSString stringWithFormat:@"<%@ [%@]: %p>", [[self class] description], [self nodeName], _internal]; |
| } |
| |
| IGNORE_WARNINGS_END |
| |
| - (Bindings::RootObject*)_rootObject |
| { |
| auto* frame = core(self)->document().frame(); |
| if (!frame) |
| return nullptr; |
| return frame->script().bindingRootObject(); |
| } |
| |
| @end |
| |
| Class kitClass(Node* impl) |
| { |
| switch (impl->nodeType()) { |
| case Node::ELEMENT_NODE: |
| if (is<HTMLElement>(*impl)) |
| return elementClass(downcast<HTMLElement>(*impl).tagQName(), [DOMHTMLElement class]); |
| return [DOMElement class]; |
| case Node::ATTRIBUTE_NODE: |
| return [DOMAttr class]; |
| case Node::TEXT_NODE: |
| return [DOMText class]; |
| case Node::CDATA_SECTION_NODE: |
| return [DOMCDATASection class]; |
| case Node::PROCESSING_INSTRUCTION_NODE: |
| return [DOMProcessingInstruction class]; |
| case Node::COMMENT_NODE: |
| return [DOMComment class]; |
| case Node::DOCUMENT_NODE: |
| if (static_cast<Document*>(impl)->isHTMLDocument()) |
| return [DOMHTMLDocument class]; |
| return [DOMDocument class]; |
| case Node::DOCUMENT_TYPE_NODE: |
| return [DOMDocumentType class]; |
| case Node::DOCUMENT_FRAGMENT_NODE: |
| return [DOMDocumentFragment class]; |
| } |
| ASSERT_NOT_REACHED(); |
| return nil; |
| } |
| |
| id <DOMEventTarget> kit(EventTarget* target) |
| { |
| // We don't have Objective-C bindings for XMLHttpRequest, DOMWindow, and other non-Node targets. |
| return is<Node>(target) ? kit(downcast<Node>(target)) : nil; |
| } |
| |
| @implementation DOMNode (DOMNodeExtensions) |
| |
| #if PLATFORM(IOS_FAMILY) |
| - (CGRect)boundingBox |
| #else |
| - (NSRect)boundingBox |
| #endif |
| { |
| auto& node = *core(self); |
| node.document().updateLayoutIgnorePendingStylesheets(); |
| auto* renderer = node.renderer(); |
| if (!renderer) |
| #if PLATFORM(IOS_FAMILY) |
| return CGRectZero; |
| #else |
| return NSZeroRect; |
| #endif |
| return renderer->absoluteBoundingBoxRect(); |
| } |
| |
| - (NSArray *)lineBoxRects |
| { |
| return [self textRects]; |
| } |
| |
| #if PLATFORM(IOS_FAMILY) |
| |
| // quad in page coordinates, taking transforms into account. c.f. - (NSRect)boundingBox; |
| - (WKQuad)absoluteQuad |
| { |
| return [self absoluteQuadAndInsideFixedPosition:0]; |
| } |
| |
| - (WKQuad)absoluteQuadAndInsideFixedPosition:(BOOL *)insideFixed |
| { |
| auto& node = *core(self); |
| node.document().updateLayoutIgnorePendingStylesheets(); |
| auto* renderer = node.renderer(); |
| if (!renderer) { |
| if (insideFixed) |
| *insideFixed = false; |
| return zeroQuad(); |
| } |
| |
| Vector<FloatQuad> quads; |
| bool wasFixed = false; |
| renderer->absoluteQuads(quads, &wasFixed); |
| if (insideFixed) |
| *insideFixed = wasFixed; |
| |
| if (quads.size() == 0) |
| return zeroQuad(); |
| if (quads.size() == 1) |
| return wkQuadFromFloatQuad(quads[0]); |
| return wkQuadFromFloatQuad(unitedBoundingBoxes(quads)); |
| } |
| |
| // this method is like - (CGRect)boundingBox, but it accounts for for transforms |
| - (CGRect)boundingBoxUsingTransforms |
| { |
| auto& node = *core(self); |
| node.document().updateLayoutIgnorePendingStylesheets(); |
| auto* renderer = node.renderer(); |
| if (!renderer) |
| return CGRectZero; |
| return renderer->absoluteBoundingBoxRect(true); |
| } |
| |
| // returns array of WKQuadObject |
| - (NSArray *)lineBoxQuads |
| { |
| auto& node = *core(self); |
| node.document().updateLayoutIgnorePendingStylesheets(); |
| WebCore::RenderObject *renderer = node.renderer(); |
| if (!renderer) |
| return nil; |
| Vector<WebCore::FloatQuad> quads; |
| renderer->absoluteQuads(quads); |
| return kit(quads); |
| } |
| |
| - (Element*)_linkElement |
| { |
| for (auto* node = core(self); node; node = node->parentNode()) { |
| if (node->isLink()) |
| return &downcast<Element>(*node); |
| } |
| return nullptr; |
| } |
| |
| - (NSURL *)hrefURL |
| { |
| auto* link = [self _linkElement]; |
| if (!link) |
| return nil; |
| return link->document().completeURL(stripLeadingAndTrailingHTMLSpaces(link->getAttribute(HTMLNames::hrefAttr))); |
| } |
| |
| - (NSString *)hrefTarget |
| { |
| auto* link = [self _linkElement]; |
| if (!link) |
| return nil; |
| return link->getAttribute(HTMLNames::targetAttr); |
| } |
| |
| - (CGRect)hrefFrame |
| { |
| auto* link = [self _linkElement]; |
| if (!link) |
| return CGRectZero; |
| auto* renderer = link->renderer(); |
| if (!renderer) |
| return CGRectZero; |
| return renderer->absoluteBoundingBoxRect(); |
| } |
| |
| - (NSString *)hrefLabel |
| { |
| auto* link = [self _linkElement]; |
| if (!link) |
| return nil; |
| return link->textContent(); |
| } |
| |
| - (NSString *)hrefTitle |
| { |
| auto* link = [self _linkElement]; |
| if (!is<HTMLElement>(link)) |
| return nil; |
| return link->document().displayStringModifiedByEncoding(downcast<HTMLElement>(*link).title()); |
| } |
| |
| - (CGRect)boundingFrame |
| { |
| return [self boundingBox]; |
| } |
| |
| - (WKQuad)innerFrameQuad // takes transforms into account |
| { |
| auto& node = *core(self); |
| node.document().updateLayoutIgnorePendingStylesheets(); |
| auto* renderer = node.renderer(); |
| if (!renderer) |
| return zeroQuad(); |
| |
| auto& style = renderer->style(); |
| IntRect boundingBox = renderer->absoluteBoundingBoxRect(true /* use transforms*/); |
| |
| boundingBox.move(style.borderLeftWidth(), style.borderTopWidth()); |
| boundingBox.setWidth(boundingBox.width() - style.borderLeftWidth() - style.borderRightWidth()); |
| boundingBox.setHeight(boundingBox.height() - style.borderBottomWidth() - style.borderTopWidth()); |
| |
| // FIXME: This function advertises returning a quad, but it actually returns a bounding box (so there is no rotation, for instance). |
| return wkQuadFromFloatQuad(FloatQuad(boundingBox)); |
| } |
| |
| - (float)computedFontSize |
| { |
| auto* style = core(self)->renderStyle(); |
| if (!style) |
| return 0.0f; |
| return style->fontDescription().computedSize(); |
| } |
| |
| - (DOMNode *)nextFocusNode |
| { |
| Page* page = core(self)->document().page(); |
| if (!page) |
| return nil; |
| return kit(page->focusController().nextFocusableElement(*core(self))); |
| } |
| |
| - (DOMNode *)previousFocusNode |
| { |
| Page* page = core(self)->document().page(); |
| if (!page) |
| return nil; |
| return kit(page->focusController().previousFocusableElement(*core(self))); |
| } |
| |
| #endif // PLATFORM(IOS_FAMILY) |
| |
| @end |
| |
| @implementation DOMNode (DOMNodeExtensionsPendingPublic) |
| |
| #if PLATFORM(MAC) |
| |
| - (NSImage *)renderedImage |
| { |
| auto& node = *core(self); |
| auto* frame = node.document().frame(); |
| if (!frame) |
| return nil; |
| return createDragImageForNode(*frame, node).autorelease(); |
| } |
| |
| #endif |
| |
| - (NSArray *)textRects |
| { |
| auto& node = *core(self); |
| node.document().updateLayoutIgnorePendingStylesheets(); |
| if (!node.renderer()) |
| return nil; |
| return createNSArray(RenderObject::absoluteTextRects(makeRangeSelectingNodeContents(node))).autorelease(); |
| } |
| |
| @end |
| |
| @implementation DOMNode (WebPrivate) |
| |
| + (id)_nodeFromJSWrapper:(JSObjectRef)jsWrapper |
| { |
| JSObject* object = toJS(jsWrapper); |
| if (!object->inherits<JSNode>(object->vm())) |
| return nil; |
| return kit(&jsCast<JSNode*>(object)->wrapped()); |
| } |
| |
| - (void)getPreviewSnapshotImage:(CGImageRef*)cgImage andRects:(NSArray **)rects |
| { |
| if (!cgImage || !rects) |
| return; |
| |
| *cgImage = nullptr; |
| *rects = nullptr; |
| |
| auto& node = *core(self); |
| |
| constexpr OptionSet<TextIndicatorOption> options { |
| TextIndicatorOption::TightlyFitContent, |
| TextIndicatorOption::RespectTextColor, |
| TextIndicatorOption::PaintBackgrounds, |
| TextIndicatorOption::UseBoundingRectAndPaintAllContentForComplexRanges, |
| TextIndicatorOption::IncludeMarginIfRangeMatchesSelection |
| }; |
| const float margin = 4 / node.document().page()->pageScaleFactor(); |
| auto textIndicator = TextIndicator::createWithRange(makeRangeSelectingNodeContents(node), options, TextIndicatorPresentationTransition::None, FloatSize(margin, margin)); |
| |
| if (textIndicator) { |
| if (Image* image = textIndicator->contentImage()) { |
| auto contentImage = image->nativeImage()->platformImage(); |
| *cgImage = contentImage.autorelease(); |
| } |
| } |
| |
| if (!*cgImage) { |
| if (auto* renderer = node.renderer()) { |
| FloatRect boundingBox; |
| if (renderer->isRenderImage()) |
| boundingBox = downcast<RenderImage>(*renderer).absoluteContentQuad().enclosingBoundingBox(); |
| else |
| boundingBox = renderer->absoluteBoundingBoxRect(); |
| boundingBox.inflate(margin); |
| *rects = @[makeNSArrayElement(node.document().frame()->view()->contentsToWindow(enclosingIntRect(boundingBox)))]; |
| } |
| return; |
| } |
| |
| FloatPoint origin = textIndicator->textBoundingRectInRootViewCoordinates().location(); |
| *rects = createNSArray(textIndicator->textRectsInBoundingRectCoordinates(), [&] (CGRect rect) { |
| rect.origin.x += origin.x(); |
| rect.origin.y += origin.y(); |
| return makeNSArrayElement(node.document().frame()->view()->contentsToWindow(enclosingIntRect(rect))); |
| }).autorelease(); |
| } |
| |
| @end |
| |
| @implementation DOMRange (DOMRangeExtensions) |
| |
| #if PLATFORM(IOS_FAMILY) |
| - (CGRect)boundingBox |
| #else |
| - (NSRect)boundingBox |
| #endif |
| { |
| auto range = makeSimpleRange(*core(self)); |
| range.start.document().updateLayoutIgnorePendingStylesheets(); |
| return unionRect(RenderObject::absoluteTextRects(range)); |
| } |
| |
| #if PLATFORM(MAC) |
| - (NSImage *)renderedImageForcingBlackText:(BOOL)forceBlackText |
| #else |
| - (CGImageRef)renderedImageForcingBlackText:(BOOL)forceBlackText |
| #endif |
| { |
| auto range = makeSimpleRange(*core(self)); |
| RefPtr frame = range.start.document().frame(); |
| if (!frame) |
| return nil; |
| |
| auto renderedImage = createDragImageForRange(*frame, range, forceBlackText); |
| |
| #if PLATFORM(MAC) |
| // iOS uses CGImageRef for drag images, which doesn't support separate logical/physical sizes. |
| IntSize size([renderedImage size]); |
| size.scale(1 / frame->page()->deviceScaleFactor()); |
| [renderedImage setSize:size]; |
| #endif |
| |
| return renderedImage.autorelease(); |
| } |
| |
| - (NSArray *)textRects |
| { |
| auto range = makeSimpleRange(*core(self)); |
| range.start.document().updateLayoutIgnorePendingStylesheets(); |
| return createNSArray(RenderObject::absoluteTextRects(range)).autorelease(); |
| } |
| |
| - (NSArray *)lineBoxRects |
| { |
| // FIXME: Remove this once all clients stop using it and we drop Leopard support. |
| return [self textRects]; |
| } |
| |
| @end |
| |
| //------------------------------------------------------------------------------------------ |
| // DOMElement |
| |
| @implementation DOMElement (DOMElementAppKitExtensions) |
| |
| #if PLATFORM(MAC) |
| |
| - (NSImage *)image |
| { |
| auto* renderer = core(self)->renderer(); |
| if (!is<RenderImage>(renderer)) |
| return nil; |
| auto* cachedImage = downcast<RenderImage>(*renderer).cachedImage(); |
| if (!cachedImage || cachedImage->errorOccurred()) |
| return nil; |
| return cachedImage->imageForRenderer(renderer)->nsImage(); |
| } |
| |
| #endif |
| |
| @end |
| |
| @implementation DOMElement (WebPrivate) |
| |
| - (CTFontRef)_font |
| { |
| auto* renderer = core(self)->renderer(); |
| if (!renderer) |
| return nil; |
| return renderer->style().fontCascade().primaryFont().getCTFont(); |
| } |
| |
| #if PLATFORM(MAC) |
| |
| - (NSData *)_imageTIFFRepresentation |
| { |
| // FIXME: Could we move this function to WebCore::Element and autogenerate? |
| auto* renderer = core(self)->renderer(); |
| if (!is<RenderImage>(renderer)) |
| return nil; |
| auto* cachedImage = downcast<RenderImage>(*renderer).cachedImage(); |
| if (!cachedImage || cachedImage->errorOccurred()) |
| return nil; |
| return (__bridge NSData *)cachedImage->imageForRenderer(renderer)->tiffRepresentation(); |
| } |
| |
| #endif |
| |
| - (NSURL *)_getURLAttribute:(NSString *)name |
| { |
| auto& element = *core(self); |
| return element.document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.getAttribute(name))); |
| } |
| |
| - (BOOL)isFocused |
| { |
| auto& element = *core(self); |
| return element.document().focusedElement() == &element; |
| } |
| |
| @end |
| |
| #if PLATFORM(IOS_FAMILY) |
| |
| @implementation DOMHTMLLinkElement (WebPrivate) |
| |
| - (BOOL)_mediaQueryMatchesForOrientation:(int)orientation |
| { |
| Document& document = static_cast<HTMLLinkElement*>(core(self))->document(); |
| FrameView* frameView = document.frame() ? document.frame()->view() : 0; |
| if (!frameView) |
| return false; |
| int layoutWidth = frameView->layoutWidth(); |
| int layoutHeight = frameView->layoutHeight(); |
| IntSize savedFixedLayoutSize = frameView->fixedLayoutSize(); |
| bool savedUseFixedLayout = frameView->useFixedLayout(); |
| if ((orientation == WebMediaQueryOrientationPortrait && layoutWidth > layoutHeight) || |
| (orientation == WebMediaQueryOrientationLandscape && layoutWidth < layoutHeight)) { |
| // temporarily swap the orientation for the evaluation |
| frameView->setFixedLayoutSize(IntSize(layoutHeight, layoutWidth)); |
| frameView->setUseFixedLayout(true); |
| } |
| |
| bool result = [self _mediaQueryMatches]; |
| |
| frameView->setFixedLayoutSize(savedFixedLayoutSize); |
| frameView->setUseFixedLayout(savedUseFixedLayout); |
| |
| return result; |
| } |
| |
| - (BOOL)_mediaQueryMatches |
| { |
| HTMLLinkElement& link = *static_cast<HTMLLinkElement*>(core(self)); |
| |
| auto& media = link.attributeWithoutSynchronization(HTMLNames::mediaAttr); |
| if (media.isEmpty()) |
| return true; |
| |
| Document& document = link.document(); |
| auto mediaQuerySet = MediaQuerySet::create(media, MediaQueryParserContext(document)); |
| return MediaQueryEvaluator { "screen", document, document.renderView() ? &document.renderView()->style() : nullptr }.evaluate(mediaQuerySet.get()); |
| } |
| |
| @end |
| |
| #endif |
| |
| //------------------------------------------------------------------------------------------ |
| // DOMRange |
| |
| @implementation DOMRange (WebPrivate) |
| |
| - (NSString *)description |
| { |
| if (!_internal) |
| return @"<DOMRange: null>"; |
| return [NSString stringWithFormat:@"<DOMRange: %@ %d %@ %d>", |
| [self startContainer], [self startOffset], [self endContainer], [self endOffset]]; |
| } |
| |
| // FIXME: This should be removed as soon as all internal Apple uses of it have been replaced with |
| // calls to the public method - (NSString *)text. |
| - (NSString *)_text |
| { |
| return [self text]; |
| } |
| |
| @end |
| |
| //------------------------------------------------------------------------------------------ |
| // DOMRGBColor |
| |
| @implementation DOMRGBColor (WebPrivate) |
| |
| #if PLATFORM(MAC) |
| |
| // FIXME: This should be removed as soon as all internal Apple uses of it have been replaced with |
| // calls to the public method - (NSColor *)color. |
| - (NSColor *)_color |
| { |
| return [self color]; |
| } |
| |
| #endif |
| |
| @end |
| |
| //------------------------------------------------------------------------------------------ |
| // DOMHTMLTableCellElement |
| |
| @implementation DOMHTMLTableCellElement (WebPrivate) |
| |
| - (DOMHTMLTableCellElement *)_cellAbove |
| { |
| return kit(core(self)->cellAbove()); |
| } |
| |
| @end |
| |
| //------------------------------------------------------------------------------------------ |
| // DOMNodeFilter |
| |
| DOMNodeFilter *kit(WebCore::NodeFilter* impl) |
| { |
| if (!impl) |
| return nil; |
| |
| if (DOMNodeFilter *wrapper = getDOMWrapper(impl)) |
| return retainPtr(wrapper).autorelease(); |
| |
| auto wrapper = adoptNS([[DOMNodeFilter alloc] _init]); |
| wrapper->_internal = reinterpret_cast<DOMObjectInternal*>(impl); |
| impl->ref(); |
| addDOMWrapper(wrapper.get(), impl); |
| return wrapper.autorelease(); |
| } |
| |
| WebCore::NodeFilter* core(DOMNodeFilter *wrapper) |
| { |
| return wrapper ? reinterpret_cast<WebCore::NodeFilter*>(wrapper->_internal) : 0; |
| } |
| |
| @implementation DOMNodeFilter |
| |
| - (void)dealloc |
| { |
| if (_internal) |
| reinterpret_cast<WebCore::NodeFilter*>(_internal)->deref(); |
| [super dealloc]; |
| } |
| |
| - (short)acceptNode:(DOMNode *)node |
| { |
| if (!node) |
| raiseTypeErrorException(); |
| |
| auto result = core(self)->acceptNode(*core(node)); |
| return result.type() == CallbackResultType::Success ? result.releaseReturnValue() : NodeFilter::FILTER_REJECT; |
| } |
| |
| @end |