diff -r 000000000000 -r 6474c204b198 layout/ipc/RenderFrameParent.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/ipc/RenderFrameParent.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1189 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "base/basictypes.h" + +#include "BasicLayers.h" +#include "gfx3DMatrix.h" +#ifdef MOZ_ENABLE_D3D9_LAYER +# include "LayerManagerD3D9.h" +#endif //MOZ_ENABLE_D3D9_LAYER +#include "mozilla/BrowserElementParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/layers/APZCTreeManager.h" +#include "mozilla/layers/CompositorParent.h" +#include "mozilla/layers/LayerTransactionParent.h" +#include "nsContentUtils.h" +#include "nsFrameLoader.h" +#include "nsIObserver.h" +#include "nsSubDocumentFrame.h" +#include "nsView.h" +#include "nsViewportFrame.h" +#include "RenderFrameParent.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/CompositorChild.h" +#include "ClientLayerManager.h" + +typedef nsContentView::ViewConfig ViewConfig; +using namespace mozilla::dom; +using namespace mozilla::layers; + +namespace mozilla { +namespace layout { + +typedef FrameMetrics::ViewID ViewID; +typedef RenderFrameParent::ViewMap ViewMap; + +// Represents (affine) transforms that are calculated from a content view. +struct ViewTransform { + ViewTransform(nsIntPoint aTranslation = nsIntPoint(0, 0), float aXScale = 1, float aYScale = 1) + : mTranslation(aTranslation) + , mXScale(aXScale) + , mYScale(aYScale) + {} + + operator gfx3DMatrix() const + { + return + gfx3DMatrix::Translation(mTranslation.x, mTranslation.y, 0) * + gfx3DMatrix::ScalingMatrix(mXScale, mYScale, 1); + } + + nsIntPoint mTranslation; + float mXScale; + float mYScale; +}; + +// Matrix helpers +// For our simple purposes, these helpers apply to 2D affine transformations +// that can be represented by a scale and a translation. This makes the math +// much easier because we only expect the diagonals and the translation +// coordinates of the matrix to be non-zero. + +static double GetXScale(const gfx3DMatrix& aTransform) +{ + return aTransform._11; +} + +static double GetYScale(const gfx3DMatrix& aTransform) +{ + return aTransform._22; +} + +static void Scale(gfx3DMatrix& aTransform, double aXScale, double aYScale) +{ + aTransform._11 *= aXScale; + aTransform._22 *= aYScale; +} + +static void ReverseTranslate(gfx3DMatrix& aTransform, const gfxPoint& aOffset) +{ + aTransform._41 -= aOffset.x; + aTransform._42 -= aOffset.y; +} + + +static void ApplyTransform(nsRect& aRect, + gfx3DMatrix& aTransform, + nscoord auPerDevPixel) +{ + aRect.x = aRect.x * aTransform._11 + aTransform._41 * auPerDevPixel; + aRect.y = aRect.y * aTransform._22 + aTransform._42 * auPerDevPixel; + aRect.width = aRect.width * aTransform._11; + aRect.height = aRect.height * aTransform._22; +} + +static void +AssertInTopLevelChromeDoc(ContainerLayer* aContainer, + nsIFrame* aContainedFrame) +{ + NS_ASSERTION( + (aContainer->Manager()->GetBackendType() != mozilla::layers::LayersBackend::LAYERS_BASIC) || + (aContainedFrame->GetNearestWidget() == + static_cast(aContainer->Manager())->GetRetainerWidget()), + "Expected frame to be in top-level chrome document"); +} + +// Return view for given ID in aMap, nullptr if not found. +static nsContentView* +FindViewForId(const ViewMap& aMap, ViewID aId) +{ + ViewMap::const_iterator iter = aMap.find(aId); + return iter != aMap.end() ? iter->second : nullptr; +} + +// Return the root content view in aMap, nullptr if not found. +static nsContentView* +FindRootView(const ViewMap& aMap) +{ + for (ViewMap::const_iterator iter = aMap.begin(), end = aMap.end(); + iter != end; + ++iter) { + if (iter->second->IsRoot()) + return iter->second; + } + return nullptr; +} + +static const FrameMetrics* +GetFrameMetrics(Layer* aLayer) +{ + ContainerLayer* container = aLayer->AsContainerLayer(); + return container ? &container->GetFrameMetrics() : nullptr; +} + +/** + * Gets the layer-pixel offset of aContainerFrame's content rect top-left + * from the nearest display item reference frame (which we assume will be inducing + * a ContainerLayer). + */ +static nsIntPoint +GetContentRectLayerOffset(nsIFrame* aContainerFrame, nsDisplayListBuilder* aBuilder) +{ + nscoord auPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel(); + + // Offset to the content rect in case we have borders or padding + // Note that aContainerFrame could be a reference frame itself, so + // we need to be careful here to ensure that we call ToReferenceFrame + // on aContainerFrame and not its parent. + nsPoint frameOffset = aBuilder->ToReferenceFrame(aContainerFrame) + + (aContainerFrame->GetContentRect().TopLeft() - aContainerFrame->GetPosition()); + + return frameOffset.ToNearestPixels(auPerDevPixel); +} + +// Compute the transform of the shadow tree contained by +// |aContainerFrame| to widget space. We transform because the +// subprocess layer manager renders to a different top-left than where +// the shadow tree is drawn here and because a scale can be set on the +// shadow tree. +static ViewTransform +ComputeShadowTreeTransform(nsIFrame* aContainerFrame, + nsFrameLoader* aRootFrameLoader, + const FrameMetrics* aMetrics, + const ViewConfig& aConfig, + float aTempScaleX = 1.0, + float aTempScaleY = 1.0) +{ + // |aMetrics->mViewportScrollOffset| The frame's scroll offset when it was + // painted, in content document pixels. + // |aConfig.mScrollOffset| What our user expects, or wants, the + // frame scroll offset to be in chrome + // document app units. + // + // So we set a compensating translation that moves the content document + // pixels to where the user wants them to be. + // + nscoord auPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel(); + nsIntPoint scrollOffset = + aConfig.mScrollOffset.ToNearestPixels(auPerDevPixel); + LayerIntPoint metricsScrollOffset = RoundedToInt(aMetrics->GetScrollOffsetInLayerPixels()); + + if (aRootFrameLoader->AsyncScrollEnabled() && !aMetrics->mDisplayPort.IsEmpty()) { + // Only use asynchronous scrolling if it is enabled and there is a + // displayport defined. It is useful to have a scroll layer that is + // synchronously scrolled for identifying a scroll area before it is + // being actively scrolled. + nsIntPoint scrollCompensation( + (scrollOffset.x / aTempScaleX - metricsScrollOffset.x), + (scrollOffset.y / aTempScaleY - metricsScrollOffset.y)); + + return ViewTransform(-scrollCompensation, aConfig.mXScale, aConfig.mYScale); + } else { + return ViewTransform(nsIntPoint(0, 0), 1, 1); + } +} + +// Use shadow layer tree to build display list for the browser's frame. +static void +BuildListForLayer(Layer* aLayer, + nsFrameLoader* aRootFrameLoader, + const gfx3DMatrix& aTransform, + nsDisplayListBuilder* aBuilder, + nsDisplayList& aShadowTree, + nsIFrame* aSubdocFrame) +{ + const FrameMetrics* metrics = GetFrameMetrics(aLayer); + + gfx3DMatrix transform; + + if (metrics && metrics->IsScrollable()) { + const ViewID scrollId = metrics->GetScrollId(); + + // We need to figure out the bounds of the scrollable region using the + // shadow layer tree from the remote process. The metrics viewport is + // defined based on all the transformations of its parent layers and + // the scale of the current layer. + + // Calculate transform for this layer. + nsContentView* view = + aRootFrameLoader->GetCurrentRemoteFrame()->GetContentView(scrollId); + // XXX why don't we include aLayer->GetTransform() in the inverse-scale here? + // This seems wrong, but it doesn't seem to cause bugs! + gfx3DMatrix applyTransform = ComputeShadowTreeTransform( + aSubdocFrame, aRootFrameLoader, metrics, view->GetViewConfig(), + 1 / GetXScale(aTransform), 1 / GetYScale(aTransform)); + gfx3DMatrix layerTransform; + To3DMatrix(aLayer->GetTransform(), layerTransform); + transform = applyTransform * layerTransform * aTransform; + + // As mentioned above, bounds calculation also depends on the scale + // of this layer. + gfx3DMatrix tmpTransform = aTransform; + Scale(tmpTransform, GetXScale(applyTransform), GetYScale(applyTransform)); + + // Calculate rect for this layer based on aTransform. + nsRect bounds; + { + bounds = CSSRect::ToAppUnits(metrics->mViewport); + nscoord auPerDevPixel = aSubdocFrame->PresContext()->AppUnitsPerDevPixel(); + ApplyTransform(bounds, tmpTransform, auPerDevPixel); + } + + aShadowTree.AppendToTop( + new (aBuilder) nsDisplayRemoteShadow(aBuilder, aSubdocFrame, bounds, scrollId)); + + } else { + gfx3DMatrix layerTransform; + To3DMatrix(aLayer->GetTransform(), layerTransform); + transform = layerTransform * aTransform; + } + + for (Layer* child = aLayer->GetFirstChild(); child; + child = child->GetNextSibling()) { + BuildListForLayer(child, aRootFrameLoader, transform, + aBuilder, aShadowTree, aSubdocFrame); + } +} + +// Go down shadow layer tree and apply transformations for scrollable layers. +static void +TransformShadowTree(nsDisplayListBuilder* aBuilder, nsFrameLoader* aFrameLoader, + nsIFrame* aFrame, Layer* aLayer, + const ViewTransform& aTransform, + float aTempScaleDiffX = 1.0, + float aTempScaleDiffY = 1.0) +{ + LayerComposite* shadow = aLayer->AsLayerComposite(); + shadow->SetShadowClipRect(aLayer->GetClipRect()); + shadow->SetShadowVisibleRegion(aLayer->GetVisibleRegion()); + shadow->SetShadowOpacity(aLayer->GetOpacity()); + + const FrameMetrics* metrics = GetFrameMetrics(aLayer); + + gfx3DMatrix shadowTransform; + To3DMatrix(aLayer->GetTransform(), shadowTransform); + ViewTransform layerTransform = aTransform; + + if (metrics && metrics->IsScrollable()) { + const ViewID scrollId = metrics->GetScrollId(); + const nsContentView* view = + aFrameLoader->GetCurrentRemoteFrame()->GetContentView(scrollId); + NS_ABORT_IF_FALSE(view, "Array of views should be consistent with layer tree"); + gfx3DMatrix currentTransform; + To3DMatrix(aLayer->GetTransform(), currentTransform); + + const ViewConfig& config = view->GetViewConfig(); + // With temporary scale we should compensate translation + // using temporary scale value + aTempScaleDiffX *= GetXScale(shadowTransform) * config.mXScale; + aTempScaleDiffY *= GetYScale(shadowTransform) * config.mYScale; + ViewTransform viewTransform = ComputeShadowTreeTransform( + aFrame, aFrameLoader, metrics, view->GetViewConfig(), + aTempScaleDiffX, aTempScaleDiffY + ); + + // Apply the layer's own transform *before* the view transform + shadowTransform = gfx3DMatrix(viewTransform) * currentTransform; + + layerTransform = viewTransform; + if (metrics->IsRootScrollable()) { + // Apply the translation *before* we do the rest of the transforms. + nsIntPoint offset = GetContentRectLayerOffset(aFrame, aBuilder); + shadowTransform = shadowTransform * + gfx3DMatrix::Translation(float(offset.x), float(offset.y), 0.0); + } + } + + if (aLayer->GetIsFixedPosition() && + !aLayer->GetParent()->GetIsFixedPosition()) { + // Alter the shadow transform of fixed position layers in the situation + // that the view transform's scroll position doesn't match the actual + // scroll position, due to asynchronous layer scrolling. + float offsetX = layerTransform.mTranslation.x; + float offsetY = layerTransform.mTranslation.y; + ReverseTranslate(shadowTransform, gfxPoint(offsetX, offsetY)); + const nsIntRect* clipRect = shadow->GetShadowClipRect(); + if (clipRect) { + nsIntRect transformedClipRect(*clipRect); + transformedClipRect.MoveBy(-offsetX, -offsetY); + shadow->SetShadowClipRect(&transformedClipRect); + } + } + + // The transform already takes the resolution scale into account. Since we + // will apply the resolution scale again when computing the effective + // transform, we must apply the inverse resolution scale here. + if (ContainerLayer* c = aLayer->AsContainerLayer()) { + shadowTransform.Scale(1.0f/c->GetPreXScale(), + 1.0f/c->GetPreYScale(), + 1); + } + shadowTransform.ScalePost(1.0f/aLayer->GetPostXScale(), + 1.0f/aLayer->GetPostYScale(), + 1); + + gfx::Matrix4x4 realShadowTransform; + ToMatrix4x4(shadowTransform, realShadowTransform); + shadow->SetShadowTransform(realShadowTransform); + for (Layer* child = aLayer->GetFirstChild(); + child; child = child->GetNextSibling()) { + TransformShadowTree(aBuilder, aFrameLoader, aFrame, child, layerTransform, + aTempScaleDiffX, aTempScaleDiffY); + } +} + +static void +ClearContainer(ContainerLayer* aContainer) +{ + while (Layer* layer = aContainer->GetFirstChild()) { + aContainer->RemoveChild(layer); + } +} + +// Return true iff |aManager| is a "temporary layer manager". They're +// used for small software rendering tasks, like drawWindow. That's +// currently implemented by a BasicLayerManager without a backing +// widget, and hence in non-retained mode. +inline static bool +IsTempLayerManager(LayerManager* aManager) +{ + return (mozilla::layers::LayersBackend::LAYERS_BASIC == aManager->GetBackendType() && + !static_cast(aManager)->IsRetained()); +} + +// Recursively create a new array of scrollables, preserving any scrollables +// that are still in the layer tree. +// +// aXScale and aYScale are used to calculate any values that need to be in +// chrome-document CSS pixels and aren't part of the rendering loop, such as +// the initial scroll offset for a new view. +static void +BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews, + nsFrameLoader* aFrameLoader, Layer* aLayer, + float aXScale = 1, float aYScale = 1, + float aAccConfigXScale = 1, float aAccConfigYScale = 1) +{ + ContainerLayer* container = aLayer->AsContainerLayer(); + if (!container) + return; + const FrameMetrics metrics = container->GetFrameMetrics(); + const ViewID scrollId = metrics.GetScrollId(); + gfx3DMatrix transform; + To3DMatrix(aLayer->GetTransform(), transform); + aXScale *= GetXScale(transform); + aYScale *= GetYScale(transform); + + if (metrics.IsScrollable()) { + nscoord auPerDevPixel = aFrameLoader->GetPrimaryFrameOfOwningContent() + ->PresContext()->AppUnitsPerDevPixel(); + nscoord auPerCSSPixel = auPerDevPixel * metrics.mDevPixelsPerCSSPixel.scale; + nsContentView* view = FindViewForId(oldContentViews, scrollId); + if (view) { + // View already exists. Be sure to propagate scales for any values + // that need to be calculated something in chrome-doc CSS pixels. + ViewConfig config = view->GetViewConfig(); + aXScale *= config.mXScale; + aYScale *= config.mYScale; + view->mFrameLoader = aFrameLoader; + // If scale has changed, then we should update + // current scroll offset to new scaled value + if (aAccConfigXScale != view->mParentScaleX || + aAccConfigYScale != view->mParentScaleY) { + float xscroll = 0, yscroll = 0; + view->GetScrollX(&xscroll); + view->GetScrollY(&yscroll); + xscroll = xscroll * (aAccConfigXScale / view->mParentScaleX); + yscroll = yscroll * (aAccConfigYScale / view->mParentScaleY); + view->ScrollTo(xscroll, yscroll); + view->mParentScaleX = aAccConfigXScale; + view->mParentScaleY = aAccConfigYScale; + } + // Collect only config scale values for scroll compensation + aAccConfigXScale *= config.mXScale; + aAccConfigYScale *= config.mYScale; + } else { + // View doesn't exist, so generate one. We start the view scroll offset at + // the same position as the framemetric's scroll offset from the layer. + // The default scale is 1, so no need to propagate scale down. + ViewConfig config; + config.mScrollOffset = nsPoint( + NSIntPixelsToAppUnits(metrics.GetScrollOffset().x, auPerCSSPixel) * aXScale, + NSIntPixelsToAppUnits(metrics.GetScrollOffset().y, auPerCSSPixel) * aYScale); + view = new nsContentView(aFrameLoader, scrollId, metrics.mIsRoot, config); + view->mParentScaleX = aAccConfigXScale; + view->mParentScaleY = aAccConfigYScale; + } + + // I don't know what units mViewportSize is in, hence use ToUnknownRect + // here to mark the current frontier in type info propagation + gfx::Rect viewport = metrics.mViewport.ToUnknownRect(); + view->mViewportSize = nsSize( + NSIntPixelsToAppUnits(viewport.width, auPerDevPixel) * aXScale, + NSIntPixelsToAppUnits(viewport.height, auPerDevPixel) * aYScale); + view->mContentSize = nsSize( + NSFloatPixelsToAppUnits(metrics.mScrollableRect.width, auPerCSSPixel) * aXScale, + NSFloatPixelsToAppUnits(metrics.mScrollableRect.height, auPerCSSPixel) * aYScale); + + newContentViews[scrollId] = view; + } + + for (Layer* child = aLayer->GetFirstChild(); + child; child = child->GetNextSibling()) { + BuildViewMap(oldContentViews, newContentViews, aFrameLoader, child, + aXScale, aYScale, aAccConfigXScale, aAccConfigYScale); + } +} + +static void +BuildBackgroundPatternFor(ContainerLayer* aContainer, + Layer* aShadowRoot, + const ViewConfig& aConfig, + const gfxRGBA& aColor, + LayerManager* aManager, + nsIFrame* aFrame) +{ + LayerComposite* shadowRoot = aShadowRoot->AsLayerComposite(); + gfx::Matrix t; + if (!shadowRoot->GetShadowTransform().Is2D(&t)) { + return; + } + + // Get the rect bounding the shadow content, transformed into the + // same space as |aFrame| + nsIntRect contentBounds = shadowRoot->GetShadowVisibleRegion().GetBounds(); + gfxRect contentVis(contentBounds.x, contentBounds.y, + contentBounds.width, contentBounds.height); + gfxRect localContentVis(gfx::ThebesMatrix(t).Transform(contentVis)); + // Round *in* here because this area is punched out of the background + localContentVis.RoundIn(); + nsIntRect localIntContentVis(localContentVis.X(), localContentVis.Y(), + localContentVis.Width(), localContentVis.Height()); + + // Get the frame's rect + nscoord auPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); + nsIntRect frameRect = aFrame->GetRect().ToOutsidePixels(auPerDevPixel); + + // If the shadow tree covers the frame rect, don't bother building + // the background, it wouldn't be visible + if (localIntContentVis.Contains(frameRect)) { + return; + } + nsRefPtr layer = aManager->CreateColorLayer(); + layer->SetColor(aColor); + + // The visible area of the background is the frame's area minus the + // content area + nsIntRegion bgRgn(frameRect); + bgRgn.Sub(bgRgn, localIntContentVis); + bgRgn.MoveBy(-frameRect.TopLeft()); + layer->SetVisibleRegion(bgRgn); + + aContainer->InsertAfter(layer, nullptr); +} + +already_AddRefed +GetFrom(nsFrameLoader* aFrameLoader) +{ + nsIDocument* doc = aFrameLoader->GetOwnerDoc(); + return nsContentUtils::LayerManagerForDocument(doc); +} + +class RemoteContentController : public GeckoContentController { +public: + RemoteContentController(RenderFrameParent* aRenderFrame) + : mUILoop(MessageLoop::current()) + , mRenderFrame(aRenderFrame) + , mHaveZoomConstraints(false) + { } + + virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) MOZ_OVERRIDE + { + // We always need to post requests into the "UI thread" otherwise the + // requests may get processed out of order. + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &RemoteContentController::DoRequestContentRepaint, + aFrameMetrics)); + } + + virtual void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId, + const uint32_t& aScrollGeneration) MOZ_OVERRIDE + { + if (MessageLoop::current() != mUILoop) { + // We have to send this message from the "UI thread" (main + // thread). + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &RemoteContentController::AcknowledgeScrollUpdate, + aScrollId, aScrollGeneration)); + return; + } + if (mRenderFrame) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + browser->AcknowledgeScrollUpdate(aScrollId, aScrollGeneration); + } + } + + virtual void HandleDoubleTap(const CSSPoint& aPoint, + int32_t aModifiers, + const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE + { + if (MessageLoop::current() != mUILoop) { + // We have to send this message from the "UI thread" (main + // thread). + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &RemoteContentController::HandleDoubleTap, + aPoint, aModifiers, aGuid)); + return; + } + if (mRenderFrame) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + browser->HandleDoubleTap(aPoint, aModifiers, aGuid); + } + } + + virtual void HandleSingleTap(const CSSPoint& aPoint, + int32_t aModifiers, + const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE + { + if (MessageLoop::current() != mUILoop) { + // We have to send this message from the "UI thread" (main + // thread). + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &RemoteContentController::HandleSingleTap, + aPoint, aModifiers, aGuid)); + return; + } + if (mRenderFrame) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + browser->HandleSingleTap(aPoint, aModifiers, aGuid); + } + } + + virtual void HandleLongTap(const CSSPoint& aPoint, + int32_t aModifiers, + const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE + { + if (MessageLoop::current() != mUILoop) { + // We have to send this message from the "UI thread" (main + // thread). + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &RemoteContentController::HandleLongTap, + aPoint, aModifiers, aGuid)); + return; + } + if (mRenderFrame) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + browser->HandleLongTap(aPoint, aModifiers, aGuid); + } + } + + virtual void HandleLongTapUp(const CSSPoint& aPoint, + int32_t aModifiers, + const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE + { + if (MessageLoop::current() != mUILoop) { + // We have to send this message from the "UI thread" (main + // thread). + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &RemoteContentController::HandleLongTapUp, + aPoint, aModifiers, aGuid)); + return; + } + if (mRenderFrame) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + browser->HandleLongTapUp(aPoint, aModifiers, aGuid); + } + } + + void ClearRenderFrame() { mRenderFrame = nullptr; } + + virtual void SendAsyncScrollDOMEvent(bool aIsRoot, + const CSSRect& aContentRect, + const CSSSize& aContentSize) MOZ_OVERRIDE + { + if (MessageLoop::current() != mUILoop) { + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &RemoteContentController::SendAsyncScrollDOMEvent, + aIsRoot, aContentRect, aContentSize)); + return; + } + if (mRenderFrame && aIsRoot) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + BrowserElementParent::DispatchAsyncScrollEvent(browser, aContentRect, + aContentSize); + } + } + + virtual void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE + { + MessageLoop::current()->PostDelayedTask(FROM_HERE, aTask, aDelayMs); + } + + virtual bool GetRootZoomConstraints(ZoomConstraints* aOutConstraints) + { + if (mHaveZoomConstraints && aOutConstraints) { + *aOutConstraints = mZoomConstraints; + } + return mHaveZoomConstraints; + } + + virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) + { + if (mTouchSensitiveRegion.IsEmpty()) + return false; + + *aOutRegion = CSSRect::FromAppUnits(mTouchSensitiveRegion.GetBounds()); + return true; + } + + virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, + APZStateChange aChange, + int aArg) + { + if (MessageLoop::current() != mUILoop) { + mUILoop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &RemoteContentController::NotifyAPZStateChange, + aGuid, aChange, aArg)); + return; + } + if (mRenderFrame) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + browser->NotifyAPZStateChange(aGuid.mScrollId, aChange, aArg); + } + } + + // Methods used by RenderFrameParent to set fields stored here. + + void SaveZoomConstraints(const ZoomConstraints& aConstraints) + { + mHaveZoomConstraints = true; + mZoomConstraints = aConstraints; + } + + void SetTouchSensitiveRegion(const nsRegion& aRegion) + { + mTouchSensitiveRegion = aRegion; + } +private: + void DoRequestContentRepaint(const FrameMetrics& aFrameMetrics) + { + if (mRenderFrame) { + TabParent* browser = static_cast(mRenderFrame->Manager()); + browser->UpdateFrame(aFrameMetrics); + } + } + + MessageLoop* mUILoop; + RenderFrameParent* mRenderFrame; + + bool mHaveZoomConstraints; + ZoomConstraints mZoomConstraints; + nsRegion mTouchSensitiveRegion; +}; + +RenderFrameParent::RenderFrameParent() + : mLayersId(0) + , mFrameLoaderDestroyed(false) + , mBackgroundColor(gfxRGBA(1, 1, 1)) +{ +} + +void +RenderFrameParent::Init(nsFrameLoader* aFrameLoader, + ScrollingBehavior aScrollingBehavior, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aId) +{ + mFrameLoader = aFrameLoader; + + *aId = 0; + + nsRefPtr lm = GetFrom(mFrameLoader); + // Perhaps the document containing this frame currently has no presentation? + if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) { + *aTextureFactoryIdentifier = + static_cast(lm.get())->GetTextureFactoryIdentifier(); + } else { + *aTextureFactoryIdentifier = TextureFactoryIdentifier(); + } + + if (lm && lm->GetRoot() && lm->GetRoot()->AsContainerLayer()) { + ViewID rootScrollId = lm->GetRoot()->AsContainerLayer()->GetFrameMetrics().GetScrollId(); + if (rootScrollId != FrameMetrics::NULL_SCROLL_ID) { + mContentViews[rootScrollId] = new nsContentView(aFrameLoader, rootScrollId, true); + } + } + + if (CompositorParent::CompositorLoop()) { + // Our remote frame will push layers updates to the compositor, + // and we'll keep an indirect reference to that tree. + *aId = mLayersId = CompositorParent::AllocateLayerTreeId(); + if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) { + ClientLayerManager *clientManager = static_cast(lm.get()); + clientManager->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId); + } + if (aScrollingBehavior == ASYNC_PAN_ZOOM) { + mContentController = new RemoteContentController(this); + CompositorParent::SetControllerForLayerTree(mLayersId, mContentController); + } + } + // Set a default RenderFrameParent + mFrameLoader->SetCurrentRemoteFrame(this); +} + +APZCTreeManager* +RenderFrameParent::GetApzcTreeManager() +{ + // We can't get a ref to the APZCTreeManager until after the child is + // created and the static getter knows which CompositorParent is + // instantiated with this layers ID. That's why try to fetch it when + // we first need it and cache the result. + if (!mApzcTreeManager) { + mApzcTreeManager = CompositorParent::GetAPZCTreeManager(mLayersId); + } + return mApzcTreeManager.get(); +} + +RenderFrameParent::~RenderFrameParent() +{} + +void +RenderFrameParent::Destroy() +{ + size_t numChildren = ManagedPLayerTransactionParent().Length(); + NS_ABORT_IF_FALSE(0 == numChildren || 1 == numChildren, + "render frame must only have 0 or 1 layer manager"); + + if (numChildren) { + LayerTransactionParent* layers = + static_cast(ManagedPLayerTransactionParent()[0]); + layers->Destroy(); + } + + mFrameLoaderDestroyed = true; +} + +nsContentView* +RenderFrameParent::GetContentView(ViewID aId) +{ + return FindViewForId(mContentViews, aId); +} + +nsContentView* +RenderFrameParent::GetRootContentView() +{ + return FindRootView(mContentViews); +} + +void +RenderFrameParent::ContentViewScaleChanged(nsContentView* aView) +{ + // Since the scale has changed for a view, it and its descendents need their + // shadow-space attributes updated. It's easiest to rebuild the view map. + BuildViewMap(); +} + +void +RenderFrameParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const TargetConfig& aTargetConfig, + bool aIsFirstPaint, + bool aScheduleComposite) +{ + // View map must only contain views that are associated with the current + // shadow layer tree. We must always update the map when shadow layers + // are updated. + BuildViewMap(); + + TriggerRepaint(); +} + +already_AddRefed +RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, + LayerManager* aManager, + const nsIntRect& aVisibleRect, + nsDisplayItem* aItem, + const ContainerLayerParameters& aContainerParameters) +{ + NS_ABORT_IF_FALSE(aFrame, + "makes no sense to have a shadow tree without a frame"); + NS_ABORT_IF_FALSE(!mContainer || + IsTempLayerManager(aManager) || + mContainer->Manager() == aManager, + "retaining manager changed out from under us ... HELP!"); + + if (IsTempLayerManager(aManager) || + (mContainer && mContainer->Manager() != aManager)) { + // This can happen if aManager is a "temporary" manager, or if the + // widget's layer manager changed out from under us. We need to + // FIXME handle the former case somehow, probably with an API to + // draw a manager's subtree. The latter is bad bad bad, but the + // the NS_ABORT_IF_FALSE() above will flag it. Returning nullptr + // here will just cause the shadow subtree not to be rendered. + NS_WARNING("Remote iframe not rendered"); + return nullptr; + } + + uint64_t id = GetLayerTreeId(); + if (0 != id) { + MOZ_ASSERT(!GetRootLayer()); + + nsRefPtr layer = + (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); + if (!layer) { + layer = aManager->CreateRefLayer(); + } + if (!layer) { + // Probably a temporary layer manager that doesn't know how to + // use ref layers. + return nullptr; + } + static_cast(layer.get())->SetReferentId(id); + nsIntPoint offset = GetContentRectLayerOffset(aFrame, aBuilder); + layer->SetVisibleRegion(aVisibleRect - offset); + // We can only have an offset if we're a child of an inactive + // container, but our display item is LAYER_ACTIVE_FORCE which + // forces all layers above to be active. + MOZ_ASSERT(aContainerParameters.mOffset == nsIntPoint()); + gfx::Matrix4x4 m; + m.Translate(offset.x, offset.y, 0.0); + // Remote content can't be repainted by us, so we multiply down + // the resolution that our container expects onto our container. + m.Scale(aContainerParameters.mXScale, aContainerParameters.mYScale, 1.0); + layer->SetBaseTransform(m); + + return layer.forget(); + } + + if (mContainer) { + ClearContainer(mContainer); + mContainer->SetPreScale(1.0f, 1.0f); + mContainer->SetPostScale(1.0f, 1.0f); + mContainer->SetInheritedScale(1.0f, 1.0f); + } + + Layer* shadowRoot = GetRootLayer(); + if (!shadowRoot) { + mContainer = nullptr; + return nullptr; + } + + NS_ABORT_IF_FALSE(!shadowRoot || shadowRoot->Manager() == aManager, + "retaining manager changed out from under us ... HELP!"); + + // Wrap the shadow layer tree in mContainer. + if (!mContainer) { + mContainer = aManager->CreateContainerLayer(); + } + NS_ABORT_IF_FALSE(!mContainer->GetFirstChild(), + "container of shadow tree shouldn't have a 'root' here"); + + mContainer->InsertAfter(shadowRoot, nullptr); + + AssertInTopLevelChromeDoc(mContainer, aFrame); + ViewTransform transform; + TransformShadowTree(aBuilder, mFrameLoader, aFrame, shadowRoot, transform); + mContainer->SetClipRect(nullptr); + + if (mFrameLoader->AsyncScrollEnabled()) { + const nsContentView* view = GetRootContentView(); + BuildBackgroundPatternFor(mContainer, + shadowRoot, + view->GetViewConfig(), + mBackgroundColor, + aManager, aFrame); + } + mContainer->SetVisibleRegion(aVisibleRect); + + return nsRefPtr(mContainer).forget(); +} + +void +RenderFrameParent::OwnerContentChanged(nsIContent* aContent) +{ + NS_ABORT_IF_FALSE(mFrameLoader->GetOwnerContent() == aContent, + "Don't build new map if owner is same!"); + BuildViewMap(); +} + +void +RenderFrameParent::NotifyInputEvent(WidgetInputEvent& aEvent, + ScrollableLayerGuid* aOutTargetGuid) +{ + if (GetApzcTreeManager()) { + GetApzcTreeManager()->ReceiveInputEvent(aEvent, aOutTargetGuid); + } +} + +void +RenderFrameParent::ActorDestroy(ActorDestroyReason why) +{ + if (mLayersId != 0) { + CompositorParent::DeallocateLayerTreeId(mLayersId); + if (mContentController) { + // Stop our content controller from requesting repaints of our + // content. + mContentController->ClearRenderFrame(); + // TODO: notify the compositor? + } + } + + if (mFrameLoader && mFrameLoader->GetCurrentRemoteFrame() == this) { + // XXX this might cause some weird issues ... we'll just not + // redraw the part of the window covered by this until the "next" + // remote frame has a layer-tree transaction. For + // why==NormalShutdown, we'll definitely want to do something + // better, especially as nothing guarantees another Update() from + // the "next" remote layer tree. + mFrameLoader->SetCurrentRemoteFrame(nullptr); + } + mFrameLoader = nullptr; +} + +bool +RenderFrameParent::RecvNotifyCompositorTransaction() +{ + TriggerRepaint(); + return true; +} + +bool +RenderFrameParent::RecvUpdateHitRegion(const nsRegion& aRegion) +{ + mTouchRegion = aRegion; + if (mContentController) { + // Tell the content controller about the touch-sensitive region, so + // that it can provide it to APZ. This is required for APZ to do + // correct hit testing for a remote 'mozpasspointerevents' iframe + // until bug 928833 is fixed. + mContentController->SetTouchSensitiveRegion(aRegion); + } + return true; +} + +PLayerTransactionParent* +RenderFrameParent::AllocPLayerTransactionParent() +{ + if (!mFrameLoader || mFrameLoaderDestroyed) { + return nullptr; + } + nsRefPtr lm = GetFrom(mFrameLoader); + LayerTransactionParent* result = new LayerTransactionParent(lm->AsLayerManagerComposite(), this, 0); + result->AddIPDLReference(); + return result; +} + +bool +RenderFrameParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) +{ + static_cast(aLayers)->ReleaseIPDLReference(); + return true; +} + +void +RenderFrameParent::BuildViewMap() +{ + ViewMap newContentViews; + // BuildViewMap assumes we have a primary frame, which may not be the case. + if (GetRootLayer() && mFrameLoader->GetPrimaryFrameOfOwningContent()) { + // Some of the content views in our hash map may no longer be active. To + // tag them as inactive and to remove any chance of them using a dangling + // pointer, we set mContentView to nullptr. + // + // BuildViewMap will restore mFrameLoader if the content view is still + // in our hash table. + + for (ViewMap::const_iterator iter = mContentViews.begin(); + iter != mContentViews.end(); + ++iter) { + iter->second->mFrameLoader = nullptr; + } + + mozilla::layout::BuildViewMap(mContentViews, newContentViews, mFrameLoader, GetRootLayer()); + } + + // Here, we guarantee that *only* the root view is preserved in + // case we couldn't build a new view map above. This is important because + // the content view map should only contain the root view and content + // views that are present in the layer tree. + if (newContentViews.empty()) { + nsContentView* rootView = FindRootView(mContentViews); + if (rootView) + newContentViews[rootView->GetId()] = rootView; + } + + mContentViews = newContentViews; +} + +void +RenderFrameParent::TriggerRepaint() +{ + mFrameLoader->SetCurrentRemoteFrame(this); + + nsIFrame* docFrame = mFrameLoader->GetPrimaryFrameOfOwningContent(); + if (!docFrame) { + // Bad, but nothing we can do about it (XXX/cjones: or is there? + // maybe bug 589337?). When the new frame is created, we'll + // probably still be the current render frame and will get to draw + // our content then. Or, we're shutting down and this update goes + // to /dev/null. + return; + } + + docFrame->InvalidateLayer(nsDisplayItem::TYPE_REMOTE); +} + +LayerTransactionParent* +RenderFrameParent::GetShadowLayers() const +{ + const InfallibleTArray& shadowParents = ManagedPLayerTransactionParent(); + NS_ABORT_IF_FALSE(shadowParents.Length() <= 1, + "can only support at most 1 LayerTransactionParent"); + return (shadowParents.Length() == 1) ? + static_cast(shadowParents[0]) : nullptr; +} + +uint64_t +RenderFrameParent::GetLayerTreeId() const +{ + return mLayersId; +} + +Layer* +RenderFrameParent::GetRootLayer() const +{ + LayerTransactionParent* shadowLayers = GetShadowLayers(); + return shadowLayers ? shadowLayers->GetRoot() : nullptr; +} + +void +RenderFrameParent::BuildDisplayList(nsDisplayListBuilder* aBuilder, + nsSubDocumentFrame* aFrame, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + // We're the subdoc for and it has + // painted content. Display its shadow layer tree. + DisplayListClipState::AutoSaveRestore clipState(aBuilder); + + nsPoint offset = aBuilder->ToReferenceFrame(aFrame); + nsRect bounds = aFrame->EnsureInnerView()->GetBounds() + offset; + clipState.ClipContentDescendants(bounds); + + Layer* container = GetRootLayer(); + if (aBuilder->IsForEventDelivery() && container) { + ViewTransform offset = + ViewTransform(GetContentRectLayerOffset(aFrame, aBuilder)); + BuildListForLayer(container, mFrameLoader, offset, + aBuilder, *aLists.Content(), aFrame); + } else { + aLists.Content()->AppendToTop( + new (aBuilder) nsDisplayRemote(aBuilder, aFrame, this)); + } +} + +void +RenderFrameParent::ZoomToRect(uint32_t aPresShellId, ViewID aViewId, + const CSSRect& aRect) +{ + if (GetApzcTreeManager()) { + GetApzcTreeManager()->ZoomToRect(ScrollableLayerGuid(mLayersId, aPresShellId, aViewId), + aRect); + } +} + +void +RenderFrameParent::ContentReceivedTouch(const ScrollableLayerGuid& aGuid, + bool aPreventDefault) +{ + if (aGuid.mLayersId != mLayersId) { + // Guard against bad data from hijacked child processes + NS_ERROR("Unexpected layers id in ContentReceivedTouch; dropping message..."); + return; + } + if (GetApzcTreeManager()) { + GetApzcTreeManager()->ContentReceivedTouch(aGuid, aPreventDefault); + } +} + +void +RenderFrameParent::UpdateZoomConstraints(uint32_t aPresShellId, + ViewID aViewId, + bool aIsRoot, + const ZoomConstraints& aConstraints) +{ + if (mContentController && aIsRoot) { + mContentController->SaveZoomConstraints(aConstraints); + } + if (GetApzcTreeManager()) { + GetApzcTreeManager()->UpdateZoomConstraints(ScrollableLayerGuid(mLayersId, aPresShellId, aViewId), + aConstraints); + } +} + +bool +RenderFrameParent::HitTest(const nsRect& aRect) +{ + return mTouchRegion.Contains(aRect); +} + +} // namespace layout +} // namespace mozilla + +already_AddRefed +nsDisplayRemote::BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) +{ + int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); + nsIntRect visibleRect = GetVisibleRect().ToNearestPixels(appUnitsPerDevPixel); + visibleRect += aContainerParameters.mOffset; + nsRefPtr layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, visibleRect, this, aContainerParameters); + return layer.forget(); +} + +void +nsDisplayRemote::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, + HitTestState* aState, nsTArray *aOutFrames) +{ + if (mRemoteFrame->HitTest(aRect)) { + aOutFrames->AppendElement(mFrame); + } +} + +void +nsDisplayRemoteShadow::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, + HitTestState* aState, nsTArray *aOutFrames) +{ + // If we are here, then rects have intersected. + // + // XXX I think iframes and divs can be rounded like anything else but we don't + // cover that case here. + // + if (aState->mShadows) { + aState->mShadows->AppendElement(mId); + } +}