Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "BrowserChild.h"
#ifdef ACCESSIBILITY
# include "mozilla/a11y/DocAccessibleChild.h"
#endif
#include <algorithm>
#include <utility>
#include "BrowserParent.h"
#include "ClientLayerManager.h"
#include "ContentChild.h"
#include "DocumentInlines.h"
#include "EventStateManager.h"
#include "FrameLayerBuilder.h"
#include "Layers.h"
#include "LayersLogging.h"
#include "MMPrinter.h"
#include "PermissionMessageUtils.h"
#include "PuppetWidget.h"
#include "StructuredCloneData.h"
#include "UnitTransforms.h"
#include "Units.h"
#include "VRManagerChild.h"
#include "ipc/nsGUIEventIPC.h"
#include "js/JSON.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/EventForwards.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/ProcessHangMonitor.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/AutoPrintEventDispatcher.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/JSWindowActorChild.h"
#include "mozilla/dom/LoadURIOptionsBinding.h"
#include "mozilla/dom/MessageManagerBinding.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/PBrowser.h"
#include "mozilla/dom/PaymentRequestChild.h"
#include "mozilla/dom/SessionStoreListener.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/gfx/CrossProcessPaint.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/APZCTreeManagerChild.h"
#include "mozilla/layers/APZChild.h"
#include "mozilla/layers/APZEventState.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/ContentProcessController.h"
#include "mozilla/layers/DoubleTapToZoom.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/ShadowLayers.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/plugins/PPluginWidgetChild.h"
#include "nsBrowserStatusFilter.h"
#include "nsColorPickerProxy.h"
#include "nsCommandParams.h"
#include "nsContentPermissionHelper.h"
#include "nsContentUtils.h"
#include "nsDeviceContext.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsEmbedCID.h"
#include "nsExceptionHandler.h"
#include "nsFilePickerProxy.h"
#include "nsFocusManager.h"
#include "nsGlobalWindow.h"
#include "nsIBaseWindow.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIClassifiedChannel.h"
#include "nsIDocShell.h"
#include "nsIFrame.h"
#include "nsILoadContext.h"
#include "nsISHEntry.h"
#include "nsISHistory.h"
#include "nsIScriptError.h"
#include "nsISecureBrowserUI.h"
#include "nsIURI.h"
#include "nsIURIMutator.h"
#include "nsIWeakReferenceUtils.h"
#include "nsIWebBrowser.h"
#include "nsIWebProgress.h"
#include "nsLayoutUtils.h"
#include "nsNetUtil.h"
#include "nsIOpenWindowInfo.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowRoot.h"
#include "nsPointerHashKeys.h"
#include "nsPrintfCString.h"
#include "nsQueryActor.h"
#include "nsQueryObject.h"
#include "nsRefreshDriver.h"
#include "nsSandboxFlags.h"
#include "nsString.h"
#include "nsTHashtable.h"
#include "nsThreadManager.h"
#include "nsThreadUtils.h"
#include "nsViewManager.h"
#include "nsViewportInfo.h"
#include "nsWebBrowser.h"
#include "nsWindowWatcher.h"
#ifdef XP_WIN
# include "mozilla/plugins/PluginWidgetChild.h"
#endif
#ifdef NS_PRINTING
# include "nsIPrintSession.h"
# include "nsIPrintSettings.h"
# include "nsIPrintSettingsService.h"
# include "nsIWebBrowserPrint.h"
#endif
static mozilla::LazyLogModule sApzChildLog("apz.child");
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::dom::ipc;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::docshell;
using namespace mozilla::widget;
using mozilla::layers::GeckoContentController;
NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener)
static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
typedef nsDataHashtable<nsUint64HashKey, BrowserChild*> BrowserChildMap;
static BrowserChildMap* sBrowserChildren;
StaticMutex sBrowserChildrenMutex;
already_AddRefed<Document> BrowserChild::GetTopLevelDocument() const {
nsCOMPtr<Document> doc;
WebNavigation()->GetDocument(getter_AddRefs(doc));
return doc.forget();
}
PresShell* BrowserChild::GetTopLevelPresShell() const {
if (RefPtr<Document> doc = GetTopLevelDocument()) {
return doc->GetPresShell();
}
return nullptr;
}
bool BrowserChild::UpdateFrame(const RepaintRequest& aRequest) {
MOZ_ASSERT(aRequest.GetScrollId() != ScrollableLayerGuid::NULL_SCROLL_ID);
if (aRequest.IsRootContent()) {
if (PresShell* presShell = GetTopLevelPresShell()) {
// Guard against stale updates (updates meant for a pres shell which
// has since been torn down and destroyed).
if (aRequest.GetPresShellId() == presShell->GetPresShellId()) {
APZCCallbackHelper::UpdateRootFrame(aRequest);
return true;
}
}
} else {
// aRequest.mIsRoot is false, so we are trying to update a subframe.
// This requires special handling.
APZCCallbackHelper::UpdateSubFrame(aRequest);
return true;
}
return true;
}
NS_IMETHODIMP
ContentListener::HandleEvent(Event* aEvent) {
RemoteDOMEvent remoteEvent;
remoteEvent.mEvent = aEvent;
NS_ENSURE_STATE(remoteEvent.mEvent);
mBrowserChild->SendEvent(remoteEvent);
return NS_OK;
}
class BrowserChild::DelayedDeleteRunnable final : public Runnable,
public nsIRunnablePriority {
RefPtr<BrowserChild> mBrowserChild;
// In order to ensure that this runnable runs after everything that could
// possibly touch this tab, we send it through the event queue twice. The
// first time it runs at normal priority and the second time it runs at
// input priority. This ensures that it runs after all events that were in
// either queue at the time it was first dispatched. mReadyToDelete starts
// out false (when it runs at normal priority) and is then set to true.
bool mReadyToDelete = false;
public:
explicit DelayedDeleteRunnable(BrowserChild* aBrowserChild)
: Runnable("BrowserChild::DelayedDeleteRunnable"),
mBrowserChild(aBrowserChild) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aBrowserChild);
}
NS_DECL_ISUPPORTS_INHERITED
private:
~DelayedDeleteRunnable() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mBrowserChild);
}
NS_IMETHOD GetPriority(uint32_t* aPriority) override {
*aPriority = mReadyToDelete ? nsIRunnablePriority::PRIORITY_INPUT_HIGH
: nsIRunnablePriority::PRIORITY_NORMAL;
return NS_OK;
}
NS_IMETHOD
Run() override {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBrowserChild);
if (!mReadyToDelete) {
// This time run this runnable at input priority.
mReadyToDelete = true;
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
return NS_OK;
}
// Check in case ActorDestroy was called after RecvDestroy message.
if (mBrowserChild->IPCOpen()) {
Unused << PBrowserChild::Send__delete__(mBrowserChild);
}
mBrowserChild = nullptr;
return NS_OK;
}
};
NS_IMPL_ISUPPORTS_INHERITED(BrowserChild::DelayedDeleteRunnable, Runnable,
nsIRunnablePriority)
namespace {
std::map<TabId, RefPtr<BrowserChild>>& NestedBrowserChildMap() {
MOZ_ASSERT(NS_IsMainThread());
static std::map<TabId, RefPtr<BrowserChild>> sNestedBrowserChildMap;
return sNestedBrowserChildMap;
}
} // namespace
already_AddRefed<BrowserChild> BrowserChild::FindBrowserChild(
const TabId& aTabId) {
auto iter = NestedBrowserChildMap().find(aTabId);
if (iter == NestedBrowserChildMap().end()) {
return nullptr;
}
RefPtr<BrowserChild> browserChild = iter->second;
return browserChild.forget();
}
/*static*/
already_AddRefed<BrowserChild> BrowserChild::Create(
ContentChild* aManager, const TabId& aTabId, const TabContext& aContext,
BrowsingContext* aBrowsingContext, uint32_t aChromeFlags,
bool aIsTopLevel) {
RefPtr<BrowserChild> iframe = new BrowserChild(
aManager, aTabId, aContext, aBrowsingContext, aChromeFlags, aIsTopLevel);
return iframe.forget();
}
BrowserChild::BrowserChild(ContentChild* aManager, const TabId& aTabId,
const TabContext& aContext,
BrowsingContext* aBrowsingContext,
uint32_t aChromeFlags, bool aIsTopLevel)
: TabContext(aContext),
mBrowserChildMessageManager(nullptr),
mManager(aManager),
mBrowsingContext(aBrowsingContext),
mChromeFlags(aChromeFlags),
mMaxTouchPoints(0),
mLayersId{0},
mEffectsInfo{EffectsInfo::FullyHidden()},
mDidFakeShow(false),
mNotified(false),
mTriedBrowserInit(false),
mOrientation(hal::eScreenOrientation_PortraitPrimary),
mIgnoreKeyPressEvent(false),
mHasValidInnerSize(false),
mDestroyed(false),
mDynamicToolbarMaxHeight(0),
mUniqueId(aTabId),
mIsTopLevel(aIsTopLevel),
mHasSiblings(false),
mIsTransparent(false),
mIPCOpen(false),
mParentIsActive(false),
mDidSetRealShowInfo(false),
mDidLoadURLInit(false),
mSkipKeyPress(false),
mLayersObserverEpoch{1},
#if defined(XP_WIN) && defined(ACCESSIBILITY)
mNativeWindowHandle(0),
#endif
#if defined(ACCESSIBILITY)
mTopLevelDocAccessibleChild(nullptr),
#endif
mShouldSendWebProgressEventsToParent(false),
mRenderLayers(true),
mPendingDocShellIsActive(false),
mPendingSuspendMediaWhenInactive(false),
mPendingDocShellReceivedMessage(false),
mPendingRenderLayers(false),
mPendingRenderLayersReceivedMessage(false),
mPendingLayersObserverEpoch{0},
mPendingDocShellBlockers(0),
mCancelContentJSEpoch(0),
mWidgetNativeData(0) {
mozilla::HoldJSObjects(this);
nsWeakPtr weakPtrThis(do_GetWeakReference(
static_cast<nsIBrowserChild*>(this))); // for capture by the lambda
mSetAllowedTouchBehaviorCallback =
[weakPtrThis](uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags>& aFlags) {
if (nsCOMPtr<nsIBrowserChild> browserChild =
do_QueryReferent(weakPtrThis)) {
static_cast<BrowserChild*>(browserChild.get())
->SetAllowedTouchBehavior(aInputBlockId, aFlags);
}
};
// preloaded BrowserChild should not be added to child map
if (mUniqueId) {
MOZ_ASSERT(NestedBrowserChildMap().find(mUniqueId) ==
NestedBrowserChildMap().end());
NestedBrowserChildMap()[mUniqueId] = this;
}
mCoalesceMouseMoveEvents =
Preferences::GetBool("dom.event.coalesce_mouse_move");
if (mCoalesceMouseMoveEvents) {
mCoalescedMouseEventFlusher = new CoalescedMouseMoveFlusher(this);
}
}
const CompositorOptions& BrowserChild::GetCompositorOptions() const {
// If you're calling this before mCompositorOptions is set, well.. don't.
MOZ_ASSERT(mCompositorOptions);
return mCompositorOptions.ref();
}
bool BrowserChild::AsyncPanZoomEnabled() const {
// This might get called by the TouchEvent::PrefEnabled code before we have
// mCompositorOptions populated (bug 1370089). In that case we just assume
// APZ is enabled because we're in a content process (because BrowserChild)
// and APZ is probably going to be enabled here since e10s is enabled.
return mCompositorOptions ? mCompositorOptions->UseAPZ() : true;
}
NS_IMETHODIMP
BrowserChild::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) {
if (AsyncPanZoomEnabled()) {
nsCOMPtr<Document> subject(do_QueryInterface(aSubject));
nsCOMPtr<Document> doc(GetTopLevelDocument());
if (subject == doc && doc->IsTopLevelContentDocument()) {
RefPtr<PresShell> presShell = doc->GetPresShell();
if (presShell) {
presShell->SetIsFirstPaint(true);
}
APZCCallbackHelper::InitializeRootDisplayport(presShell);
}
}
}
return NS_OK;
}
void BrowserChild::ContentReceivedInputBlock(uint64_t aInputBlockId,
bool aPreventDefault) const {
if (mApzcTreeManager) {
mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
}
}
void BrowserChild::SetTargetAPZC(
uint64_t aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets) const {
if (mApzcTreeManager) {
mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets);
}
}
void BrowserChild::SetAllowedTouchBehavior(
uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags>& aTargets) const {
if (mApzcTreeManager) {
mApzcTreeManager->SetAllowedTouchBehavior(aInputBlockId, aTargets);
}
}
bool BrowserChild::DoUpdateZoomConstraints(
const uint32_t& aPresShellId, const ViewID& aViewId,
const Maybe<ZoomConstraints>& aConstraints) {
if (!mApzcTreeManager || mDestroyed) {
return false;
}
ScrollableLayerGuid guid =
ScrollableLayerGuid(mLayersId, aPresShellId, aViewId);
mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints);
return true;
}
nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
WindowGlobalChild* aInitialWindowChild) {
MOZ_ASSERT_IF(aInitialWindowChild,
aInitialWindowChild->BrowsingContext() == mBrowsingContext);
nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(this);
mPuppetWidget = static_cast<PuppetWidget*>(widget.get());
if (!mPuppetWidget) {
NS_ERROR("couldn't create fake widget");
return NS_ERROR_FAILURE;
}
mPuppetWidget->InfallibleCreate(nullptr,
nullptr, // no parents
LayoutDeviceIntRect(0, 0, 0, 0),
nullptr); // HandleWidgetEvent
mWebBrowser = nsWebBrowser::Create(this, mPuppetWidget, mBrowsingContext,
aInitialWindowChild);
nsIWebBrowser* webBrowser = mWebBrowser;
mWebNav = do_QueryInterface(webBrowser);
NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?");
// Set the tab context attributes then pass to docShell
NotifyTabContextUpdated();
// IPC uses a WebBrowser object for which DNS prefetching is turned off
// by default. But here we really want it, so enable it explicitly
mWebBrowser->SetAllowDNSPrefetch(true);
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
MOZ_ASSERT(docShell);
mStatusFilter = new nsBrowserStatusFilter();
nsresult rv =
mStatusFilter->AddProgressListener(this, nsIWebProgress::NOTIFY_ALL);
NS_ENSURE_SUCCESS(rv, rv);
{
nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(docShell);
rv = webProgress->AddProgressListener(mStatusFilter,
nsIWebProgress::NOTIFY_ALL);
NS_ENSURE_SUCCESS(rv, rv);
}
docShell->SetAffectPrivateSessionLifetime(
mBrowsingContext->UsePrivateBrowsing() ||
mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
#ifdef DEBUG
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation());
MOZ_ASSERT(loadContext);
MOZ_ASSERT(loadContext->UseRemoteTabs() ==
!!(mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW));
MOZ_ASSERT(loadContext->UseRemoteSubframes() ==
!!(mChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW));
#endif // defined(DEBUG)
// Few lines before, baseWindow->Create() will end up creating a new
// window root in nsGlobalWindow::SetDocShell.
// Then this chrome event handler, will be inherited to inner windows.
// We want to also set it to the docshell so that inner windows
// and any code that has access to the docshell
// can all listen to the same chrome event handler.
// XXX: ideally, we would set a chrome event handler earlier,
// and all windows, even the root one, will use the docshell one.
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
nsCOMPtr<EventTarget> chromeHandler = window->GetChromeEventHandler();
docShell->SetChromeEventHandler(chromeHandler);
if (window->GetCurrentInnerWindow()) {
window->SetKeyboardIndicators(ShowFocusRings());
} else {
// Skip ShouldShowFocusRing check if no inner window is available
window->SetInitialKeyboardIndicators(ShowFocusRings());
}
// Window scrollbar flags only affect top level remote frames, not fission
// frames.
if (mIsTopLevel) {
nsContentUtils::SetScrollbarsVisibility(
docShell, !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS));
}
nsWeakPtr weakPtrThis = do_GetWeakReference(
static_cast<nsIBrowserChild*>(this)); // for capture by the lambda
ContentReceivedInputBlockCallback callback(
[weakPtrThis](uint64_t aInputBlockId, bool aPreventDefault) {
if (nsCOMPtr<nsIBrowserChild> browserChild =
do_QueryReferent(weakPtrThis)) {
static_cast<BrowserChild*>(browserChild.get())
->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
}
});
mAPZEventState = new APZEventState(mPuppetWidget, std::move(callback));
mIPCOpen = true;
#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_THUNDERBIRD) && \
!defined(MOZ_SUITE)
mSessionStoreListener = new TabListener(docShell, nullptr);
rv = mSessionStoreListener->Init();
NS_ENSURE_SUCCESS(rv, rv);
#endif
// We've all set up, make sure our visibility state is consistent. This is
// important for OOP iframes, which start off as hidden.
UpdateVisibility();
return NS_OK;
}
void BrowserChild::NotifyTabContextUpdated() {
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
MOZ_ASSERT(docShell);
if (!docShell) {
return;
}
// Set SANDBOXED_AUXILIARY_NAVIGATION flag if this is a receiver page.
if (!PresentationURL().IsEmpty()) {
// Return value of setting synced field should be checked. See bug 1656492.
Unused << mBrowsingContext->SetSandboxFlags(SANDBOXED_AUXILIARY_NAVIGATION);
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(BrowserChild)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowserChild)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChildMessageManager)
tmp->nsMessageManagerScriptExecutor::Unlink();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebBrowserChrome)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusFilter)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebNav)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowserChild)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChildMessageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebBrowserChrome)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusFilter)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebNav)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowserChild)
tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserChild)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
NS_INTERFACE_MAP_ENTRY(nsIBrowserChild)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsITooltipListener)
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener2)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIBrowserChild)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserChild)
NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserChild)
NS_IMETHODIMP
BrowserChild::GetChromeFlags(uint32_t* aChromeFlags) {
*aChromeFlags = mChromeFlags;
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::SetChromeFlags(uint32_t aChromeFlags) {
NS_WARNING("trying to SetChromeFlags from content process?");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
BrowserChild::RemoteSizeShellTo(int32_t aWidth, int32_t aHeight,
int32_t aShellItemWidth,
int32_t aShellItemHeight) {
nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(ourDocShell));
NS_ENSURE_STATE(docShellAsWin);
int32_t width, height;
docShellAsWin->GetSize(&width, &height);
uint32_t flags = 0;
if (width == aWidth) {
flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX;
}
if (height == aHeight) {
flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY;
}
bool sent = SendSizeShellTo(flags, aWidth, aHeight, aShellItemWidth,
aShellItemHeight);
return sent ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
BrowserChild::RemoteDropLinks(
const nsTArray<RefPtr<nsIDroppedLinkItem>>& aLinks) {
nsTArray<nsString> linksArray;
nsresult rv = NS_OK;
for (nsIDroppedLinkItem* link : aLinks) {
nsString tmp;
rv = link->GetUrl(tmp);
if (NS_FAILED(rv)) {
return rv;
}
linksArray.AppendElement(tmp);
rv = link->GetName(tmp);
if (NS_FAILED(rv)) {
return rv;
}
linksArray.AppendElement(tmp);
rv = link->GetType(tmp);
if (NS_FAILED(rv)) {
return rv;
}
linksArray.AppendElement(tmp);
}
bool sent = SendDropLinks(linksArray);
return sent ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
BrowserChild::ShowAsModal() {
NS_WARNING("BrowserChild::ShowAsModal not supported in BrowserChild");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
BrowserChild::IsWindowModal(bool* aRetVal) {
*aRetVal = false;
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::SetLinkStatus(const nsAString& aStatusText) {
// We can only send the status after the ipc machinery is set up
if (IPCOpen()) {
SendSetLinkStatus(nsString(aStatusText));
}
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY,
int32_t aCx, int32_t aCy) {
// The parent is in charge of the dimension changes. If JS code wants to
// change the dimensions (moveTo, screenX, etc.) we send a message to the
// parent about the new requested dimension, the parent does the resize/move
// then send a message to the child to update itself. For APIs like screenX
// this function is called with the current value for the non-changed values.
// In a series of calls like window.screenX = 10; window.screenY = 10; for
// the second call, since screenX is not yet updated we might accidentally
// reset back screenX to it's old value. To avoid this if a parameter did not
// change we want the parent to ignore its value.
int32_t x, y, cx, cy;
GetDimensions(aFlags, &x, &y, &cx, &cy);
if (x == aX) {
aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X;
}
if (y == aY) {
aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y;
}
if (cx == aCx) {
aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX;
}
if (cy == aCy) {
aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY;
}
double scale = mPuppetWidget ? mPuppetWidget->GetDefaultScale().scale : 1.0;
Unused << SendSetDimensions(aFlags, aX, aY, aCx, aCy, scale);
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::GetDimensions(uint32_t aFlags, int32_t* aX, int32_t* aY,
int32_t* aCx, int32_t* aCy) {
ScreenIntRect rect = GetOuterRect();
if (aX) {
*aX = rect.x;
}
if (aY) {
*aY = rect.y;
}
if (aCx) {
*aCx = rect.width;
}
if (aCy) {
*aCy = rect.height;
}
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::SetFocus() { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHODIMP
BrowserChild::GetVisibility(bool* aVisibility) {
*aVisibility = true;
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::SetVisibility(bool aVisibility) {
// should the platform support this? Bug 666365
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::GetTitle(nsAString& aTitle) {
NS_WARNING("BrowserChild::GetTitle not supported in BrowserChild");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
BrowserChild::SetTitle(const nsAString& aTitle) {
// JavaScript sends the "DOMTitleChanged" event to the parent
// via the message manager.
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::GetSiteWindow(void** aSiteWindow) {
NS_WARNING("BrowserChild::GetSiteWindow not supported in BrowserChild");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
BrowserChild::Blur() { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHODIMP
BrowserChild::FocusNextElement(bool aForDocumentNavigation) {
SendMoveFocus(true, aForDocumentNavigation);
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::FocusPrevElement(bool aForDocumentNavigation) {
SendMoveFocus(false, aForDocumentNavigation);
return NS_OK;
}
NS_IMETHODIMP
BrowserChild::GetInterface(const nsIID& aIID, void** aSink) {
if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome3))) {
return GetWebBrowserChrome(reinterpret_cast<nsIWebBrowserChrome3**>(aSink));
}
// XXXbz should we restrict the set of interfaces we hand out here?
// See bug 537429
return QueryInterface(aIID, aSink);
}
NS_IMETHODIMP
BrowserChild::ProvideWindow(nsIOpenWindowInfo* aOpenWindowInfo,
uint32_t aChromeFlags, bool aCalledFromJS,
bool aWidthSpecified, nsIURI* aURI,
const nsAString& aName, const nsACString& aFeatures,
bool aForceNoOpener, bool aForceNoReferrer,
nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
BrowsingContext** aReturn) {
*aReturn = nullptr;
RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
parent->GetDOMWindow(), aChromeFlags, aCalledFromJS, aWidthSpecified,
aOpenWindowInfo->GetIsForPrinting());
// If it turns out we're opening in the current browser, just hand over the
// current browser's docshell.
if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation());
*aWindowIsNew = false;
nsCOMPtr<mozIDOMWindowProxy> win;
MOZ_TRY(browser->GetContentDOMWindow(getter_AddRefs(win)));
RefPtr<BrowsingContext> bc(
nsPIDOMWindowOuter::From(win)->GetBrowsingContext());
bc.forget(aReturn);
return NS_OK;
}
// Note that ProvideWindowCommon may return NS_ERROR_ABORT if the
// open window call was canceled. It's important that we pass this error
// code back to our caller.
ContentChild* cc = ContentChild::GetSingleton();
return cc->ProvideWindowCommon(this, aOpenWindowInfo, aChromeFlags,
aCalledFromJS, aWidthSpecified, aURI, aName,
aFeatures, aForceNoOpener, aForceNoReferrer,
aLoadState, aWindowIsNew, aReturn);
}
void BrowserChild::DestroyWindow() {
mBrowsingContext = nullptr;
if (mStatusFilter) {
if (nsCOMPtr<nsIWebProgress> webProgress =
do_QueryInterface(WebNavigation())) {
webProgress->RemoveProgressListener(mStatusFilter);
}
mStatusFilter->RemoveProgressListener(this);
mStatusFilter = nullptr;
}
if (mCoalescedMouseEventFlusher) {
mCoalescedMouseEventFlusher->RemoveObserver();
mCoalescedMouseEventFlusher = nullptr;
}
if (mSessionStoreListener) {
mSessionStoreListener->RemoveListeners();
mSessionStoreListener = nullptr;
}
// In case we don't have chance to process all entries, clean all data in
// the queue.
while (mToBeDispatchedMouseData.GetSize() > 0) {
UniquePtr<CoalescedMouseData> data(
static_cast<CoalescedMouseData*>(mToBeDispatchedMouseData.PopFront()));
data.reset();
}
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
if (baseWindow) baseWindow->Destroy();
if (mPuppetWidget) {
mPuppetWidget->Destroy();
}
mLayersConnected = Nothing();
if (mLayersId.IsValid()) {
StaticMutexAutoLock lock(sBrowserChildrenMutex);
MOZ_ASSERT(sBrowserChildren);
sBrowserChildren->Remove(uint64_t(mLayersId));
if (!sBrowserChildren->Count()) {
delete sBrowserChildren;
sBrowserChildren = nullptr;
}
mLayersId = layers::LayersId{0};
}
}
void BrowserChild::ActorDestroy(ActorDestroyReason why) {
mIPCOpen = false;
DestroyWindow();
if (mBrowserChildMessageManager) {
// We should have a message manager if the global is alive, but it
// seems sometimes we don't. Assert in aurora/nightly, but don't
// crash in release builds.
MOZ_DIAGNOSTIC_ASSERT(mBrowserChildMessageManager->GetMessageManager());
if (mBrowserChildMessageManager->GetMessageManager()) {
// The messageManager relays messages via the BrowserChild which
// no longer exists.
mBrowserChildMessageManager->DisconnectMessageManager();
}
}
CompositorBridgeChild* compositorChild = CompositorBridgeChild::Get();
if (compositorChild) {
compositorChild->CancelNotifyAfterRemotePaint(this);
}
if (GetTabId() != 0) {
NestedBrowserChildMap().erase(GetTabId());
}
}
BrowserChild::~BrowserChild() {
mAnonymousGlobalScopes.Clear();
DestroyWindow();
nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
if (webBrowser) {
webBrowser->SetContainerWindow(nullptr);
}
mozilla::DropJSObjects(this);
}
mozilla::ipc::IPCResult BrowserChild::RecvWillChangeProcess(
WillChangeProcessResolver&& aResolve) {
if (mWebBrowser) {
mWebBrowser->SetWillChangeProcess();
}
aResolve(true);
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvLoadURL(
nsDocShellLoadState* aLoadState, const ParentShowInfo& aInfo) {
if (!mDidLoadURLInit) {
mDidLoadURLInit = true;
if (!InitBrowserChildMessageManager()) {
return IPC_FAIL_NO_REASON(this);
}
ApplyParentShowInfo(aInfo);
}
nsAutoCString spec;
aLoadState->URI()->GetSpec(spec);
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
MOZ_ASSERT(docShell);
if (!docShell) {
NS_WARNING("WebNavigation does not have a docshell");
}
docShell->LoadURI(aLoadState, true);
nsDocShell::Cast(docShell)->MaybeClearStorageAccessFlag();
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, spec);
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvResumeLoad(
const uint64_t& aPendingSwitchID, const ParentShowInfo& aInfo) {
if (!mDidLoadURLInit) {
mDidLoadURLInit = true;
if (!InitBrowserChildMessageManager()) {
return IPC_FAIL_NO_REASON(this);
}
ApplyParentShowInfo(aInfo);
}
nsresult rv = WebNavigation()->ResumeRedirectedLoad(aPendingSwitchID, -1);
if (NS_FAILED(rv)) {
NS_WARNING("WebNavigation()->ResumeRedirectedLoad failed");
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvCloneDocumentTreeIntoSelf(
const MaybeDiscarded<BrowsingContext>& aSourceBC) {
if (NS_WARN_IF(aSourceBC.IsNullOrDiscarded())) {
return IPC_OK();
}
nsCOMPtr<Document> sourceDocument = aSourceBC.get()->GetDocument();
if (NS_WARN_IF(!sourceDocument)) {
return IPC_OK();
}
nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
if (NS_WARN_IF(!ourDocShell)) {
return IPC_OK();
}
nsCOMPtr<nsIContentViewer> cv;
ourDocShell->GetContentViewer(getter_AddRefs(cv));
if (NS_WARN_IF(!cv)) {
return IPC_OK();
}
RefPtr<Document> clone;
{
AutoPrintEventDispatcher dispatcher(*sourceDocument);
nsAutoScriptBlocker scriptBlocker;
bool hasInProcessCallbacks = false;
clone = sourceDocument->CreateStaticClone(ourDocShell, cv,
&hasInProcessCallbacks);
if (NS_WARN_IF(!clone)) {
return IPC_OK();
}
}
// Since the clone document is not parsed-created, we need to initialize
// layout manually. This is usually done in ReflowPrintObject for non-remote
// documents.
if (RefPtr<PresShell> ps = clone->GetPresShell()) {
if (!ps->DidInitialize()) {
nsresult rv = ps->Initialize();
Unused << NS_WARN_IF(NS_FAILED(rv));
}
}
return IPC_OK();
}
void BrowserChild::DoFakeShow(const ParentShowInfo& aParentShowInfo) {
OwnerShowInfo ownerInfo{ScreenIntSize(), ScrollbarPreference::Auto,
mParentIsActive, nsSizeMode_Normal};
RecvShow(aParentShowInfo, ownerInfo);
mDidFakeShow = true;
}
void BrowserChild::ApplyParentShowInfo(const ParentShowInfo& aInfo) {
// Even if we already set real show info, the dpi / rounding & scale may still
// be invalid (if BrowserParent wasn't able to get widget it would just send
// 0). So better to always set up-to-date values here.
if (aInfo.dpi() > 0) {
mPuppetWidget->UpdateBackingScaleCache(aInfo.dpi(), aInfo.widgetRounding(),
aInfo.defaultScale());
}
if (mDidSetRealShowInfo) {
return;
}
if (!aInfo.fakeShowInfo()) {
// Once we've got one ShowInfo from parent, no need to update the values
// anymore.
mDidSetRealShowInfo = true;
}
mIsTransparent = aInfo.isTransparent();
}
mozilla::ipc::IPCResult BrowserChild::RecvShow(
const ParentShowInfo& aParentInfo, const OwnerShowInfo& aOwnerInfo) {
bool res = true;
mPuppetWidget->SetSizeMode(aOwnerInfo.sizeMode());
if (!mDidFakeShow) {
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
if (!baseWindow) {
NS_ERROR("WebNavigation() doesn't QI to nsIBaseWindow");
return IPC_FAIL_NO_REASON(this);
}
baseWindow->SetVisibility(true);
res = InitBrowserChildMessageManager();
}
ApplyParentShowInfo(aParentInfo);
RecvParentActivated(aOwnerInfo.parentWindowIsActive());
if (!mIsTopLevel) {
RecvScrollbarPreferenceChanged(aOwnerInfo.scrollbarPreference());
}
if (!res) {
return IPC_FAIL_NO_REASON(this);
}
UpdateVisibility();
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvInitRendering(
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
const layers::LayersId& aLayersId,
const CompositorOptions& aCompositorOptions, const bool& aLayersConnected) {
mLayersConnected = Some(aLayersConnected);
InitRenderingState(aTextureFactoryIdentifier, aLayersId, aCompositorOptions);
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvScrollbarPreferenceChanged(
ScrollbarPreference aPreference) {
MOZ_ASSERT(!mIsTopLevel,
"Scrollbar visibility should be derived from chrome flags for "
"top-level windows");
if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
nsDocShell::Cast(docShell)->SetScrollbarPreference(aPreference);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvCompositorOptionsChanged(
const CompositorOptions& aNewOptions) {
MOZ_ASSERT(mCompositorOptions);
// The only compositor option we currently support changing is APZ
// enablement. Even that is only partially supported for now:
// * Going from APZ to non-APZ is fine - we just flip the stored flag.
// Note that we keep the actors (mApzcTreeManager, and the APZChild
// created in InitAPZState()) around (read on for why).
// * Going from non-APZ to APZ is only supported if we were using
// APZ initially (at InitRendering() time) and we are transitioning
// back. In this case, we just reuse the actors which we kept around.
// Fully supporting a non-APZ to APZ transition (i.e. even in cases
// where we initialized as non-APZ) would require setting up the actors
// here. (In that case, we would also have the options of destroying
// the actors in the APZ --> non-APZ case, and always re-creating them
// during a non-APZ --> APZ transition).
mCompositorOptions->SetUseAPZ(aNewOptions.UseAPZ());
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvUpdateDimensions(
const DimensionInfo& aDimensionInfo) {
if (mLayersConnected.isNothing()) {
return IPC_OK();
}
mUnscaledOuterRect = aDimensionInfo.rect();
mClientOffset = aDimensionInfo.clientOffset();
mChromeOffset = aDimensionInfo.chromeOffset();
mOrientation = aDimensionInfo.orientation();
SetUnscaledInnerSize(aDimensionInfo.size());
if (!mHasValidInnerSize && aDimensionInfo.size().width != 0 &&
aDimensionInfo.size().height != 0) {
mHasValidInnerSize = true;
}
ScreenIntSize screenSize = GetInnerSize();
ScreenIntRect screenRect = GetOuterRect();
// Make sure to set the size on the document viewer first. The
// MobileViewportManager needs the content viewer size to be updated before
// the reflow, otherwise it gets a stale size when it computes a new CSS
// viewport.
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
nsIBaseWindow::eRepaint);
mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeOffset.x,
screenRect.y + mClientOffset.y + mChromeOffset.y,
screenSize.width, screenSize.height, true);
RecvSafeAreaInsetsChanged(mPuppetWidget->GetSafeAreaInsets());
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvSizeModeChanged(
const nsSizeMode& aSizeMode) {
mPuppetWidget->SetSizeMode(aSizeMode);
if (!mPuppetWidget->IsVisible()) {
return IPC_OK();
}
nsCOMPtr<Document> document(GetTopLevelDocument());
if (!document) {
return IPC_OK();
}
nsPresContext* presContext = document->GetPresContext();
if (presContext) {
presContext->SizeModeChanged(aSizeMode);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvChildToParentMatrix(
const Maybe<gfx::Matrix4x4>& aMatrix,
const ScreenRect& aTopLevelViewportVisibleRectInBrowserCoords) {
mChildToParentConversionMatrix =
LayoutDeviceToLayoutDeviceMatrix4x4::FromUnknownMatrix(aMatrix);
mTopLevelViewportVisibleRectInBrowserCoords =
aTopLevelViewportVisibleRectInBrowserCoords;
// Trigger an intersection observation update since ancestor viewports
// changed.
if (RefPtr<Document> toplevelDoc = GetTopLevelDocument()) {
if (nsPresContext* pc = toplevelDoc->GetPresContext()) {
pc->RefreshDriver()->EnsureIntersectionObservationsUpdateHappens();
}
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvSetIsUnderHiddenEmbedderElement(
const bool& aIsUnderHiddenEmbedderElement) {
if (RefPtr<PresShell> presShell = GetTopLevelPresShell()) {
presShell->SetIsUnderHiddenEmbedderElement(aIsUnderHiddenEmbedderElement);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvDynamicToolbarMaxHeightChanged(
const ScreenIntCoord& aHeight) {
#if defined(MOZ_WIDGET_ANDROID)
mDynamicToolbarMaxHeight = aHeight;
RefPtr<Document> document = GetTopLevelDocument();
if (!document) {
return IPC_OK();
}
if (RefPtr<nsPresContext> presContext = document->GetPresContext()) {
presContext->SetDynamicToolbarMaxHeight(aHeight);
}
#endif
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvDynamicToolbarOffsetChanged(
const ScreenIntCoord& aOffset) {
#if defined(MOZ_WIDGET_ANDROID)
RefPtr<Document> document = GetTopLevelDocument();
if (!document) {
return IPC_OK();
}
if (nsPresContext* presContext = document->GetPresContext()) {
presContext->UpdateDynamicToolbarOffset(aOffset);
}
#endif
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvSuppressDisplayport(
const bool& aEnabled) {
if (RefPtr<PresShell> presShell = GetTopLevelPresShell()) {
presShell->SuppressDisplayport(aEnabled);
}
return IPC_OK();
}
void BrowserChild::HandleDoubleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid) {
MOZ_LOG(
sApzChildLog, LogLevel::Debug,
("Handling double tap at %s with %p %p\n", Stringify(aPoint).c_str(),
mBrowserChildMessageManager ? mBrowserChildMessageManager->GetWrapper()
: nullptr,
mBrowserChildMessageManager.get()));
if (!mBrowserChildMessageManager) {
return;
}
// Note: there is nothing to do with the modifiers here, as we are not
// synthesizing any sort of mouse event.
RefPtr<Document> document = GetTopLevelDocument();
CSSRect zoomToRect = CalculateRectToZoomTo(document, aPoint);
// The double-tap can be dispatched by any scroll frame (so |aGuid| could be
// the guid of any scroll frame), but the zoom-to-rect operation must be
// performed by the root content scroll frame, so query its identifiers
// for the SendZoomToRect() call rather than using the ones from |aGuid|.
uint32_t presShellId;
ViewID viewId;
if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
document->GetDocumentElement(), &presShellId, &viewId) &&
mApzcTreeManager) {
ScrollableLayerGuid guid(mLayersId, presShellId, viewId);
mApzcTreeManager->ZoomToRect(guid, zoomToRect, DEFAULT_BEHAVIOR);
}
}
mozilla::ipc::IPCResult BrowserChild::RecvHandleTap(
const GeckoContentController::TapType& aType,
const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) {
// IPDL doesn't hold a strong reference to protocols as they're not required
// to be refcounted. This function can run script, which may trigger a nested
// event loop, which may release this, so we hold a strong reference here.
RefPtr<BrowserChild> kungFuDeathGrip(this);
RefPtr<PresShell> presShell = GetTopLevelPresShell();
if (!presShell) {
return IPC_OK();
}
if (!presShell->GetPresContext()) {
return IPC_OK();
}
CSSToLayoutDeviceScale scale(
presShell->GetPresContext()->CSSToDevPixelScale());
CSSPoint point = aPoint / scale;
// Stash the guid in InputAPZContext so that when the visual-to-layout
// transform is applied to the event's coordinates, we use the right transform
// based on the scroll frame being targeted.
// The other values don't really matter.
InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel);
switch (aType) {
case GeckoContentController::TapType::eSingleTap:
if (mBrowserChildMessageManager) {
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1);
}
break;
case GeckoContentController::TapType::eDoubleTap:
HandleDoubleTap(point, aModifiers, aGuid);
break;
case GeckoContentController::TapType::eSecondTap:
if (mBrowserChildMessageManager) {
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2);
}
break;
case GeckoContentController::TapType::eLongTap:
if (mBrowserChildMessageManager) {
RefPtr<APZEventState> eventState(mAPZEventState);
eventState->ProcessLongTap(presShell, point, scale, aModifiers,
aInputBlockId);
}
break;
case GeckoContentController::TapType::eLongTapUp:
if (mBrowserChildMessageManager) {
RefPtr<APZEventState> eventState(mAPZEventState);
eventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
}
break;
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityHandleTap(
const GeckoContentController::TapType& aType,
const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) {
// IPDL doesn't hold a strong reference to protocols as they're not required
// to be refcounted. This function can run script, which may trigger a nested
// event loop, which may release this, so we hold a strong reference here.
RefPtr<BrowserChild> kungFuDeathGrip(this);
return RecvHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
}
bool BrowserChild::NotifyAPZStateChange(
const ViewID& aViewId,
const layers::GeckoContentController::APZStateChange& aChange,
const int& aArg) {
mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg);
if (aChange ==
layers::GeckoContentController::APZStateChange::eTransformEnd) {
// This is used by tests to determine when the APZ is done doing whatever
// it's doing. XXX generify this as needed when writing additional tests.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr);
}
return true;
}
void BrowserChild::StartScrollbarDrag(
const layers::AsyncDragMetrics& aDragMetrics) {
ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId,
aDragMetrics.mViewId);
if (mApzcTreeManager) {
mApzcTreeManager->StartScrollbarDrag(guid, aDragMetrics);
}
}
void BrowserChild::ZoomToRect(const uint32_t& aPresShellId,
const ScrollableLayerGuid::ViewID& aViewId,
const CSSRect& aRect, const uint32_t& aFlags) {
ScrollableLayerGuid guid(mLayersId, aPresShellId, aViewId);
if (mApzcTreeManager) {
mApzcTreeManager->ZoomToRect(guid, aRect, aFlags);
}
}
mozilla::ipc::IPCResult BrowserChild::RecvActivate() {
MOZ_ASSERT(mWebBrowser);
mWebBrowser->FocusActivate();
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvDeactivate() {
MOZ_ASSERT(mWebBrowser);
mWebBrowser->FocusDeactivate();
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvParentActivated(
const bool& aActivated) {
mParentIsActive = aActivated;
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE(fm, IPC_OK());
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
fm->ParentActivated(window, aActivated);
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvSetKeyboardIndicators(
const UIStateChangeType& aShowFocusRings) {
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
NS_ENSURE_TRUE(window, IPC_OK());
window->SetKeyboardIndicators(aShowFocusRings);
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvStopIMEStateManagement() {
IMEStateManager::StopIMEStateManagement();
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvMouseEvent(
const nsString& aType, const float& aX, const float& aY,
const int32_t& aButton, const int32_t& aClickCount,
const int32_t& aModifiers) {
// IPDL doesn't hold a strong reference to protocols as they're not required
// to be refcounted. This function can run script, which may trigger a nested
// event loop, which may release this, so we hold a strong reference here.
RefPtr<BrowserChild> kungFuDeathGrip(this);
RefPtr<PresShell> presShell = GetTopLevelPresShell();
APZCCallbackHelper::DispatchMouseEvent(presShell, aType, CSSPoint(aX, aY),
aButton, aClickCount, aModifiers,
MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
0 /* Use the default value here. */);
return IPC_OK();
}
void BrowserChild::ProcessPendingCoalescedMouseDataAndDispatchEvents() {
if (!mCoalesceMouseMoveEvents || !mCoalescedMouseEventFlusher) {
// We don't enable mouse coalescing or we are destroying BrowserChild.
return;
}
// We may reentry the event loop and push more data to
// mToBeDispatchedMouseData while dispatching an event.
// We may have some pending coalesced data while dispatch an event and reentry
// the event loop. In that case we don't have chance to consume the remainding
// pending data until we get new mouse events. Get some helps from
// mCoalescedMouseEventFlusher to trigger it.
mCoalescedMouseEventFlusher->StartObserver();
while (mToBeDispatchedMouseData.GetSize() > 0) {
UniquePtr<CoalescedMouseData> data(
static_cast<CoalescedMouseData*>(mToBeDispatchedMouseData.PopFront()));
UniquePtr<WidgetMouseEvent> event = data->TakeCoalescedEvent();
if (event) {
// Dispatch the pending events. Using HandleRealMouseButtonEvent
// to bypass the coalesce handling in RecvRealMouseMoveEvent. Can't use
// RecvRealMouseButtonEvent because we may also put some mouse events
// other than mousemove.
HandleRealMouseButtonEvent(*event, data->GetScrollableLayerGuid(),
data->GetInputBlockId());
}
}
// mCoalescedMouseEventFlusher may be destroyed when reentrying the event
// loop.
if (mCoalescedMouseEventFlusher) {
mCoalescedMouseEventFlusher->RemoveObserver();
}
}
LayoutDeviceToLayoutDeviceMatrix4x4
BrowserChild::GetChildToParentConversionMatrix() const {
if (mChildToParentConversionMatrix) {
return *mChildToParentConversionMatrix;
}
LayoutDevicePoint offset(GetChromeOffset());
return LayoutDeviceToLayoutDeviceMatrix4x4::Translation(offset);
}
ScreenRect BrowserChild::GetTopLevelViewportVisibleRectInBrowserCoords() const {
return mTopLevelViewportVisibleRectInBrowserCoords;
}
void BrowserChild::FlushAllCoalescedMouseData() {
MOZ_ASSERT(mCoalesceMouseMoveEvents);
// Move all entries from mCoalescedMouseData to mToBeDispatchedMouseData.
for (auto iter = mCoalescedMouseData.Iter(); !iter.Done(); iter.Next()) {
CoalescedMouseData* data = iter.UserData();
if (!data || data->IsEmpty()) {
continue;
}
UniquePtr<CoalescedMouseData> dispatchData =
MakeUnique<CoalescedMouseData>();
dispatchData->RetrieveDataFrom(*data);
mToBeDispatchedMouseData.Push(dispatchData.release());
}
mCoalescedMouseData.Clear();
}
mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEvent(
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
if (mCoalesceMouseMoveEvents && mCoalescedMouseEventFlusher) {
CoalescedMouseData* data =
mCoalescedMouseData.LookupOrAdd(aEvent.pointerId);
MOZ_ASSERT(data);
if (data->CanCoalesce(aEvent, aGuid, aInputBlockId)) {
data->Coalesce(aEvent, aGuid, aInputBlockId);
mCoalescedMouseEventFlusher->StartObserver();
return IPC_OK();
}
// Can't coalesce current mousemove event. Put the coalesced mousemove data
// with the same pointer id to mToBeDispatchedMouseData, coalesce the
// current one, and process all pending data in mToBeDispatchedMouseData.
UniquePtr<CoalescedMouseData> dispatchData =
MakeUnique<CoalescedMouseData>();
dispatchData->RetrieveDataFrom(*data);
mToBeDispatchedMouseData.Push(dispatchData.release());
// Put new data to replace the old one in the hash table.
CoalescedMouseData* newData = new CoalescedMouseData();
mCoalescedMouseData.Put(aEvent.pointerId, newData);
newData->Coalesce(aEvent, aGuid, aInputBlockId);
// Dispatch all pending mouse events.
ProcessPendingCoalescedMouseDataAndDispatchEvents();
mCoalescedMouseEventFlusher->StartObserver();
} else if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseMoveEvent(
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId);
}
mozilla::ipc::IPCResult BrowserChild::RecvSynthMouseMoveEvent(
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySynthMouseMoveEvent(
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
return RecvSynthMouseMoveEvent(aEvent, aGuid, aInputBlockId);
}
mozilla::ipc::IPCResult BrowserChild::RecvRealMouseButtonEvent(
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
if (mCoalesceMouseMoveEvents && mCoalescedMouseEventFlusher &&
aEvent.mMessage != eMouseMove) {
// When receiving a mouse event other than mousemove, we have to dispatch
// all coalesced events before it. However, we can't dispatch all pending
// coalesced events directly because we may reentry the event loop while
// dispatching. To make sure we won't dispatch disorder events, we move all
// coalesced mousemove events and current event to a deque to dispatch them.
// When reentrying the event loop and dispatching more events, we put new
// events in the end of the nsQueue and dispatch events from the beginning.
FlushAllCoalescedMouseData();
UniquePtr<CoalescedMouseData> dispatchData =
MakeUnique<CoalescedMouseData>();
dispatchData->Coalesce(aEvent, aGuid, aInputBlockId);
mToBeDispatchedMouseData.Push(dispatchData.release());
ProcessPendingCoalescedMouseDataAndDispatchEvents();
return IPC_OK();
}
HandleRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
return IPC_OK();
}
void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
WidgetMouseEvent localEvent(aEvent);
localEvent.mWidget = mPuppetWidget;
// We need one InputAPZContext here to propagate |aGuid| to places in
// SendSetTargetAPZCNotification() which apply the visual-to-layout transform,
// and another below to propagate the |postLayerization| flag (whose value
// we don't know until SendSetTargetAPZCNotification() returns) into
// the event dispatch code.
InputAPZContext context1(aGuid, aInputBlockId, nsEventStatus_eSentinel);
// Mouse events like eMouseEnterIntoWidget, that are created in the parent
// process EventStateManager code, have an input block id which they get from
// the InputAPZContext in the parent process stack. However, they did not
// actually go through the APZ code and so their mHandledByAPZ flag is false.
// Since thos events didn't go through APZ, we don't need to send
// notifications for them.
UniquePtr<DisplayportSetListener> postLayerization;
if (aInputBlockId && localEvent.mFlags.mHandledByAPZ) {
nsCOMPtr<Document> document(GetTopLevelDocument());
postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
mPuppetWidget, document, localEvent, aGuid.mLayersId, aInputBlockId);
}
InputAPZContext context2(aGuid, aInputBlockId, nsEventStatus_eSentinel,
postLayerization != nullptr);
DispatchWidgetEventViaAPZ(localEvent);
if (aInputBlockId && localEvent.mFlags.mHandledByAPZ) {
mAPZEventState->ProcessMouseEvent(localEvent, aInputBlockId);
}
// Do this after the DispatchWidgetEventViaAPZ call above, so that if the
// mouse event triggered a post-refresh AsyncDragMetrics message to be sent
// to APZ (from scrollbar dragging in nsSliderFrame), then that will reach
// APZ before the SetTargetAPZC message. This ensures the drag input block
// gets the drag metrics before handling the input events.
if (postLayerization && postLayerization->Register()) {
Unused << postLayerization.release();
}
}
mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseButtonEvent(
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
}
// In case handling repeated mouse wheel takes much time, we skip firing current
// wheel event if it may be coalesced to the next one.
bool BrowserChild::MaybeCoalesceWheelEvent(const WidgetWheelEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
bool* aIsNextWheelEvent) {
MOZ_ASSERT(aIsNextWheelEvent);
if (aEvent.mMessage == eWheel) {
GetIPCChannel()->PeekMessages(
[aIsNextWheelEvent](const IPC::Message& aMsg) -> bool {
if (aMsg.type() == mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID) {
*aIsNextWheelEvent = true;
}
return false; // Stop peeking.
});
// We only coalesce the current event when
// 1. It's eWheel (we don't coalesce eOperationStart and eWheelOperationEnd)
// 2. It's not the first wheel event.
// 3. It's not the last wheel event.
// 4. It's dispatched before the last wheel event was processed +
// the processing time of the last event.
// This way pages spending lots of time in wheel listeners get wheel
// events coalesced more aggressively.
// 5. It has same attributes as the coalesced wheel event which is not yet
// fired.
if (!mLastWheelProcessedTimeFromParent.IsNull() && *aIsNextWheelEvent &&
aEvent.mTimeStamp < (mLastWheelProcessedTimeFromParent +
mLastWheelProcessingDuration) &&
(mCoalescedWheelData.IsEmpty() ||
mCoalescedWheelData.CanCoalesce(aEvent, aGuid, aInputBlockId))) {
mCoalescedWheelData.Coalesce(aEvent, aGuid, aInputBlockId);
return true;
}
}
return false;
}
nsEventStatus BrowserChild::DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent) {
aEvent.ResetWaitingReplyFromRemoteProcessState();
return APZCCallbackHelper::DispatchWidgetEvent(aEvent);
}
void BrowserChild::MaybeDispatchCoalescedWheelEvent() {
if (mCoalescedWheelData.IsEmpty()) {
return;
}
UniquePtr<WidgetWheelEvent> wheelEvent =
mCoalescedWheelData.TakeCoalescedEvent();
MOZ_ASSERT(wheelEvent);
DispatchWheelEvent(*wheelEvent, mCoalescedWheelData.GetScrollableLayerGuid(),
mCoalescedWheelData.GetInputBlockId());
}
void BrowserChild::DispatchWheelEvent(const WidgetWheelEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
WidgetWheelEvent localEvent(aEvent);
if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
nsCOMPtr<Document> document(GetTopLevelDocument());
UniquePtr<DisplayportSetListener> postLayerization =
APZCCallbackHelper::SendSetTargetAPZCNotification(
mPuppetWidget, document, aEvent, aGuid.mLayersId, aInputBlockId);
if (postLayerization && postLayerization->Register()) {
Unused << postLayerization.release();
}
}
localEvent.mWidget = mPuppetWidget;
// Stash the guid in InputAPZContext so that when the visual-to-layout
// transform is applied to the event's coordinates, we use the right transform
// based on the scroll frame being targeted.
// The other values don't really matter.
InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel);
DispatchWidgetEventViaAPZ(localEvent);
if (localEvent.mCanTriggerSwipe) {
SendRespondStartSwipeEvent(aInputBlockId, localEvent.TriggersSwipe());
}
if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
mAPZEventState->ProcessWheelEvent(localEvent, aInputBlockId);
}
}
mozilla::ipc::IPCResult BrowserChild::RecvMouseWheelEvent(
const WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
bool isNextWheelEvent = false;
if (MaybeCoalesceWheelEvent(aEvent, aGuid, aInputBlockId,
&isNextWheelEvent)) {
return IPC_OK();
}
if (isNextWheelEvent) {
// Update mLastWheelProcessedTimeFromParent so that we can compare the end
// time of the current event with the dispatched time of the next event.
mLastWheelProcessedTimeFromParent = aEvent.mTimeStamp;
mozilla::TimeStamp beforeDispatchingTime = TimeStamp::Now();
MaybeDispatchCoalescedWheelEvent();
DispatchWheelEvent(aEvent, aGuid, aInputBlockId);
mLastWheelProcessingDuration = (TimeStamp::Now() - beforeDispatchingTime);
mLastWheelProcessedTimeFromParent += mLastWheelProcessingDuration;
} else {
// This is the last wheel event. Set mLastWheelProcessedTimeFromParent to
// null moment to avoid coalesce the next incoming wheel event.
mLastWheelProcessedTimeFromParent = TimeStamp();
MaybeDispatchCoalescedWheelEvent();
DispatchWheelEvent(aEvent, aGuid, aInputBlockId);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityMouseWheelEvent(
const WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) {
return RecvMouseWheelEvent(aEvent, aGuid, aInputBlockId);
}
mozilla::ipc::IPCResult BrowserChild::RecvRealTouchEvent(
const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
MOZ_LOG(sApzChildLog, LogLevel::Debug,
("Receiving touch event of type %d\n", aEvent.mMessage));
WidgetTouchEvent localEvent(aEvent);
localEvent.mWidget = mPuppetWidget;
// Stash the guid in InputAPZContext so that when the visual-to-layout
// transform is applied to the event's coordinates, we use the right transform
// based on the scroll frame being targeted.
// The other values don't really matter.
InputAPZContext context(aGuid, aInputBlockId, aApzResponse);
nsTArray<TouchBehaviorFlags> allowedTouchBehaviors;
if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) {
nsCOMPtr<Document> document = GetTopLevelDocument();
if (StaticPrefs::layout_css_touch_action_enabled()) {
allowedTouchBehaviors =
APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
mPuppetWidget, document, localEvent, aInputBlockId,
mSetAllowedTouchBehaviorCallback);
}
UniquePtr<DisplayportSetListener> postLayerization =
APZCCallbackHelper::SendSetTargetAPZCNotification(
mPuppetWidget, document, localEvent, aGuid.mLayersId,
aInputBlockId);
if (postLayerization && postLayerization->Register()) {
Unused << postLayerization.release();
}
}
// Dispatch event to content (potentially a long-running operation)
nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent);
if (!AsyncPanZoomEnabled()) {
// We shouldn't have any e10s platforms that have touch events enabled
// without APZ.
MOZ_ASSERT(false);
return IPC_OK();
}
mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId,
aApzResponse, status,
std::move(allowedTouchBehaviors));
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchEvent(
const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
}
mozilla::ipc::IPCResult BrowserChild::RecvRealTouchMoveEvent(
const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
if (!RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse)) {