gecko/layout/ipc/RenderFrameParent.cpp
Robert O'Callahan b550e89fa5 Bug 1022612. Part 27: Make FrameLayerBuilder responsible for setting all layer visible regions. r=mattwoodrow
Calling Layer::SetVisibleRegion multiple times in a transaction can result in
unnecessary IPC traffic.

This patch removes Intersect(childGfxBounds). This is only needed to
restrict the visible region to something sane for 3D transforms, and this will
be fixed up in a later patch.

--HG--
extra : rebase_source : 5931e3462734983134193b766410593288afc296
2014-06-18 15:12:55 +12:00

1203 lines
42 KiB
C++

/* -*- 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/ContentChild.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<BasicLayerManager*>(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<BasicLayerManager*>(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<ColorLayer> 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<LayerManager>
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<TabParent*>(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<TabParent*>(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<TabParent*>(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<TabParent*>(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<TabParent*>(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<TabParent*>(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<TabParent*>(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<TabParent*>(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<LayerManager> lm = GetFrom(mFrameLoader);
// Perhaps the document containing this frame currently has no presentation?
if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
*aTextureFactoryIdentifier =
static_cast<ClientLayerManager*>(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 (gfxPlatform::UsesOffMainThreadCompositing() &&
XRE_GetProcessType() == GeckoProcessType_Default) {
// 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<ClientLayerManager*>(lm.get());
clientManager->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId);
}
if (aScrollingBehavior == ASYNC_PAN_ZOOM) {
mContentController = new RemoteContentController(this);
CompositorParent::SetControllerForLayerTree(mLayersId, mContentController);
}
} else if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild::GetSingleton()->SendAllocateLayerTreeId(aId);
mLayersId = *aId;
CompositorChild::Get()->SendNotifyChildCreated(mLayersId);
}
// 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<LayerTransactionParent*>(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<Layer>
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> 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<RefLayer*>(layer.get())->SetReferentId(id);
nsIntPoint offset = GetContentRectLayerOffset(aFrame, aBuilder);
// 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);
}
return nsRefPtr<Layer>(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) {
if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild::GetSingleton()->SendDeallocateLayerTreeId(mLayersId);
} else {
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<LayerManager> lm = GetFrom(mFrameLoader);
LayerTransactionParent* result = new LayerTransactionParent(lm->AsLayerManagerComposite(), this, 0, 0);
result->AddIPDLReference();
return result;
}
bool
RenderFrameParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers)
{
static_cast<LayerTransactionParent*>(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<PLayerTransactionParent*>& shadowParents = ManagedPLayerTransactionParent();
NS_ABORT_IF_FALSE(shadowParents.Length() <= 1,
"can only support at most 1 LayerTransactionParent");
return (shadowParents.Length() == 1) ?
static_cast<LayerTransactionParent*>(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 <browser remote="true"> 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<Layer>
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> layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, visibleRect, this, aContainerParameters);
return layer.forget();
}
void
nsDisplayRemote::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
{
if (mRemoteFrame->HitTest(aRect)) {
aOutFrames->AppendElement(mFrame);
}
}
void
nsDisplayRemoteShadow::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState, nsTArray<nsIFrame*> *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);
}
}