| /* |
| * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> |
| * 1999 Lars Knoll <knoll@kde.org> |
| * 1999 Antti Koivisto <koivisto@kde.org> |
| * 2000 Simon Hausmann <hausmann@kde.org> |
| * 2000 Stefan Schimanski <1Stein@gmx.de> |
| * 2001 George Staikos <staikos@kde.org> |
| * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All |
| * rights reserved. |
| * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com> |
| * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
| * Copyright (C) 2008 Eric Seidel <eric@webkit.org> |
| * Copyright (C) 2008 Google Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "core/frame/LocalFrame.h" |
| |
| #include "bindings/core/v8/ScriptController.h" |
| #include "core/InstrumentingAgents.h" |
| #include "core/dom/ChildFrameDisconnector.h" |
| #include "core/dom/DocumentType.h" |
| #include "core/dom/StyleChangeReason.h" |
| #include "core/editing/EditingUtilities.h" |
| #include "core/editing/Editor.h" |
| #include "core/editing/FrameSelection.h" |
| #include "core/editing/InputMethodController.h" |
| #include "core/editing/serializers/Serialization.h" |
| #include "core/editing/spellcheck/SpellChecker.h" |
| #include "core/events/Event.h" |
| #include "core/fetch/ResourceFetcher.h" |
| #include "core/frame/EventHandlerRegistry.h" |
| #include "core/frame/FrameConsole.h" |
| #include "core/frame/FrameHost.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalDOMWindow.h" |
| #include "core/frame/PerformanceMonitor.h" |
| #include "core/frame/Settings.h" |
| #include "core/frame/VisualViewport.h" |
| #include "core/html/HTMLFrameElementBase.h" |
| #include "core/html/HTMLPlugInElement.h" |
| #include "core/input/EventHandler.h" |
| #include "core/inspector/InspectorInstrumentation.h" |
| #include "core/layout/HitTestResult.h" |
| #include "core/layout/LayoutView.h" |
| #include "core/layout/api/LayoutPartItem.h" |
| #include "core/layout/api/LayoutViewItem.h" |
| #include "core/layout/compositing/PaintLayerCompositor.h" |
| #include "core/loader/FrameLoadRequest.h" |
| #include "core/loader/FrameLoaderClient.h" |
| #include "core/loader/NavigationScheduler.h" |
| #include "core/page/ChromeClient.h" |
| #include "core/page/FocusController.h" |
| #include "core/page/Page.h" |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| #include "core/paint/ObjectPainter.h" |
| #include "core/paint/PaintInfo.h" |
| #include "core/paint/PaintLayer.h" |
| #include "core/paint/PaintLayerPainter.h" |
| #include "core/paint/TransformRecorder.h" |
| #include "core/svg/SVGDocumentExtensions.h" |
| #include "core/timing/Performance.h" |
| #include "platform/DragImage.h" |
| #include "platform/PluginScriptForbiddenScope.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/ScriptForbiddenScope.h" |
| #include "platform/WebFrameScheduler.h" |
| #include "platform/graphics/GraphicsContext.h" |
| #include "platform/graphics/StaticBitmapImage.h" |
| #include "platform/graphics/paint/ClipRecorder.h" |
| #include "platform/graphics/paint/PaintController.h" |
| #include "platform/graphics/paint/SkPictureBuilder.h" |
| #include "platform/graphics/paint/TransformDisplayItem.h" |
| #include "platform/json/JSONValues.h" |
| #include "platform/plugins/PluginData.h" |
| #include "platform/text/TextStream.h" |
| #include "public/platform/InterfaceProvider.h" |
| #include "public/platform/InterfaceRegistry.h" |
| #include "public/platform/WebScreenInfo.h" |
| #include "public/platform/WebViewScheduler.h" |
| #include "third_party/skia/include/core/SkImage.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/StdLibExtras.h" |
| #include <memory> |
| |
| namespace blink { |
| |
| using namespace HTMLNames; |
| |
| namespace { |
| |
| // Convenience class for initializing a GraphicsContext to build a DragImage |
| // from a specific region specified by |bounds|. After painting the using |
| // context(), the DragImage returned from createImage() will only contain the |
| // content in |bounds| with the appropriate device scale factor included. |
| class DragImageBuilder { |
| STACK_ALLOCATED(); |
| |
| public: |
| DragImageBuilder(const LocalFrame& localFrame, const FloatRect& bounds) |
| : m_localFrame(&localFrame), m_bounds(bounds) { |
| // TODO(oshima): Remove this when all platforms are migrated to |
| // use-zoom-for-dsf. |
| float deviceScaleFactor = |
| m_localFrame->host()->deviceScaleFactorDeprecated(); |
| float pageScaleFactor = m_localFrame->host()->visualViewport().scale(); |
| m_bounds.setWidth(m_bounds.width() * deviceScaleFactor * pageScaleFactor); |
| m_bounds.setHeight(m_bounds.height() * deviceScaleFactor * pageScaleFactor); |
| m_pictureBuilder = WTF::wrapUnique(new SkPictureBuilder( |
| SkRect::MakeIWH(m_bounds.width(), m_bounds.height()))); |
| |
| AffineTransform transform; |
| transform.scale(deviceScaleFactor * pageScaleFactor, |
| deviceScaleFactor * pageScaleFactor); |
| transform.translate(-m_bounds.x(), -m_bounds.y()); |
| context().getPaintController().createAndAppend<BeginTransformDisplayItem>( |
| *m_pictureBuilder, transform); |
| } |
| |
| GraphicsContext& context() { return m_pictureBuilder->context(); } |
| |
| std::unique_ptr<DragImage> createImage( |
| float opacity, |
| RespectImageOrientationEnum imageOrientation = |
| DoNotRespectImageOrientation) { |
| context().getPaintController().endItem<EndTransformDisplayItem>( |
| *m_pictureBuilder); |
| // TODO(fmalita): endRecording() should return a non-const SKP. |
| sk_sp<SkPicture> recording( |
| const_cast<SkPicture*>(m_pictureBuilder->endRecording().release())); |
| sk_sp<SkImage> skImage = SkImage::MakeFromPicture( |
| std::move(recording), |
| SkISize::Make(m_bounds.width(), m_bounds.height()), nullptr, nullptr); |
| RefPtr<Image> image = StaticBitmapImage::create(std::move(skImage)); |
| float screenDeviceScaleFactor = |
| m_localFrame->page()->chromeClient().screenInfo().deviceScaleFactor; |
| |
| return DragImage::create(image.get(), imageOrientation, |
| screenDeviceScaleFactor, InterpolationHigh, |
| opacity); |
| } |
| |
| private: |
| const Member<const LocalFrame> m_localFrame; |
| FloatRect m_bounds; |
| std::unique_ptr<SkPictureBuilder> m_pictureBuilder; |
| }; |
| |
| class DraggedNodeImageBuilder { |
| STACK_ALLOCATED(); |
| |
| public: |
| DraggedNodeImageBuilder(const LocalFrame& localFrame, Node& node) |
| : m_localFrame(&localFrame), |
| m_node(&node) |
| #if ENABLE(ASSERT) |
| , |
| m_domTreeVersion(node.document().domTreeVersion()) |
| #endif |
| { |
| for (Node& descendant : NodeTraversal::inclusiveDescendantsOf(*m_node)) |
| descendant.setDragged(true); |
| } |
| |
| ~DraggedNodeImageBuilder() { |
| #if ENABLE(ASSERT) |
| DCHECK_EQ(m_domTreeVersion, m_node->document().domTreeVersion()); |
| #endif |
| for (Node& descendant : NodeTraversal::inclusiveDescendantsOf(*m_node)) |
| descendant.setDragged(false); |
| } |
| |
| std::unique_ptr<DragImage> createImage() { |
| #if ENABLE(ASSERT) |
| DCHECK_EQ(m_domTreeVersion, m_node->document().domTreeVersion()); |
| #endif |
| // Construct layout object for |m_node| with pseudo class "-webkit-drag" |
| m_localFrame->view()->updateAllLifecyclePhasesExceptPaint(); |
| LayoutObject* const draggedLayoutObject = m_node->layoutObject(); |
| if (!draggedLayoutObject) |
| return nullptr; |
| // Paint starting at the nearest stacking context, clipped to the object |
| // itself. This will also paint the contents behind the object if the |
| // object contains transparency and there are other elements in the same |
| // stacking context which stacked below. |
| PaintLayer* layer = draggedLayoutObject->enclosingLayer(); |
| if (!layer->stackingNode()->isStackingContext()) |
| layer = layer->stackingNode()->ancestorStackingContextNode()->layer(); |
| IntRect absoluteBoundingBox = |
| draggedLayoutObject->absoluteBoundingBoxRectIncludingDescendants(); |
| FloatRect boundingBox = |
| layer->layoutObject() |
| ->absoluteToLocalQuad(FloatQuad(absoluteBoundingBox), UseTransforms) |
| .boundingBox(); |
| DragImageBuilder dragImageBuilder(*m_localFrame, boundingBox); |
| { |
| PaintLayerPaintingInfo paintingInfo(layer, LayoutRect(boundingBox), |
| GlobalPaintFlattenCompositingLayers, |
| LayoutSize()); |
| PaintLayerFlags flags = PaintLayerHaveTransparency | |
| PaintLayerAppliedTransform | |
| PaintLayerUncachedClipRects; |
| PaintLayerPainter(*layer).paintLayer(dragImageBuilder.context(), |
| paintingInfo, flags); |
| } |
| return dragImageBuilder.createImage( |
| 1.0f, LayoutObject::shouldRespectImageOrientation(draggedLayoutObject)); |
| } |
| |
| private: |
| const Member<const LocalFrame> m_localFrame; |
| const Member<Node> m_node; |
| #if ENABLE(ASSERT) |
| const uint64_t m_domTreeVersion; |
| #endif |
| }; |
| |
| inline float parentPageZoomFactor(LocalFrame* frame) { |
| Frame* parent = frame->tree().parent(); |
| if (!parent || !parent->isLocalFrame()) |
| return 1; |
| return toLocalFrame(parent)->pageZoomFactor(); |
| } |
| |
| inline float parentTextZoomFactor(LocalFrame* frame) { |
| Frame* parent = frame->tree().parent(); |
| if (!parent || !parent->isLocalFrame()) |
| return 1; |
| return toLocalFrame(parent)->textZoomFactor(); |
| } |
| |
| } // namespace |
| |
| template class CORE_TEMPLATE_EXPORT Supplement<LocalFrame>; |
| |
| LocalFrame* LocalFrame::create(FrameLoaderClient* client, |
| FrameHost* host, |
| FrameOwner* owner, |
| InterfaceProvider* interfaceProvider, |
| InterfaceRegistry* interfaceRegistry) { |
| LocalFrame* frame = new LocalFrame( |
| client, host, owner, |
| interfaceProvider ? interfaceProvider |
| : InterfaceProvider::getEmptyInterfaceProvider(), |
| interfaceRegistry ? interfaceRegistry |
| : InterfaceRegistry::getEmptyInterfaceRegistry()); |
| InspectorInstrumentation::frameAttachedToParent(frame); |
| return frame; |
| } |
| |
| void LocalFrame::setView(FrameView* view) { |
| ASSERT(!m_view || m_view != view); |
| ASSERT(!document() || !document()->isActive()); |
| |
| eventHandler().clear(); |
| |
| m_view = view; |
| } |
| |
| void LocalFrame::createView(const IntSize& viewportSize, |
| const Color& backgroundColor, |
| bool transparent, |
| ScrollbarMode horizontalScrollbarMode, |
| bool horizontalLock, |
| ScrollbarMode verticalScrollbarMode, |
| bool verticalLock) { |
| ASSERT(this); |
| ASSERT(page()); |
| |
| bool isLocalRoot = this->isLocalRoot(); |
| |
| if (isLocalRoot && view()) |
| view()->setParentVisible(false); |
| |
| setView(nullptr); |
| |
| FrameView* frameView = nullptr; |
| if (isLocalRoot) { |
| frameView = FrameView::create(*this, viewportSize); |
| |
| // The layout size is set by WebViewImpl to support @viewport |
| frameView->setLayoutSizeFixedToFrameSize(false); |
| } else { |
| frameView = FrameView::create(*this); |
| } |
| |
| frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode, |
| horizontalLock, verticalLock); |
| |
| setView(frameView); |
| |
| frameView->updateBackgroundRecursively(backgroundColor, transparent); |
| |
| if (isLocalRoot) |
| frameView->setParentVisible(true); |
| |
| // FIXME: Not clear what the right thing for OOPI is here. |
| if (!ownerLayoutItem().isNull()) { |
| HTMLFrameOwnerElement* owner = deprecatedLocalOwner(); |
| ASSERT(owner); |
| // FIXME: OOPI might lead to us temporarily lying to a frame and telling it |
| // that it's owned by a FrameOwner that knows nothing about it. If we're |
| // lying to this frame, don't let it clobber the existing widget. |
| if (owner->contentFrame() == this) |
| owner->setWidget(frameView); |
| } |
| |
| if (owner()) |
| view()->setCanHaveScrollbars(owner()->scrollingMode() != |
| ScrollbarAlwaysOff); |
| } |
| |
| LocalFrame::~LocalFrame() { |
| // Verify that the FrameView has been cleared as part of detaching |
| // the frame owner. |
| ASSERT(!m_view); |
| } |
| |
| DEFINE_TRACE(LocalFrame) { |
| visitor->trace(m_instrumentingAgents); |
| visitor->trace(m_performanceMonitor); |
| visitor->trace(m_loader); |
| visitor->trace(m_navigationScheduler); |
| visitor->trace(m_view); |
| visitor->trace(m_domWindow); |
| visitor->trace(m_pagePopupOwner); |
| visitor->trace(m_script); |
| visitor->trace(m_editor); |
| visitor->trace(m_spellChecker); |
| visitor->trace(m_selection); |
| visitor->trace(m_eventHandler); |
| visitor->trace(m_console); |
| visitor->trace(m_inputMethodController); |
| Frame::trace(visitor); |
| Supplementable<LocalFrame>::trace(visitor); |
| } |
| |
| WindowProxy* LocalFrame::windowProxy(DOMWrapperWorld& world) { |
| return m_script->windowProxy(world); |
| } |
| |
| void LocalFrame::navigate(Document& originDocument, |
| const KURL& url, |
| bool replaceCurrentItem, |
| UserGestureStatus userGestureStatus) { |
| m_navigationScheduler->scheduleLocationChange( |
| &originDocument, url.getString(), replaceCurrentItem); |
| } |
| |
| void LocalFrame::navigate(const FrameLoadRequest& request) { |
| m_loader.load(request); |
| } |
| |
| void LocalFrame::reload(FrameLoadType loadType, |
| ClientRedirectPolicy clientRedirectPolicy) { |
| DCHECK(isReloadLoadType(loadType)); |
| if (clientRedirectPolicy == ClientRedirectPolicy::NotClientRedirect) { |
| if (!m_loader.currentItem()) |
| return; |
| FrameLoadRequest request = |
| FrameLoadRequest(nullptr, m_loader.resourceRequestForReload( |
| loadType, KURL(), clientRedirectPolicy)); |
| request.setClientRedirect(clientRedirectPolicy); |
| m_loader.load(request, loadType); |
| } else { |
| DCHECK_EQ(FrameLoadTypeReload, loadType); |
| m_navigationScheduler->scheduleReload(); |
| } |
| } |
| |
| void LocalFrame::detach(FrameDetachType type) { |
| // Note that detach() can be re-entered, so it's not possible to |
| // DCHECK(!m_isDetaching) here. |
| m_isDetaching = true; |
| |
| if (isLocalRoot()) |
| m_performanceMonitor->shutdown(); |
| |
| PluginScriptForbiddenScope forbidPluginDestructorScripting; |
| m_loader.stopAllLoaders(); |
| // Don't allow any new child frames to load in this frame: attaching a new |
| // child frame during or after detaching children results in an attached |
| // frame on a detached DOM tree, which is bad. |
| SubframeLoadingDisabler disabler(*document()); |
| m_loader.dispatchUnloadEvent(); |
| detachChildren(); |
| |
| // All done if detaching the subframes brought about a detach of this frame |
| // also. |
| if (!client()) |
| return; |
| |
| // stopAllLoaders() needs to be called after detachChildren(), because |
| // detachChildren() will trigger the unload event handlers of any child |
| // frames, and those event handlers might start a new subresource load in this |
| // frame. |
| m_loader.stopAllLoaders(); |
| m_loader.detach(); |
| document()->shutdown(); |
| // This is the earliest that scripting can be disabled: |
| // - FrameLoader::detach() can fire XHR abort events |
| // - Document::shutdown()'s deferred widget updates can run script. |
| ScriptForbiddenScope forbidScript; |
| m_loader.clear(); |
| if (!client()) |
| return; |
| |
| client()->willBeDetached(); |
| // Notify ScriptController that the frame is closing, since its cleanup ends |
| // up calling back to FrameLoaderClient via WindowProxy. |
| script().clearForClose(); |
| setView(nullptr); |
| |
| m_host->eventHandlerRegistry().didRemoveAllEventHandlers(*domWindow()); |
| |
| domWindow()->frameDestroyed(); |
| |
| // TODO: Page should take care of updating focus/scrolling instead of Frame. |
| // TODO: It's unclear as to why this is called more than once, but it is, |
| // so page() could be null. |
| if (page() && page()->focusController().focusedFrame() == this) |
| page()->focusController().setFocusedFrame(nullptr); |
| |
| if (page() && page()->scrollingCoordinator() && m_view) |
| page()->scrollingCoordinator()->willDestroyScrollableArea(m_view.get()); |
| |
| InspectorInstrumentation::frameDetachedFromParent(this); |
| Frame::detach(type); |
| |
| m_supplements.clear(); |
| m_frameScheduler.reset(); |
| WeakIdentifierMap<LocalFrame>::notifyObjectDestroyed(this); |
| } |
| |
| bool LocalFrame::prepareForCommit() { |
| return loader().prepareForCommit(); |
| } |
| |
| SecurityContext* LocalFrame::securityContext() const { |
| return document(); |
| } |
| |
| void LocalFrame::printNavigationErrorMessage(const Frame& targetFrame, |
| const char* reason) { |
| // URLs aren't available for RemoteFrames, so the error message uses their |
| // origin instead. |
| String targetFrameDescription = |
| targetFrame.isLocalFrame() |
| ? "with URL '" + |
| toLocalFrame(targetFrame).document()->url().getString() + "'" |
| : "with origin '" + |
| targetFrame.securityContext()->getSecurityOrigin()->toString() + |
| "'"; |
| String message = |
| "Unsafe JavaScript attempt to initiate navigation for frame " + |
| targetFrameDescription + " from frame with URL '" + |
| document()->url().getString() + "'. " + reason + "\n"; |
| |
| domWindow()->printErrorMessage(message); |
| } |
| |
| WindowProxyManager* LocalFrame::getWindowProxyManager() const { |
| return m_script->getWindowProxyManager(); |
| } |
| |
| bool LocalFrame::shouldClose() { |
| // TODO(dcheng): This should be fixed to dispatch beforeunload events to |
| // both local and remote frames. |
| return m_loader.shouldClose(); |
| } |
| |
| void LocalFrame::detachChildren() { |
| DCHECK(m_loader.stateMachine()->creatingInitialEmptyDocument() || document()); |
| |
| if (Document* document = this->document()) |
| ChildFrameDisconnector(*document).disconnect(); |
| } |
| |
| void LocalFrame::documentAttached() { |
| DCHECK(document()); |
| selection().documentAttached(document()); |
| inputMethodController().documentAttached(document()); |
| } |
| |
| LocalDOMWindow* LocalFrame::domWindow() const { |
| return toLocalDOMWindow(m_domWindow); |
| } |
| |
| void LocalFrame::setDOMWindow(LocalDOMWindow* domWindow) { |
| // TODO(haraken): Update this comment. |
| // Oilpan: setDOMWindow() cannot be used when finalizing. Which |
| // is acceptable as its actions are either not needed or handled |
| // by other means -- |
| // |
| // - LocalFrameLifecycleObserver::willDetachFrameHost() will have |
| // signalled the Inspector frameWindowDiscarded() notifications. |
| // We assume that all LocalFrames are detached, where that notification |
| // will have been done. |
| // |
| // - Calling LocalDOMWindow::reset() is not needed (called from |
| // Frame::setDOMWindow().) The Member references it clears will now |
| // die with the window. And the registered DOMWindowProperty instances that |
| // don't, only keep a weak reference to this frame, so there's no need to |
| // be explicitly notified that this frame is going away. |
| if (domWindow) |
| script().clearWindowProxy(); |
| |
| if (this->domWindow()) |
| this->domWindow()->reset(); |
| m_domWindow = domWindow; |
| } |
| |
| Document* LocalFrame::document() const { |
| return m_domWindow ? m_domWindow->document() : nullptr; |
| } |
| |
| void LocalFrame::setPagePopupOwner(Element& owner) { |
| m_pagePopupOwner = &owner; |
| } |
| |
| LayoutView* LocalFrame::contentLayoutObject() const { |
| return document() ? document()->layoutView() : nullptr; |
| } |
| |
| LayoutViewItem LocalFrame::contentLayoutItem() const { |
| return LayoutViewItem(contentLayoutObject()); |
| } |
| |
| void LocalFrame::didChangeVisibilityState() { |
| if (document()) |
| document()->didChangeVisibilityState(); |
| |
| Frame::didChangeVisibilityState(); |
| } |
| |
| void LocalFrame::setDocumentHasReceivedUserGesture() { |
| if (document()) |
| document()->setHasReceivedUserGesture(); |
| } |
| |
| LocalFrame* LocalFrame::localFrameRoot() { |
| LocalFrame* curFrame = this; |
| while (curFrame && curFrame->tree().parent() && |
| curFrame->tree().parent()->isLocalFrame()) |
| curFrame = toLocalFrame(curFrame->tree().parent()); |
| |
| return curFrame; |
| } |
| |
| bool LocalFrame::isCrossOriginSubframe() const { |
| const SecurityOrigin* securityOrigin = securityContext()->getSecurityOrigin(); |
| Frame* top = tree().top(); |
| return top && |
| !securityOrigin->canAccess( |
| top->securityContext()->getSecurityOrigin()); |
| } |
| |
| void LocalFrame::setPrinting(bool printing, |
| const FloatSize& pageSize, |
| const FloatSize& originalPageSize, |
| float maximumShrinkRatio) { |
| // In setting printing, we should not validate resources already cached for |
| // the document. See https://bugs.webkit.org/show_bug.cgi?id=43704 |
| ResourceCacheValidationSuppressor validationSuppressor(document()->fetcher()); |
| |
| document()->setPrinting(printing ? Document::Printing |
| : Document::FinishingPrinting); |
| view()->adjustMediaTypeForPrinting(printing); |
| |
| if (shouldUsePrintingLayout()) { |
| view()->forceLayoutForPagination(pageSize, originalPageSize, |
| maximumShrinkRatio); |
| } else { |
| if (LayoutView* layoutView = view()->layoutView()) { |
| layoutView->setPreferredLogicalWidthsDirty(); |
| layoutView->setNeedsLayout(LayoutInvalidationReason::PrintingChanged); |
| layoutView->setShouldDoFullPaintInvalidationForViewAndAllDescendants(); |
| } |
| view()->layout(); |
| view()->adjustViewSize(); |
| } |
| |
| // Subframes of the one we're printing don't lay out to the page size. |
| for (Frame* child = tree().firstChild(); child; |
| child = child->tree().nextSibling()) { |
| if (child->isLocalFrame()) |
| toLocalFrame(child)->setPrinting(printing, FloatSize(), FloatSize(), 0); |
| } |
| |
| if (!printing) |
| document()->setPrinting(Document::NotPrinting); |
| } |
| |
| bool LocalFrame::shouldUsePrintingLayout() const { |
| // Only top frame being printed should be fit to page size. |
| // Subframes should be constrained by parents only. |
| return document()->printing() && |
| (!tree().parent() || !tree().parent()->isLocalFrame() || |
| !toLocalFrame(tree().parent())->document()->printing()); |
| } |
| |
| FloatSize LocalFrame::resizePageRectsKeepingRatio( |
| const FloatSize& originalSize, |
| const FloatSize& expectedSize) { |
| FloatSize resultSize; |
| if (contentLayoutItem().isNull()) |
| return FloatSize(); |
| |
| if (contentLayoutItem().style()->isHorizontalWritingMode()) { |
| ASSERT(fabs(originalSize.width()) > std::numeric_limits<float>::epsilon()); |
| float ratio = originalSize.height() / originalSize.width(); |
| resultSize.setWidth(floorf(expectedSize.width())); |
| resultSize.setHeight(floorf(resultSize.width() * ratio)); |
| } else { |
| ASSERT(fabs(originalSize.height()) > std::numeric_limits<float>::epsilon()); |
| float ratio = originalSize.width() / originalSize.height(); |
| resultSize.setHeight(floorf(expectedSize.height())); |
| resultSize.setWidth(floorf(resultSize.height() * ratio)); |
| } |
| return resultSize; |
| } |
| |
| void LocalFrame::setPageZoomFactor(float factor) { |
| setPageAndTextZoomFactors(factor, m_textZoomFactor); |
| } |
| |
| void LocalFrame::setTextZoomFactor(float factor) { |
| setPageAndTextZoomFactors(m_pageZoomFactor, factor); |
| } |
| |
| void LocalFrame::setPageAndTextZoomFactors(float pageZoomFactor, |
| float textZoomFactor) { |
| if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor) |
| return; |
| |
| Page* page = this->page(); |
| if (!page) |
| return; |
| |
| Document* document = this->document(); |
| if (!document) |
| return; |
| |
| // Respect SVGs zoomAndPan="disabled" property in standalone SVG documents. |
| // FIXME: How to handle compound documents + zoomAndPan="disabled"? Needs SVG |
| // WG clarification. |
| if (document->isSVGDocument()) { |
| if (!document->accessSVGExtensions().zoomAndPanEnabled()) |
| return; |
| } |
| |
| if (m_pageZoomFactor != pageZoomFactor) { |
| if (FrameView* view = this->view()) { |
| // Update the scroll position when doing a full page zoom, so the content |
| // stays in relatively the same position. |
| ScrollOffset scrollOffset = view->getScrollOffset(); |
| float percentDifference = (pageZoomFactor / m_pageZoomFactor); |
| view->setScrollOffset( |
| ScrollOffset(scrollOffset.width() * percentDifference, |
| scrollOffset.height() * percentDifference), |
| ProgrammaticScroll); |
| } |
| } |
| |
| m_pageZoomFactor = pageZoomFactor; |
| m_textZoomFactor = textZoomFactor; |
| |
| for (Frame* child = tree().firstChild(); child; |
| child = child->tree().nextSibling()) { |
| if (child->isLocalFrame()) |
| toLocalFrame(child)->setPageAndTextZoomFactors(m_pageZoomFactor, |
| m_textZoomFactor); |
| } |
| |
| document->mediaQueryAffectingValueChanged(); |
| document->setNeedsStyleRecalc( |
| SubtreeStyleChange, |
| StyleChangeReasonForTracing::create(StyleChangeReason::Zoom)); |
| document->updateStyleAndLayoutIgnorePendingStylesheets(); |
| } |
| |
| void LocalFrame::deviceScaleFactorChanged() { |
| document()->mediaQueryAffectingValueChanged(); |
| document()->setNeedsStyleRecalc( |
| SubtreeStyleChange, |
| StyleChangeReasonForTracing::create(StyleChangeReason::Zoom)); |
| for (Frame* child = tree().firstChild(); child; |
| child = child->tree().nextSibling()) { |
| if (child->isLocalFrame()) |
| toLocalFrame(child)->deviceScaleFactorChanged(); |
| } |
| } |
| |
| double LocalFrame::devicePixelRatio() const { |
| if (!m_host) |
| return 0; |
| |
| double ratio = m_host->deviceScaleFactorDeprecated(); |
| ratio *= pageZoomFactor(); |
| return ratio; |
| } |
| |
| std::unique_ptr<DragImage> LocalFrame::nodeImage(Node& node) { |
| DraggedNodeImageBuilder imageNode(*this, node); |
| return imageNode.createImage(); |
| } |
| |
| std::unique_ptr<DragImage> LocalFrame::dragImageForSelection(float opacity) { |
| if (!selection().isRange()) |
| return nullptr; |
| |
| m_view->updateAllLifecyclePhasesExceptPaint(); |
| ASSERT(document()->isActive()); |
| |
| FloatRect paintingRect = FloatRect(selection().bounds()); |
| DragImageBuilder dragImageBuilder(*this, paintingRect); |
| GlobalPaintFlags paintFlags = |
| GlobalPaintSelectionOnly | GlobalPaintFlattenCompositingLayers; |
| m_view->paintContents(dragImageBuilder.context(), paintFlags, |
| enclosingIntRect(paintingRect)); |
| return dragImageBuilder.createImage(opacity); |
| } |
| |
| String LocalFrame::selectedText() const { |
| return selection().selectedText(); |
| } |
| |
| String LocalFrame::selectedTextForClipboard() const { |
| if (!document()) |
| return emptyString(); |
| DCHECK(!document()->needsLayoutTreeUpdate()); |
| return selection().selectedTextForClipboard(); |
| } |
| |
| PositionWithAffinity LocalFrame::positionForPoint(const IntPoint& framePoint) { |
| HitTestResult result = eventHandler().hitTestResultAtPoint(framePoint); |
| Node* node = result.innerNodeOrImageMapImage(); |
| if (!node) |
| return PositionWithAffinity(); |
| LayoutObject* layoutObject = node->layoutObject(); |
| if (!layoutObject) |
| return PositionWithAffinity(); |
| const PositionWithAffinity position = |
| layoutObject->positionForPoint(result.localPoint()); |
| if (position.isNull()) |
| return PositionWithAffinity(firstPositionInOrBeforeNode(node)); |
| return position; |
| } |
| |
| Document* LocalFrame::documentAtPoint(const IntPoint& pointInRootFrame) { |
| if (!view()) |
| return nullptr; |
| |
| IntPoint pt = view()->rootFrameToContents(pointInRootFrame); |
| |
| if (contentLayoutItem().isNull()) |
| return nullptr; |
| HitTestResult result = eventHandler().hitTestResultAtPoint( |
| pt, HitTestRequest::ReadOnly | HitTestRequest::Active); |
| return result.innerNode() ? &result.innerNode()->document() : nullptr; |
| } |
| |
| EphemeralRange LocalFrame::rangeForPoint(const IntPoint& framePoint) { |
| const PositionWithAffinity positionWithAffinity = |
| positionForPoint(framePoint); |
| if (positionWithAffinity.isNull()) |
| return EphemeralRange(); |
| |
| VisiblePosition position = createVisiblePosition(positionWithAffinity); |
| VisiblePosition previous = previousPositionOf(position); |
| if (previous.isNotNull()) { |
| const EphemeralRange previousCharacterRange = makeRange(previous, position); |
| IntRect rect = editor().firstRectForRange(previousCharacterRange); |
| if (rect.contains(framePoint)) |
| return EphemeralRange(previousCharacterRange); |
| } |
| |
| VisiblePosition next = nextPositionOf(position); |
| const EphemeralRange nextCharacterRange = makeRange(position, next); |
| if (nextCharacterRange.isNotNull()) { |
| IntRect rect = editor().firstRectForRange(nextCharacterRange); |
| if (rect.contains(framePoint)) |
| return EphemeralRange(nextCharacterRange); |
| } |
| |
| return EphemeralRange(); |
| } |
| |
| bool LocalFrame::isURLAllowed(const KURL& url) const { |
| // Exempt about: URLs from self-reference check. |
| if (url.protocolIsAbout()) |
| return true; |
| |
| // We allow one level of self-reference because some sites depend on that, |
| // but we don't allow more than one. |
| bool foundSelfReference = false; |
| for (const Frame* frame = this; frame; frame = frame->tree().parent()) { |
| if (!frame->isLocalFrame()) |
| continue; |
| if (equalIgnoringFragmentIdentifier(toLocalFrame(frame)->document()->url(), |
| url)) { |
| if (foundSelfReference) |
| return false; |
| foundSelfReference = true; |
| } |
| } |
| return true; |
| } |
| |
| bool LocalFrame::shouldReuseDefaultView(const KURL& url) const { |
| // Secure transitions can only happen when navigating from the initial empty |
| // document. |
| if (!loader().stateMachine()->isDisplayingInitialEmptyDocument()) |
| return false; |
| |
| return document()->isSecureTransitionTo(url); |
| } |
| |
| void LocalFrame::removeSpellingMarkersUnderWords(const Vector<String>& words) { |
| spellChecker().removeSpellingMarkersUnderWords(words); |
| } |
| |
| String LocalFrame::layerTreeAsText(unsigned flags) const { |
| if (contentLayoutItem().isNull()) |
| return String(); |
| |
| std::unique_ptr<JSONObject> layers; |
| if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| layers = view()->compositedLayersAsJSON(static_cast<LayerTreeFlags>(flags)); |
| } else { |
| layers = contentLayoutItem().compositor()->layerTreeAsJSON( |
| static_cast<LayerTreeFlags>(flags)); |
| } |
| |
| if (flags & LayerTreeIncludesPaintInvalidations) { |
| std::unique_ptr<JSONArray> objectPaintInvalidations = |
| m_view->trackedObjectPaintInvalidationsAsJSON(); |
| if (objectPaintInvalidations && objectPaintInvalidations->size()) { |
| if (!layers) |
| layers = JSONObject::create(); |
| layers->setArray("objectPaintInvalidations", |
| std::move(objectPaintInvalidations)); |
| } |
| } |
| |
| return layers ? layers->toPrettyJSONString() : String(); |
| } |
| |
| bool LocalFrame::shouldThrottleRendering() const { |
| return view() && view()->shouldThrottleRendering(); |
| } |
| |
| inline LocalFrame::LocalFrame(FrameLoaderClient* client, |
| FrameHost* host, |
| FrameOwner* owner, |
| InterfaceProvider* interfaceProvider, |
| InterfaceRegistry* interfaceRegistry) |
| : Frame(client, host, owner), |
| m_frameScheduler(page()->chromeClient().createFrameScheduler( |
| client->frameBlameContext())), |
| m_loader(this), |
| m_navigationScheduler(NavigationScheduler::create(this)), |
| m_script(ScriptController::create(this)), |
| m_editor(Editor::create(*this)), |
| m_spellChecker(SpellChecker::create(*this)), |
| m_selection(FrameSelection::create(this)), |
| m_eventHandler(new EventHandler(*this)), |
| m_console(FrameConsole::create(*this)), |
| m_inputMethodController(InputMethodController::create(*this)), |
| m_navigationDisableCount(0), |
| m_pageZoomFactor(parentPageZoomFactor(this)), |
| m_textZoomFactor(parentTextZoomFactor(this)), |
| m_inViewSourceMode(false), |
| m_interfaceProvider(interfaceProvider), |
| m_interfaceRegistry(interfaceRegistry) { |
| if (isLocalRoot()) { |
| m_instrumentingAgents = new InstrumentingAgents(); |
| m_performanceMonitor = new PerformanceMonitor(this); |
| } else { |
| m_instrumentingAgents = localFrameRoot()->m_instrumentingAgents; |
| m_performanceMonitor = localFrameRoot()->m_performanceMonitor; |
| } |
| } |
| |
| WebFrameScheduler* LocalFrame::frameScheduler() { |
| return m_frameScheduler.get(); |
| } |
| |
| void LocalFrame::scheduleVisualUpdateUnlessThrottled() { |
| if (shouldThrottleRendering()) |
| return; |
| page()->animator().scheduleVisualUpdate(this); |
| } |
| |
| FrameLoaderClient* LocalFrame::client() const { |
| return static_cast<FrameLoaderClient*>(Frame::client()); |
| } |
| |
| PluginData* LocalFrame::pluginData() const { |
| if (!loader().allowPlugins(NotAboutToInstantiatePlugin)) |
| return nullptr; |
| return page()->pluginData( |
| tree().top()->securityContext()->getSecurityOrigin()); |
| } |
| |
| DEFINE_WEAK_IDENTIFIER_MAP(LocalFrame); |
| |
| FrameNavigationDisabler::FrameNavigationDisabler(LocalFrame& frame) |
| : m_frame(&frame) { |
| m_frame->disableNavigation(); |
| } |
| |
| FrameNavigationDisabler::~FrameNavigationDisabler() { |
| m_frame->enableNavigation(); |
| } |
| |
| ScopedFrameBlamer::ScopedFrameBlamer(LocalFrame* frame) : m_frame(frame) { |
| if (m_frame && m_frame->client() && m_frame->client()->frameBlameContext()) |
| m_frame->client()->frameBlameContext()->Enter(); |
| } |
| |
| ScopedFrameBlamer::~ScopedFrameBlamer() { |
| if (m_frame && m_frame->client() && m_frame->client()->frameBlameContext()) |
| m_frame->client()->frameBlameContext()->Leave(); |
| } |
| |
| } // namespace blink |