/* -*- 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(nsFrameLoader* aFrameLoader, ScrollingBehavior aScrollingBehavior, TextureFactoryIdentifier* aTextureFactoryIdentifier, uint64_t* aId, bool* aSuccess) : mLayersId(0) , mFrameLoader(aFrameLoader) , mFrameLoaderDestroyed(false) , mBackgroundColor(gfxRGBA(1, 1, 1)) { *aSuccess = false; if (!mFrameLoader) { return; } *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); *aSuccess = true; } 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 uint64_t& aTransactionId, const TargetConfig& aTargetConfig, bool aIsFirstPaint, bool aScheduleComposite, uint32_t aPaintSequenceNumber) { // 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(); } nsEventStatus RenderFrameParent::NotifyInputEvent(WidgetInputEvent& aEvent, ScrollableLayerGuid* aOutTargetGuid) { if (GetApzcTreeManager()) { return GetApzcTreeManager()->ReceiveInputEvent(aEvent, aOutTargetGuid); } return nsEventStatus_eIgnore; } 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, 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); } }