diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/ipc/BrowserChild.cpp | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/ipc/BrowserChild.cpp')
-rw-r--r-- | dom/ipc/BrowserChild.cpp | 4020 |
1 files changed, 4020 insertions, 0 deletions
diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp new file mode 100644 index 0000000000..9438f34689 --- /dev/null +++ b/dom/ipc/BrowserChild.cpp @@ -0,0 +1,4020 @@ +/* -*- 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 "BackgroundChild.h" +#include "BrowserParent.h" +#include "ClientLayerManager.h" +#include "ContentChild.h" +#include "DocumentInlines.h" +#include "EventStateManager.h" +#include "FrameLayerBuilder.h" +#include "GeckoProfiler.h" +#include "Layers.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/HoldDropJSObjects.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/ToString.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/PointerEventHandler.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/PBackgroundChild.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" +#include "nsIXULRuntime.h" + +#ifdef XP_WIN +# include "mozilla/plugins/PluginWidgetChild.h" +#endif + +#ifdef MOZ_WAYLAND +# include "nsAppRunner.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 try that this runnable runs after everything that could + // possibly touch this tab, we send it through the event queue twice. + 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 = 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), + mTriedBrowserInit(false), + mOrientation(hal::eScreenOrientation_PortraitPrimary), + mVsyncChild(nullptr), + mIgnoreKeyPressEvent(false), + mHasValidInnerSize(false), + mDestroyed(false), + mDynamicToolbarMaxHeight(0), + mUniqueId(aTabId), + mIsTopLevel(aIsTopLevel), + mHasSiblings(false), + mIsTransparent(false), + mIPCOpen(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), + 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"); + return IPC_OK(); + } + 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, + 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); + + 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(); + MOZ_ASSERT_IF(!IsTopLevel(), mChromeOffset == LayoutDeviceIntPoint()); + + 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", ToString(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(uint64_t aActionId) { + MOZ_ASSERT(mWebBrowser); + mWebBrowser->FocusActivate(aActionId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvDeactivate(uint64_t aActionId) { + MOZ_ASSERT(mWebBrowser); + mWebBrowser->FocusDeactivate(aActionId); + 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::RecvRealMouseMoveEventForTests( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseMoveEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult +BrowserChild::RecvNormalPriorityRealMouseMoveEventForTests( + 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)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchMoveEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) { + return RecvRealTouchMoveEvent(aEvent, aGuid, aInputBlockId, aApzResponse); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealDragEvent( + const WidgetDragEvent& aEvent, const uint32_t& aDragAction, + const uint32_t& aDropEffect, nsIPrincipal* aPrincipal, + nsIContentSecurityPolicy* aCsp) { + WidgetDragEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); + if (dragSession) { + dragSession->SetDragAction(aDragAction); + dragSession->SetTriggeringPrincipal(aPrincipal); + dragSession->SetCsp(aCsp); + RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer(); + if (initialDataTransfer) { + initialDataTransfer->SetDropEffectInt(aDropEffect); + } + } + + if (aEvent.mMessage == eDrop) { + bool canDrop = true; + if (!dragSession || NS_FAILED(dragSession->GetCanDrop(&canDrop)) || + !canDrop) { + localEvent.mMessage = eDragExit; + } + } else if (aEvent.mMessage == eDragOver) { + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService) { + // This will dispatch 'drag' event at the source if the + // drag transaction started in this process. + dragService->FireDragEventAtSource(eDrag, aEvent.mModifiers); + } + } + + DispatchWidgetEventViaAPZ(localEvent); + return IPC_OK(); +} + +void BrowserChild::RequestEditCommands(nsIWidget::NativeKeyBindingsType aType, + const WidgetKeyboardEvent& aEvent, + nsTArray<CommandInt>& aCommands) { + MOZ_ASSERT(aCommands.IsEmpty()); + + if (NS_WARN_IF(aEvent.IsEditCommandsInitialized(aType))) { + aCommands = aEvent.EditCommandsConstRef(aType).Clone(); + return; + } + + switch (aType) { + case nsIWidget::NativeKeyBindingsForSingleLineEditor: + case nsIWidget::NativeKeyBindingsForMultiLineEditor: + case nsIWidget::NativeKeyBindingsForRichTextEditor: + break; + default: + MOZ_ASSERT_UNREACHABLE("Invalid native key bindings type"); + } + + // Don't send aEvent to the parent process directly because it'll be marked + // as posted to remote process. + WidgetKeyboardEvent localEvent(aEvent); + SendRequestNativeKeyBindings(aType, localEvent, &aCommands); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNativeSynthesisResponse( + const uint64_t& aObserverId, const nsCString& aResponse) { + mozilla::widget::AutoObserverNotifier::NotifySavedObserver(aObserverId, + aResponse.get()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvFlushTabState( + const uint32_t& aFlushId, const bool& aIsFinal) { + UpdateSessionStore(aFlushId, aIsFinal); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateEpoch(const uint32_t& aEpoch) { + mSessionStoreListener->SetEpoch(aEpoch); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory( + const bool& aImmediately) { + if (mSessionStoreListener) { + mSessionStoreListener->UpdateSHistoryChanges(aImmediately); + } + return IPC_OK(); +} + +// In case handling repeated keys takes much time, we skip firing new ones. +bool BrowserChild::SkipRepeatedKeyEvent(const WidgetKeyboardEvent& aEvent) { + if (mRepeatedKeyEventTime.IsNull() || !aEvent.CanSkipInRemoteProcess() || + (aEvent.mMessage != eKeyDown && aEvent.mMessage != eKeyPress)) { + mRepeatedKeyEventTime = TimeStamp(); + mSkipKeyPress = false; + return false; + } + + if ((aEvent.mMessage == eKeyDown && + (mRepeatedKeyEventTime > aEvent.mTimeStamp)) || + (mSkipKeyPress && (aEvent.mMessage == eKeyPress))) { + // If we skip a keydown event, also the following keypress events should be + // skipped. + mSkipKeyPress |= aEvent.mMessage == eKeyDown; + return true; + } + + if (aEvent.mMessage == eKeyDown) { + // If keydown wasn't skipped, nor should the possible following keypress. + mRepeatedKeyEventTime = TimeStamp(); + mSkipKeyPress = false; + } + return false; +} + +void BrowserChild::UpdateRepeatedKeyEventEndTime( + const WidgetKeyboardEvent& aEvent) { + if (aEvent.mIsRepeat && + (aEvent.mMessage == eKeyDown || aEvent.mMessage == eKeyPress)) { + mRepeatedKeyEventTime = TimeStamp::Now(); + } +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealKeyEvent( + const WidgetKeyboardEvent& aEvent) { + if (SkipRepeatedKeyEvent(aEvent)) { + return IPC_OK(); + } + + MOZ_ASSERT( + aEvent.mMessage != eKeyPress || aEvent.AreAllEditCommandsInitialized(), + "eKeyPress event should have native key binding information"); + + // If content code called preventDefault() on a keydown event, then we don't + // want to process any following keypress events. + if (aEvent.mMessage == eKeyPress && mIgnoreKeyPressEvent) { + return IPC_OK(); + } + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + localEvent.mUniqueId = aEvent.mUniqueId; + nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent); + + // Update the end time of the possible repeated event so that we can skip + // some incoming events in case event handling took long time. + UpdateRepeatedKeyEventEndTime(localEvent); + + if (aEvent.mMessage == eKeyDown) { + mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault; + } + + if (localEvent.mFlags.mIsSuppressedOrDelayed) { + localEvent.PreventDefault(); + } + + // If a response is desired from the content process, resend the key event. + if (aEvent.WantReplyFromContentProcess()) { + // If the event's default isn't prevented but the status is no default, + // That means that the event was consumed by EventStateManager or something + // which is not a usual event handler. In such case, prevent its default + // as a default handler. For example, when an eKeyPress event matches + // with a content accesskey, and it's executed, peventDefault() of the + // event won't be called but the status is set to "no default". Then, + // the event shouldn't be handled by nsMenuBarListener in the main process. + if (!localEvent.DefaultPrevented() && + status == nsEventStatus_eConsumeNoDefault) { + localEvent.PreventDefault(); + } + // This is an ugly hack, mNoRemoteProcessDispatch is set to true when the + // event's PreventDefault() or StopScrollProcessForwarding() is called. + // And then, it'll be checked by ParamTraits<mozilla::WidgetEvent>::Write() + // whether the event is being sent to remote process unexpectedly. + // However, unfortunately, it cannot check the destination. Therefore, + // we need to clear the flag explicitly here because ParamTraits should + // keep checking the flag for avoiding regression. + localEvent.mFlags.mNoRemoteProcessDispatch = false; + SendReplyKeyEvent(localEvent); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealKeyEvent( + const WidgetKeyboardEvent& aEvent) { + return RecvRealKeyEvent(aEvent); +} + +mozilla::ipc::IPCResult BrowserChild::RecvCompositionEvent( + const WidgetCompositionEvent& aEvent) { + WidgetCompositionEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + DispatchWidgetEventViaAPZ(localEvent); + Unused << SendOnEventNeedingAckHandled(aEvent.mMessage); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityCompositionEvent( + const WidgetCompositionEvent& aEvent) { + return RecvCompositionEvent(aEvent); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSelectionEvent( + const WidgetSelectionEvent& aEvent) { + WidgetSelectionEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + DispatchWidgetEventViaAPZ(localEvent); + Unused << SendOnEventNeedingAckHandled(aEvent.mMessage); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySelectionEvent( + const WidgetSelectionEvent& aEvent) { + return RecvSelectionEvent(aEvent); +} + +mozilla::ipc::IPCResult BrowserChild::RecvPasteTransferable( + const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData, + nsIPrincipal* aRequestingPrincipal, + const nsContentPolicyType& aContentPolicyType) { + nsresult rv; + nsCOMPtr<nsITransferable> trans = + do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, IPC_OK()); + trans->Init(nullptr); + + rv = nsContentUtils::IPCTransferableToTransferable( + aDataTransfer, aIsPrivateData, aRequestingPrincipal, aContentPolicyType, + trans, nullptr, this); + NS_ENSURE_SUCCESS(rv, IPC_OK()); + + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return IPC_OK(); + } + + RefPtr<nsCommandParams> params = new nsCommandParams(); + rv = params->SetISupports("transferable", trans); + NS_ENSURE_SUCCESS(rv, IPC_OK()); + + ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params); + return IPC_OK(); +} + +#ifdef ACCESSIBILITY +a11y::PDocAccessibleChild* BrowserChild::AllocPDocAccessibleChild( + PDocAccessibleChild*, const uint64_t&, const uint32_t&, + const IAccessibleHolder&) { + MOZ_ASSERT(false, "should never call this!"); + return nullptr; +} + +bool BrowserChild::DeallocPDocAccessibleChild( + a11y::PDocAccessibleChild* aChild) { + delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild); + return true; +} +#endif + +PColorPickerChild* BrowserChild::AllocPColorPickerChild(const nsString&, + const nsString&) { + MOZ_CRASH("unused"); + return nullptr; +} + +bool BrowserChild::DeallocPColorPickerChild(PColorPickerChild* aColorPicker) { + nsColorPickerProxy* picker = static_cast<nsColorPickerProxy*>(aColorPicker); + NS_RELEASE(picker); + return true; +} + +PFilePickerChild* BrowserChild::AllocPFilePickerChild(const nsString&, + const int16_t&) { + MOZ_CRASH("unused"); + return nullptr; +} + +bool BrowserChild::DeallocPFilePickerChild(PFilePickerChild* actor) { + nsFilePickerProxy* filePicker = static_cast<nsFilePickerProxy*>(actor); + NS_RELEASE(filePicker); + return true; +} + +PVsyncChild* BrowserChild::AllocPVsyncChild() { + RefPtr<dom::VsyncChild> actor = new VsyncChild(); + // There still has one ref-count after return, and it will be released in + // DeallocPVsyncChild(). + return actor.forget().take(); +} + +bool BrowserChild::DeallocPVsyncChild(PVsyncChild* aActor) { + MOZ_ASSERT(aActor); + + // This actor already has one ref-count. Please check AllocPVsyncChild(). + RefPtr<VsyncChild> actor = dont_AddRef(static_cast<VsyncChild*>(aActor)); + return true; +} + +RefPtr<VsyncChild> BrowserChild::GetVsyncChild() { + // Initializing mVsyncChild here turns on per-BrowserChild Vsync for a + // given platform. Note: this only makes sense if nsWindow returns a + // window-specific VsyncSource. +#if defined(MOZ_WAYLAND) + if (!IsWaylandDisabled() && !mVsyncChild) { + PVsyncChild* actor = SendPVsyncConstructor(); + mVsyncChild = static_cast<VsyncChild*>(actor); + } +#endif + return mVsyncChild; +} + +mozilla::ipc::IPCResult BrowserChild::RecvActivateFrameEvent( + const nsString& aType, const bool& capture) { + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, IPC_OK()); + nsCOMPtr<EventTarget> chromeHandler = window->GetChromeEventHandler(); + NS_ENSURE_TRUE(chromeHandler, IPC_OK()); + RefPtr<ContentListener> listener = new ContentListener(this); + chromeHandler->AddEventListener(aType, listener, capture); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript( + const nsString& aURL, const bool& aRunInGlobalScope) { + if (!InitBrowserChildMessageManager()) + // This can happen if we're half-destroyed. It's not a fatal + // error. + return IPC_OK(); + + JS::Rooted<JSObject*> mm(RootingCx(), + mBrowserChildMessageManager->GetOrCreateWrapper()); + if (!mm) { + // This can happen if we're half-destroyed. It's not a fatal error. + return IPC_OK(); + } + + LoadScriptInternal(mm, aURL, !aRunInGlobalScope); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvAsyncMessage( + const nsString& aMessage, const ClonedMessageData& aData) { + AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserChild::RecvAsyncMessage", + OTHER, aMessage); + MMPrinter::Print("BrowserChild::RecvAsyncMessage", aMessage, aData); + + if (!mBrowserChildMessageManager) { + return IPC_OK(); + } + + RefPtr<nsFrameMessageManager> mm = + mBrowserChildMessageManager->GetMessageManager(); + + // 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(mm); + if (!mm) { + return IPC_OK(); + } + + JS::Rooted<JSObject*> kungFuDeathGrip( + dom::RootingCx(), mBrowserChildMessageManager->GetWrapper()); + StructuredCloneData data; + UnpackClonedMessageDataForChild(aData, data); + mm->ReceiveMessage(static_cast<EventTarget*>(mBrowserChildMessageManager), + nullptr, aMessage, false, &data, nullptr, IgnoreErrors()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSwappedWithOtherRemoteLoader( + const IPCTabContext& aContext) { + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return IPC_OK(); + } + + nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocShell->GetWindow(); + if (NS_WARN_IF(!ourWindow)) { + return IPC_OK(); + } + + RefPtr<nsDocShell> docShell = static_cast<nsDocShell*>(ourDocShell.get()); + + nsCOMPtr<EventTarget> ourEventTarget = nsGlobalWindowOuter::Cast(ourWindow); + + docShell->SetInFrameSwap(true); + + nsContentUtils::FirePageShowEventForFrameLoaderSwap( + ourDocShell, ourEventTarget, false, true); + nsContentUtils::FirePageHideEventForFrameLoaderSwap(ourDocShell, + ourEventTarget, true); + + // Owner content type may have changed, so store the possibly updated context + // and notify others. + MaybeInvalidTabContext maybeContext(aContext); + if (!maybeContext.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the parent process. (%s)", + maybeContext.GetInvalidReason()) + .get()); + MOZ_CRASH("Invalid TabContext received from the parent process."); + } + + if (!UpdateTabContextAfterSwap(maybeContext.GetTabContext())) { + MOZ_CRASH("Update to TabContext after swap was denied."); + } + + // Ignore previous value of mTriedBrowserInit since owner content has changed. + mTriedBrowserInit = true; + + nsContentUtils::FirePageShowEventForFrameLoaderSwap( + ourDocShell, ourEventTarget, true, true); + + docShell->SetInFrameSwap(false); + + // This is needed to get visibility state right in cases when we swapped a + // visible tab (foreground in visible window) with a non-visible tab. + if (RefPtr<Document> doc = docShell->GetDocument()) { + doc->UpdateVisibilityState(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvHandleAccessKey( + const WidgetKeyboardEvent& aEvent, nsTArray<uint32_t>&& aCharCodes) { + nsCOMPtr<Document> document(GetTopLevelDocument()); + RefPtr<nsPresContext> pc = document->GetPresContext(); + if (pc) { + if (!pc->EventStateManager()->HandleAccessKey( + &(const_cast<WidgetKeyboardEvent&>(aEvent)), pc, aCharCodes)) { + // If no accesskey was found, inform the parent so that accesskeys on + // menus can be handled. + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + SendAccessKeyNotHandled(localEvent); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvPrintPreview( + const PrintData& aPrintData, + const mozilla::Maybe<uint64_t>& aSourceOuterWindowID, + PrintPreviewResolver&& aCallback) { +#ifdef NS_PRINTING + // If we didn't succeed in passing off ownership of aCallback, then something + // went wrong. + auto sendCallbackError = MakeScopeExit([&] { + if (aCallback) { + aCallback( + PrintPreviewResultInfo(0, 0, false, false, false)); // signal error + } + }); + + RefPtr<nsGlobalWindowOuter> sourceWindow; + if (aSourceOuterWindowID) { + sourceWindow = + nsGlobalWindowOuter::GetOuterWindowWithId(aSourceOuterWindowID.value()); + if (NS_WARN_IF(!sourceWindow)) { + return IPC_OK(); + } + } else { + nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourWindow)) { + return IPC_OK(); + } + sourceWindow = nsGlobalWindowOuter::Cast(ourWindow); + } + + RefPtr<nsIPrintSettings> printSettings; + nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return IPC_OK(); + } + printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(!printSettings)) { + return IPC_OK(); + } + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + + nsCOMPtr<nsIDocShell> docShellToCloneInto; + if (aSourceOuterWindowID) { + docShellToCloneInto = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!docShellToCloneInto)) { + return IPC_OK(); + } + } + + sourceWindow->Print(printSettings, + /* aListener = */ nullptr, docShellToCloneInto, + nsGlobalWindowOuter::IsPreview::Yes, + nsGlobalWindowOuter::IsForWindowDotPrint::No, + std::move(aCallback), IgnoreErrors()); +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvExitPrintPreview() { +#ifdef NS_PRINTING + nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = + do_GetInterface(ToSupports(WebNavigation())); + if (NS_WARN_IF(!webBrowserPrint)) { + return IPC_OK(); + } + webBrowserPrint->ExitPrintPreview(); +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvPrint(const uint64_t& aOuterWindowID, + const PrintData& aPrintData) { +#ifdef NS_PRINTING + RefPtr<nsGlobalWindowOuter> outerWindow = + nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID); + if (NS_WARN_IF(!outerWindow)) { + return IPC_OK(); + } + + nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return IPC_OK(); + } + + nsCOMPtr<nsIPrintSettings> printSettings; + nsresult rv = + printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return IPC_OK(); + } + + nsCOMPtr<nsIPrintSession> printSession = + do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return IPC_OK(); + } + + printSettings->SetPrintSession(printSession); + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + { + IgnoredErrorResult rv; + outerWindow->Print(printSettings, + /* aListener = */ nullptr, + /* aWindowToCloneInto = */ nullptr, + nsGlobalWindowOuter::IsPreview::No, + nsGlobalWindowOuter::IsForWindowDotPrint::No, + /* aPrintPreviewCallback = */ nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + return IPC_OK(); + } + } +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateNativeWindowHandle( + const uintptr_t& aNewHandle) { +#if defined(XP_WIN) && defined(ACCESSIBILITY) + mNativeWindowHandle = aNewHandle; + return IPC_OK(); +#else + return IPC_FAIL_NO_REASON(this); +#endif +} + +mozilla::ipc::IPCResult BrowserChild::RecvDestroy() { + MOZ_ASSERT(mDestroyed == false); + mDestroyed = true; + + nsTArray<PContentPermissionRequestChild*> childArray = + nsContentPermissionUtils::GetContentPermissionRequestChildById( + GetTabId()); + + // Need to close undeleted ContentPermissionRequestChilds before tab is + // closed. + for (auto& permissionRequestChild : childArray) { + auto child = static_cast<RemotePermissionRequest*>(permissionRequestChild); + child->Destroy(); + } + + if (mBrowserChildMessageManager) { + // Message handlers are called from the event loop, so it better be safe to + // run script. + MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); + mBrowserChildMessageManager->DispatchTrustedEvent(u"unload"_ns); + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + observerService->RemoveObserver(this, BEFORE_FIRST_PAINT); + + // XXX what other code in ~BrowserChild() should we be running here? + DestroyWindow(); + + // Bounce through the event loop once to allow any delayed teardown runnables + // that were just generated to have a chance to run. + nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRenderLayers( + const bool& aEnabled, const layers::LayersObserverEpoch& aEpoch) { + if (mPendingDocShellBlockers > 0) { + mPendingRenderLayersReceivedMessage = true; + mPendingRenderLayers = aEnabled; + mPendingLayersObserverEpoch = aEpoch; + return IPC_OK(); + } + + // Since requests to change the rendering state come in from both the hang + // monitor channel and the PContent channel, we have an ordering problem. This + // code ensures that we respect the order in which the requests were made and + // ignore stale requests. + if (mLayersObserverEpoch >= aEpoch) { + return IPC_OK(); + } + mLayersObserverEpoch = aEpoch; + + auto clearPaintWhileInterruptingJS = MakeScopeExit([&] { + // We might force a paint, or we might already have painted and this is a + // no-op. In either case, once we exit this scope, we need to alert the + // ProcessHangMonitor that we've finished responding to what might have + // been a request to force paint. This is so that the BackgroundHangMonitor + // for force painting can be made to wait again. + if (aEnabled) { + ProcessHangMonitor::ClearPaintWhileInterruptingJS(mLayersObserverEpoch); + } + }); + + if (aEnabled) { + ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS(); + } + + if (mCompositorOptions) { + MOZ_ASSERT(mPuppetWidget); + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + MOZ_ASSERT(lm); + + // We send the current layer observer epoch to the compositor so that + // BrowserParent knows whether a layer update notification corresponds to + // the latest RecvRenderLayers request that was made. + lm->SetLayersObserverEpoch(mLayersObserverEpoch); + } + + mRenderLayers = aEnabled; + + if (aEnabled && IsVisible()) { + // This request is a no-op. + // In this case, we still want a MozLayerTreeReady notification to fire + // in the parent (so that it knows that the child has updated its epoch). + // PaintWhileInterruptingJSNoOp does that. + if (IPCOpen()) { + Unused << SendPaintWhileInterruptingJSNoOp(mLayersObserverEpoch); + } + return IPC_OK(); + } + + // FIXME(emilio): Probably / maybe this shouldn't be needed? See the comment + // in MakeVisible(), having the two separate states is not great. + UpdateVisibility(); + + if (!aEnabled) { + return IPC_OK(); + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + return IPC_OK(); + } + + // We don't use BrowserChildBase::GetPresShell() here because that would + // create a content viewer if one doesn't exist yet. Creating a content + // viewer can cause JS to run, which we want to avoid. + // nsIDocShell::GetPresShell returns null if no content viewer exists yet. + RefPtr<PresShell> presShell = docShell->GetPresShell(); + if (!presShell) { + return IPC_OK(); + } + + if (nsIFrame* root = presShell->GetRootFrame()) { + FrameLayerBuilder::InvalidateAllLayersForFrame( + nsLayoutUtils::GetDisplayRootFrame(root)); + root->SchedulePaint(); + } + + Telemetry::AutoTimer<Telemetry::TABCHILD_PAINT_TIME> timer; + // If we need to repaint, let's do that right away. No sense waiting until + // we get back to the event loop again. We suppress the display port so + // that we only paint what's visible. This ensures that the tab we're + // switching to paints as quickly as possible. + presShell->SuppressDisplayport(true); + if (nsContentUtils::IsSafeToRunScript()) { + WebWidget()->PaintNowIfNeeded(); + } else { + RefPtr<nsViewManager> vm = presShell->GetViewManager(); + if (nsView* view = vm->GetRootView()) { + presShell->Paint(view, view->GetBounds(), PaintFlags::PaintLayers); + } + } + presShell->SuppressDisplayport(false); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNavigateByKey( + const bool& aForward, const bool& aForDocumentNavigation) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return IPC_OK(); + } + + RefPtr<Element> result; + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + + // Move to the first or last document. + { + uint32_t type = + aForward + ? (aForDocumentNavigation + ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FIRSTDOC) + : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_ROOT)) + : (aForDocumentNavigation + ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LASTDOC) + : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LAST)); + uint32_t flags = nsIFocusManager::FLAG_BYKEY; + if (aForward || aForDocumentNavigation) { + flags |= nsIFocusManager::FLAG_NOSCROLL; + } + fm->MoveFocus(window, nullptr, type, flags, getter_AddRefs(result)); + } + + // No valid root element was found, so move to the first focusable element. + if (!result && aForward && !aForDocumentNavigation) { + fm->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_FIRST, + nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); + } + + SendRequestFocus(false, CallerType::System); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvHandledWindowedPluginKeyEvent( + const NativeEventData& aKeyEventData, const bool& aIsConsumed) { + if (NS_WARN_IF(!mPuppetWidget)) { + return IPC_OK(); + } + mPuppetWidget->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed); + return IPC_OK(); +} + +bool BrowserChild::InitBrowserChildMessageManager() { + if (!mBrowserChildMessageManager) { + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, false); + nsCOMPtr<EventTarget> chromeHandler = window->GetChromeEventHandler(); + NS_ENSURE_TRUE(chromeHandler, false); + + RefPtr<BrowserChildMessageManager> scope = mBrowserChildMessageManager = + new BrowserChildMessageManager(this); + + MOZ_ALWAYS_TRUE(nsMessageManagerScriptExecutor::Init()); + + nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler); + if (NS_WARN_IF(!root)) { + mBrowserChildMessageManager = nullptr; + return false; + } + root->SetParentTarget(scope); + } + + if (!mTriedBrowserInit) { + mTriedBrowserInit = true; + } + + return true; +} + +void BrowserChild::InitRenderingState( + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const layers::LayersId& aLayersId, + const CompositorOptions& aCompositorOptions) { + mPuppetWidget->InitIMEState(); + + MOZ_ASSERT(aLayersId.IsValid()); + mTextureFactoryIdentifier = aTextureFactoryIdentifier; + + // Pushing layers transactions directly to a separate + // compositor context. + PCompositorBridgeChild* compositorChild = CompositorBridgeChild::Get(); + if (!compositorChild) { + mLayersConnected = Some(false); + NS_WARNING("failed to get CompositorBridgeChild instance"); + return; + } + + mCompositorOptions = Some(aCompositorOptions); + + if (aLayersId.IsValid()) { + StaticMutexAutoLock lock(sBrowserChildrenMutex); + + if (!sBrowserChildren) { + sBrowserChildren = new BrowserChildMap; + } + MOZ_ASSERT(!sBrowserChildren->Get(uint64_t(aLayersId))); + sBrowserChildren->Put(uint64_t(aLayersId), this); + mLayersId = aLayersId; + } + + // Depending on timing, we might paint too early and fall back to basic + // layers. CreateRemoteLayerManager will destroy us if we manage to get a + // remote layer manager though, so that's fine. + MOZ_ASSERT(!mPuppetWidget->HasLayerManager() || + mPuppetWidget->GetLayerManager()->GetBackendType() == + layers::LayersBackend::LAYERS_BASIC); + bool success = false; + if (mLayersConnected == Some(true)) { + success = CreateRemoteLayerManager(compositorChild); + } + + if (success) { + MOZ_ASSERT(mLayersConnected == Some(true)); + // Succeeded to create "remote" layer manager + ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier); + gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier); + InitAPZState(); + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + MOZ_ASSERT(lm); + lm->SetLayersObserverEpoch(mLayersObserverEpoch); + } else { + NS_WARNING("Fallback to BasicLayerManager"); + mLayersConnected = Some(false); + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + if (observerService) { + observerService->AddObserver(this, BEFORE_FIRST_PAINT, false); + } +} + +bool BrowserChild::CreateRemoteLayerManager( + mozilla::layers::PCompositorBridgeChild* aCompositorChild) { + MOZ_ASSERT(aCompositorChild); + + bool success = false; + if (mCompositorOptions->UseWebRender()) { + success = mPuppetWidget->CreateRemoteLayerManager( + [&](LayerManager* aLayerManager) -> bool { + MOZ_ASSERT(aLayerManager->AsWebRenderLayerManager()); + nsCString error; + return aLayerManager->AsWebRenderLayerManager()->Initialize( + aCompositorChild, wr::AsPipelineId(mLayersId), + &mTextureFactoryIdentifier, error); + }); + } else { + nsTArray<LayersBackend> ignored; + PLayerTransactionChild* shadowManager = + aCompositorChild->SendPLayerTransactionConstructor(ignored, + GetLayersId()); + if (shadowManager && + shadowManager->SendGetTextureFactoryIdentifier( + &mTextureFactoryIdentifier) && + mTextureFactoryIdentifier.mParentBackend != + LayersBackend::LAYERS_NONE) { + success = true; + } + if (!success) { + // Since no LayerManager is associated with the tab's widget, we will + // never have an opportunity to destroy the PLayerTransaction on the next + // device or compositor reset. Therefore, we make sure to forcefully close + // it here. Failure to do so will cause the next layer tree to fail to + // attach due since the compositor requires the old layer tree to be + // disassociated. + if (shadowManager) { + static_cast<LayerTransactionChild*>(shadowManager)->Destroy(); + shadowManager = nullptr; + } + NS_WARNING("failed to allocate layer transaction"); + } else { + success = mPuppetWidget->CreateRemoteLayerManager( + [&](LayerManager* aLayerManager) -> bool { + ShadowLayerForwarder* lf = aLayerManager->AsShadowForwarder(); + lf->SetShadowManager(shadowManager); + lf->IdentifyTextureHost(mTextureFactoryIdentifier); + return true; + }); + } + } + return success; +} + +void BrowserChild::InitAPZState() { + if (!mCompositorOptions->UseAPZ()) { + return; + } + auto cbc = CompositorBridgeChild::Get(); + + // Initialize the ApzcTreeManager. This takes multiple casts because of ugly + // multiple inheritance. + PAPZCTreeManagerChild* baseProtocol = + cbc->SendPAPZCTreeManagerConstructor(mLayersId); + APZCTreeManagerChild* derivedProtocol = + static_cast<APZCTreeManagerChild*>(baseProtocol); + + mApzcTreeManager = RefPtr<IAPZCTreeManager>(derivedProtocol); + + // Initialize the GeckoContentController for this tab. We don't hold a + // reference because we don't need it. The ContentProcessController will hold + // a reference to the tab, and will be destroyed by the compositor or ipdl + // during destruction. + RefPtr<GeckoContentController> contentController = + new ContentProcessController(this); + APZChild* apzChild = new APZChild(contentController); + cbc->SendPAPZConstructor(apzChild, mLayersId); +} + +IPCResult BrowserChild::RecvUpdateEffects(const EffectsInfo& aEffects) { + bool needInvalidate = false; + if (mEffectsInfo.IsVisible() && aEffects.IsVisible() && + mEffectsInfo != aEffects) { + // if we are staying visible and either the visrect or scale changed we need + // to invalidate + needInvalidate = true; + } + + mEffectsInfo = aEffects; + UpdateVisibility(); + + if (needInvalidate) { + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (docShell) { + // We don't use BrowserChildBase::GetPresShell() here because that would + // create a content viewer if one doesn't exist yet. Creating a content + // viewer can cause JS to run, which we want to avoid. + // nsIDocShell::GetPresShell returns null if no content viewer exists yet. + RefPtr<PresShell> presShell = docShell->GetPresShell(); + if (presShell) { + if (nsIFrame* root = presShell->GetRootFrame()) { + root->InvalidateFrame(); + } + } + } + } + + return IPC_OK(); +} + +bool BrowserChild::IsVisible() { + return mPuppetWidget && mPuppetWidget->IsVisible(); +} + +void BrowserChild::UpdateVisibility() { + bool shouldBeVisible = mIsTopLevel ? mRenderLayers : mEffectsInfo.IsVisible(); + bool isVisible = IsVisible(); + + if (shouldBeVisible != isVisible) { + if (shouldBeVisible) { + MakeVisible(); + } else { + MakeHidden(); + } + } +} + +void BrowserChild::MakeVisible() { + if (IsVisible()) { + return; + } + + if (mPuppetWidget) { + mPuppetWidget->Show(true); + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + return; + } + + // The browser / tab-switcher is responsible of fixing the browsingContext + // state up explicitly via SetDocShellIsActive, which propagates to children + // automatically. + // + // We need it not to be observable, as this used via RecvRenderLayers and co., + // for stuff like async tab warming. + // + // We don't want to go through the docshell because we don't want to change + // the visibility state of the document, which has side effects like firing + // events to content, unblocking media playback, unthrottling timeouts... + if (RefPtr<PresShell> presShell = docShell->GetPresShell()) { + presShell->SetIsActive(true); + } +} + +void BrowserChild::MakeHidden() { + if (!IsVisible()) { + return; + } + + // Due to the nested event loop in ContentChild::ProvideWindowCommon, + // it's possible to be told to become hidden before we're finished + // setting up a layer manager. We should skip clearing cached layers + // in that case, since doing so might accidentally put is into + // BasicLayers mode. + if (mPuppetWidget && mPuppetWidget->HasLayerManager()) { + ClearCachedResources(); + } + + if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) { + // Hide all plugins in this tab. We don't use + // BrowserChildBase::GetPresShell() here because that would create a content + // viewer if one doesn't exist yet. Creating a content viewer can cause JS + // to run, which we want to avoid. nsIDocShell::GetPresShell returns null if + // no content viewer exists yet. + if (RefPtr<PresShell> presShell = docShell->GetPresShell()) { + if (nsPresContext* presContext = presShell->GetPresContext()) { + nsRootPresContext* rootPresContext = presContext->GetRootPresContext(); + nsIFrame* rootFrame = presShell->GetRootFrame(); + rootPresContext->ComputePluginGeometryUpdates(rootFrame, nullptr, + nullptr); + rootPresContext->ApplyPluginGeometryUpdates(); + } + presShell->SetIsActive(false); + } + } + + if (mPuppetWidget) { + mPuppetWidget->Show(false); + } +} + +NS_IMETHODIMP +BrowserChild::GetMessageManager(ContentFrameMessageManager** aResult) { + RefPtr<ContentFrameMessageManager> mm(mBrowserChildMessageManager); + mm.forget(aResult); + return *aResult ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +BrowserChild::GetWebBrowserChrome(nsIWebBrowserChrome3** aWebBrowserChrome) { + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (nsCOMPtr<nsIWebBrowserChrome3> chrome = + do_QueryActor("WebBrowserChrome", docShell->GetDocument())) { + chrome.forget(aWebBrowserChrome); + } else { + // TODO: remove fallback + NS_IF_ADDREF(*aWebBrowserChrome = mWebBrowserChrome); + } + + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::SetWebBrowserChrome(nsIWebBrowserChrome3* aWebBrowserChrome) { + mWebBrowserChrome = aWebBrowserChrome; + return NS_OK; +} + +void BrowserChild::SendRequestFocus(bool aCanFocus, CallerType aCallerType) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return; + } + + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + if (!window) { + return; + } + + BrowsingContext* focusedBC = fm->GetFocusedBrowsingContext(); + if (focusedBC == window->GetBrowsingContext()) { + // BrowsingContext has the focus already, do not request again. + return; + } + + PBrowserChild::SendRequestFocus(aCanFocus, aCallerType); +} + +NS_IMETHODIMP +BrowserChild::GetTabId(uint64_t* aId) { + *aId = GetTabId(); + return NS_OK; +} + +void BrowserChild::SetTabId(const TabId& aTabId) { + MOZ_ASSERT(mUniqueId == 0); + + mUniqueId = aTabId; + NestedBrowserChildMap()[mUniqueId] = this; +} + +NS_IMETHODIMP +BrowserChild::GetChromeOuterWindowID(uint64_t* aId) { + *aId = ChromeOuterWindowID(); + return NS_OK; +} + +bool BrowserChild::DoSendBlockingMessage( + const nsAString& aMessage, StructuredCloneData& aData, + nsTArray<StructuredCloneData>* aRetVal) { + ClonedMessageData data; + if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { + return false; + } + return SendSyncMessage(PromiseFlatString(aMessage), data, aRetVal); +} + +nsresult BrowserChild::DoSendAsyncMessage(const nsAString& aMessage, + StructuredCloneData& aData) { + ClonedMessageData data; + if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + if (!SendAsyncMessage(PromiseFlatString(aMessage), data)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +/* static */ +nsTArray<RefPtr<BrowserChild>> BrowserChild::GetAll() { + StaticMutexAutoLock lock(sBrowserChildrenMutex); + + nsTArray<RefPtr<BrowserChild>> list; + if (!sBrowserChildren) { + return list; + } + + for (auto iter = sBrowserChildren->Iter(); !iter.Done(); iter.Next()) { + list.AppendElement(iter.Data()); + } + + return list; +} + +BrowserChild* BrowserChild::GetFrom(PresShell* aPresShell) { + Document* doc = aPresShell->GetDocument(); + if (!doc) { + return nullptr; + } + nsCOMPtr<nsIDocShell> docShell(doc->GetDocShell()); + return GetFrom(docShell); +} + +BrowserChild* BrowserChild::GetFrom(layers::LayersId aLayersId) { + StaticMutexAutoLock lock(sBrowserChildrenMutex); + if (!sBrowserChildren) { + return nullptr; + } + return sBrowserChildren->Get(uint64_t(aLayersId)); +} + +void BrowserChild::DidComposite(mozilla::layers::TransactionId aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd) { + MOZ_ASSERT(mPuppetWidget); + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + MOZ_ASSERT(lm); + + lm->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); +} + +void BrowserChild::DidRequestComposite(const TimeStamp& aCompositeReqStart, + const TimeStamp& aCompositeReqEnd) { + nsCOMPtr<nsIDocShell> docShellComPtr = do_GetInterface(WebNavigation()); + if (!docShellComPtr) { + return; + } + + nsDocShell* docShell = static_cast<nsDocShell*>(docShellComPtr.get()); + RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get(); + + if (timelines && timelines->HasConsumer(docShell)) { + // Since we're assuming that it's impossible for content JS to directly + // trigger a synchronous paint, we can avoid capturing a stack trace here, + // which means we won't run into JS engine reentrancy issues like bug + // 1310014. + timelines->AddMarkerForDocShell( + docShell, "CompositeForwardTransaction", aCompositeReqStart, + MarkerTracingType::START, MarkerStackRequest::NO_STACK); + timelines->AddMarkerForDocShell(docShell, "CompositeForwardTransaction", + aCompositeReqEnd, MarkerTracingType::END, + MarkerStackRequest::NO_STACK); + } +} + +void BrowserChild::ClearCachedResources() { + MOZ_ASSERT(mPuppetWidget); + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + MOZ_ASSERT(lm); + + lm->ClearCachedResources(); + + if (nsCOMPtr<Document> document = GetTopLevelDocument()) { + nsPresContext* presContext = document->GetPresContext(); + if (presContext) { + presContext->NotifyPaintStatusReset(); + } + } +} + +void BrowserChild::InvalidateLayers() { + MOZ_ASSERT(mPuppetWidget); + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + MOZ_ASSERT(lm); + + FrameLayerBuilder::InvalidateAllLayers(lm); +} + +void BrowserChild::SchedulePaint() { + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + return; + } + + // We don't use BrowserChildBase::GetPresShell() here because that would + // create a content viewer if one doesn't exist yet. Creating a content viewer + // can cause JS to run, which we want to avoid. nsIDocShell::GetPresShell + // returns null if no content viewer exists yet. + if (RefPtr<PresShell> presShell = docShell->GetPresShell()) { + if (nsIFrame* root = presShell->GetRootFrame()) { + root->SchedulePaint(); + } + } +} + +void BrowserChild::ReinitRendering() { + MOZ_ASSERT(mLayersId.IsValid()); + + // Before we establish a new PLayerTransaction, we must connect our layer tree + // id, CompositorBridge, and the widget compositor all together again. + // Normally this happens in BrowserParent before BrowserChild is given + // rendering information. + // + // In this case, we will send a sync message to our BrowserParent, which in + // turn will send a sync message to the Compositor of the widget owning this + // tab. This guarantees the correct association is in place before our + // PLayerTransaction constructor message arrives on the cross-process + // compositor bridge. + CompositorOptions options; + SendEnsureLayersConnected(&options); + mCompositorOptions = Some(options); + + bool success = false; + RefPtr<CompositorBridgeChild> cb = CompositorBridgeChild::Get(); + + if (cb) { + success = CreateRemoteLayerManager(cb); + } + + if (!success) { + NS_WARNING("failed to recreate layer manager"); + return; + } + + mLayersConnected = Some(true); + ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier); + gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier); + + InitAPZState(); + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + MOZ_ASSERT(lm); + lm->SetLayersObserverEpoch(mLayersObserverEpoch); + + nsCOMPtr<Document> doc(GetTopLevelDocument()); + doc->NotifyLayerManagerRecreated(); +} + +void BrowserChild::ReinitRenderingForDeviceReset() { + InvalidateLayers(); + + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + if (WebRenderLayerManager* wlm = lm->AsWebRenderLayerManager()) { + wlm->DoDestroy(/* aIsSync */ true); + } else if (ClientLayerManager* clm = lm->AsClientLayerManager()) { + if (ShadowLayerForwarder* fwd = clm->AsShadowForwarder()) { + // Force the LayerTransactionChild to synchronously shutdown. It is + // okay to do this early, we'll simply stop sending messages. This + // step is necessary since otherwise the compositor will think we + // are trying to attach two layer trees to the same ID. + fwd->SynchronouslyShutdown(); + } + } else { + if (mLayersConnected.isNothing()) { + return; + } + } + + // Proceed with destroying and recreating the layer manager. + ReinitRendering(); +} + +NS_IMETHODIMP +BrowserChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, + const nsAString& aTipText, + const nsAString& aTipDir) { + nsString str(aTipText); + nsString dir(aTipDir); + SendShowTooltip(aXCoords, aYCoords, str, dir); + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::OnHideTooltip() { + SendHideTooltip(); + return NS_OK; +} + +void BrowserChild::NotifyJankedAnimations( + const nsTArray<uint64_t>& aJankedAnimations) { + MOZ_ASSERT(mPuppetWidget); + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + MOZ_ASSERT(lm); + lm->UpdatePartialPrerenderedAnimations(aJankedAnimations); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRequestNotifyAfterRemotePaint() { + // Get the CompositorBridgeChild instance for this content thread. + CompositorBridgeChild* compositor = CompositorBridgeChild::Get(); + + // Tell the CompositorBridgeChild that, when it gets a RemotePaintIsReady + // message that it should forward it us so that we can bounce it to our + // BrowserParent. + compositor->RequestNotifyAfterRemotePaint(this); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUIResolutionChanged( + const float& aDpi, const int32_t& aRounding, const double& aScale) { + nsCOMPtr<Document> document(GetTopLevelDocument()); + if (!document) { + return IPC_OK(); + } + + ScreenIntSize oldScreenSize = GetInnerSize(); + if (aDpi > 0) { + mPuppetWidget->UpdateBackingScaleCache(aDpi, aRounding, aScale); + } + RefPtr<nsPresContext> presContext = document->GetPresContext(); + if (presContext) { + presContext->UIResolutionChangedSync(); + } + + ScreenIntSize screenSize = GetInnerSize(); + if (mHasValidInnerSize && oldScreenSize != screenSize) { + ScreenIntRect screenRect = GetOuterRect(); + + // See RecvUpdateDimensions for the order of these operations. + 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); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSafeAreaInsetsChanged( + const mozilla::ScreenIntMargin& aSafeAreaInsets) { + mPuppetWidget->UpdateSafeAreaInsets(aSafeAreaInsets); + + nsCOMPtr<nsIScreenManager> screenMgr = + do_GetService("@mozilla.org/gfx/screenmanager;1"); + ScreenIntMargin currentSafeAreaInsets; + if (screenMgr) { + // aSafeAreaInsets is for current screen. But we have to calculate + // safe insets for content window. + int32_t x, y, cx, cy; + GetDimensions(0, &x, &y, &cx, &cy); + nsCOMPtr<nsIScreen> screen; + screenMgr->ScreenForRect(x, y, cx, cy, getter_AddRefs(screen)); + + if (screen) { + LayoutDeviceIntRect windowRect(x + mClientOffset.x + mChromeOffset.x, + y + mClientOffset.y + mChromeOffset.y, cx, + cy); + currentSafeAreaInsets = nsContentUtils::GetWindowSafeAreaInsets( + screen, aSafeAreaInsets, windowRect); + } + } + + if (nsCOMPtr<Document> document = GetTopLevelDocument()) { + nsPresContext* presContext = document->GetPresContext(); + if (presContext) { + presContext->SetSafeAreaInsets(currentSafeAreaInsets); + } + } + + // https://github.com/w3c/csswg-drafts/issues/4670 + // Actually we don't set this value on sub document. This behaviour is + // same as Blink that safe area insets isn't set on sub document. + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvAllowScriptsToClose() { + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + if (window) { + nsGlobalWindowOuter::Cast(window)->AllowScriptsToClose(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSetWidgetNativeData( + const WindowsHandle& aWidgetNativeData) { + mWidgetNativeData = aWidgetNativeData; + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvReleaseAllPointerCapture() { + PointerEventHandler::ReleaseAllPointerCapture(); + return IPC_OK(); +} + +mozilla::plugins::PPluginWidgetChild* BrowserChild::AllocPPluginWidgetChild() { +#ifdef XP_WIN + return new mozilla::plugins::PluginWidgetChild(); +#else + MOZ_ASSERT_UNREACHABLE("AllocPPluginWidgetChild only supports Windows"); + return nullptr; +#endif +} + +bool BrowserChild::DeallocPPluginWidgetChild( + mozilla::plugins::PPluginWidgetChild* aActor) { + delete aActor; + return true; +} + +#ifdef XP_WIN +nsresult BrowserChild::CreatePluginWidget(nsIWidget* aParent, + nsIWidget** aOut) { + *aOut = nullptr; + mozilla::plugins::PluginWidgetChild* child = + static_cast<mozilla::plugins::PluginWidgetChild*>( + SendPPluginWidgetConstructor()); + if (!child) { + NS_ERROR("couldn't create PluginWidgetChild"); + return NS_ERROR_UNEXPECTED; + } + nsCOMPtr<nsIWidget> pluginWidget = + nsIWidget::CreatePluginProxyWidget(this, child); + if (!pluginWidget) { + NS_ERROR("couldn't create PluginWidgetProxy"); + return NS_ERROR_UNEXPECTED; + } + + nsWidgetInitData initData; + initData.mWindowType = eWindowType_plugin_ipc_content; + initData.clipChildren = true; + initData.clipSiblings = true; + nsresult rv = pluginWidget->Create( + aParent, nullptr, LayoutDeviceIntRect(0, 0, 0, 0), &initData); + if (NS_FAILED(rv)) { + NS_WARNING("Creating native plugin widget on the chrome side failed."); + } + pluginWidget.forget(aOut); + return rv; +} +#endif // XP_WIN + +PPaymentRequestChild* BrowserChild::AllocPPaymentRequestChild() { + MOZ_CRASH( + "We should never be manually allocating PPaymentRequestChild actors"); + return nullptr; +} + +bool BrowserChild::DeallocPPaymentRequestChild(PPaymentRequestChild* actor) { + delete actor; + return true; +} + +ScreenIntSize BrowserChild::GetInnerSize() { + LayoutDeviceIntSize innerSize = + RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>( + innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +}; + +Maybe<nsRect> BrowserChild::GetVisibleRect() const { + if (mIsTopLevel) { + // We are conservative about visible rects for top-level browsers to avoid + // artifacts when resizing + return Nothing(); + } + + return Some(mEffectsInfo.mVisibleRect); +} + +Maybe<LayoutDeviceRect> +BrowserChild::GetTopLevelViewportVisibleRectInSelfCoords() const { + if (mIsTopLevel) { + return Nothing(); + } + + if (!mChildToParentConversionMatrix) { + // We have no way to tell this remote document visible rect right now. + return Nothing(); + } + + Maybe<LayoutDeviceToLayoutDeviceMatrix4x4> inverse = + mChildToParentConversionMatrix->MaybeInverse(); + if (!inverse) { + return Nothing(); + } + + // Convert the remote document visible rect to the coordinate system of the + // iframe document. + Maybe<LayoutDeviceRect> rect = UntransformBy( + *inverse, + ViewAs<LayoutDevicePixel>( + mTopLevelViewportVisibleRectInBrowserCoords, + PixelCastJustification::ContentProcessIsLayerInUiProcess), + LayoutDeviceRect::MaxIntRect()); + if (!rect) { + return Nothing(); + } + + return rect; +} + +ScreenIntRect BrowserChild::GetOuterRect() { + LayoutDeviceIntRect outerRect = + RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>( + outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +} + +void BrowserChild::PaintWhileInterruptingJS( + const layers::LayersObserverEpoch& aEpoch) { + if (!IPCOpen() || !mPuppetWidget || !mPuppetWidget->HasLayerManager()) { + // Don't bother doing anything now. Better to wait until we receive the + // message on the PContent channel. + return; + } + + MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript()); + nsAutoScriptBlocker scriptBlocker; + RecvRenderLayers(true /* aEnabled */, aEpoch); +} + +nsresult BrowserChild::CanCancelContentJS( + nsIRemoteTab::NavigationType aNavigationType, int32_t aNavigationIndex, + nsIURI* aNavigationURI, int32_t aEpoch, bool* aCanCancel) { + nsresult rv; + *aCanCancel = false; + + if (aEpoch <= mCancelContentJSEpoch) { + // The next page loaded before we got here, so we shouldn't try to cancel + // the content JS. + return NS_OK; + } + + // If we have session history in the parent we've already performed + // the checks following, so we can return early. + if (mozilla::SessionHistoryInParent()) { + *aCanCancel = true; + return NS_OK; + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + nsCOMPtr<nsISHistory> history; + if (docShell) { + history = nsDocShell::Cast(docShell)->GetSessionHistory()->LegacySHistory(); + } + + if (!history) { + return NS_ERROR_FAILURE; + } + + int32_t current; + rv = history->GetIndex(¤t); + NS_ENSURE_SUCCESS(rv, rv); + + if (current == -1) { + // This tab has no history! Just return. + return NS_OK; + } + + nsCOMPtr<nsISHEntry> entry; + rv = history->GetEntryAtIndex(current, getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIURI> currentURI = entry->GetURI(); + if (!currentURI->SchemeIs("http") && !currentURI->SchemeIs("https") && + !currentURI->SchemeIs("file")) { + // Only cancel content JS for http(s) and file URIs. Other URIs are probably + // internal and we should just let them run to completion. + return NS_OK; + } + + if (aNavigationType == nsIRemoteTab::NAVIGATE_BACK) { + aNavigationIndex = current - 1; + } else if (aNavigationType == nsIRemoteTab::NAVIGATE_FORWARD) { + aNavigationIndex = current + 1; + } else if (aNavigationType == nsIRemoteTab::NAVIGATE_URL) { + if (!aNavigationURI) { + return NS_ERROR_FAILURE; + } + + if (aNavigationURI->SchemeIs("javascript")) { + // "javascript:" URIs don't (necessarily) trigger navigation to a + // different page, so don't allow the current page's JS to terminate. + return NS_OK; + } + + // If navigating directly to a URL (e.g. via hitting Enter in the location + // bar), then we can cancel anytime the next URL is different from the + // current, *excluding* the ref ("#"). + bool equals; + rv = currentURI->EqualsExceptRef(aNavigationURI, &equals); + NS_ENSURE_SUCCESS(rv, rv); + *aCanCancel = !equals; + return NS_OK; + } + // Note: aNavigationType may also be NAVIGATE_INDEX, in which case we don't + // need to do anything special. + + int32_t delta = aNavigationIndex > current ? 1 : -1; + for (int32_t i = current + delta; i != aNavigationIndex + delta; i += delta) { + nsCOMPtr<nsISHEntry> nextEntry; + // If `i` happens to be negative, this call will fail (which is what we + // would want to happen). + rv = history->GetEntryAtIndex(i, getter_AddRefs(nextEntry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISHEntry> laterEntry = delta == 1 ? nextEntry : entry; + nsCOMPtr<nsIURI> thisURI = entry->GetURI(); + nsCOMPtr<nsIURI> nextURI = nextEntry->GetURI(); + + // If we changed origin and the load wasn't in a subframe, we know it was + // a full document load, so we can cancel the content JS safely. + if (!laterEntry->GetIsSubFrame()) { + nsAutoCString thisHost; + rv = thisURI->GetPrePath(thisHost); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString nextHost; + rv = nextURI->GetPrePath(nextHost); + NS_ENSURE_SUCCESS(rv, rv); + + if (!thisHost.Equals(nextHost)) { + *aCanCancel = true; + return NS_OK; + } + } + + entry = nextEntry; + } + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::BeginSendingWebProgressEventsToParent() { + mShouldSendWebProgressEventsToParent = true; + return NS_OK; +} + +nsresult BrowserChild::GetHasSiblings(bool* aHasSiblings) { + *aHasSiblings = mHasSiblings; + return NS_OK; +} + +nsresult BrowserChild::SetHasSiblings(bool aHasSiblings) { + mHasSiblings = aHasSiblings; + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aStateFlags, + nsresult aStatus) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + return NS_OK; + } + + // We shouldn't need to notify the parent of redirect state changes, since + // with DocumentChannel that only happens when we switch to the real channel, + // and that's an implementation detail that we can hide. + if (aStateFlags & nsIWebProgressListener::STATE_IS_REDIRECTED_DOCUMENT) { + return NS_OK; + } + + RefPtr<Document> document; + if (nsCOMPtr<nsPIDOMWindowOuter> outerWindow = do_GetInterface(docShell)) { + document = outerWindow->GetExtantDoc(); + } else { + return NS_OK; + } + + Maybe<WebProgressData> webProgressData; + Maybe<WebProgressStateChangeData> stateChangeData; + RequestData requestData; + + MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData, + requestData)); + + if (webProgressData->isTopLevel()) { + stateChangeData.emplace(); + + stateChangeData->isNavigating() = docShell->GetIsNavigating(); + stateChangeData->mayEnableCharacterEncodingMenu() = + docShell->GetMayEnableCharacterEncodingMenu(); + stateChangeData->charsetAutodetected() = docShell->GetCharsetAutodetected(); + + if (document && aStateFlags & nsIWebProgressListener::STATE_STOP) { + document->GetContentType(stateChangeData->contentType()); + document->GetCharacterSet(stateChangeData->charset()); + stateChangeData->documentURI() = document->GetDocumentURIObject(); + } else { + stateChangeData->contentType().SetIsVoid(true); + stateChangeData->charset().SetIsVoid(true); + } + } + + Unused << SendOnStateChange(webProgressData, requestData, aStateFlags, + aStatus, stateChangeData); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + Maybe<WebProgressData> webProgressData; + RequestData requestData; + + nsresult rv = PrepareProgressListenerData(aWebProgress, aRequest, + webProgressData, requestData); + NS_ENSURE_SUCCESS(rv, rv); + + Unused << SendOnProgressChange(webProgressData, requestData, aCurSelfProgress, + aMaxSelfProgress, aCurTotalProgress, + aMaxTotalProgress); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aLocation, + uint32_t aFlags) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + nsCOMPtr<nsIWebNavigation> webNav = WebNavigation(); + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(webNav); + if (!docShell) { + return NS_OK; + } + + RefPtr<Document> document; + if (nsCOMPtr<nsPIDOMWindowOuter> outerWindow = do_GetInterface(docShell)) { + document = outerWindow->GetExtantDoc(); + } else { + return NS_OK; + } + + if (!document) { + return NS_OK; + } + + Maybe<WebProgressData> webProgressData; + RequestData requestData; + + MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData, + requestData)); + + Maybe<WebProgressLocationChangeData> locationChangeData; + + bool canGoBack = false; + bool canGoForward = false; + + MOZ_TRY(webNav->GetCanGoBack(&canGoBack)); + MOZ_TRY(webNav->GetCanGoForward(&canGoForward)); + + if (aWebProgress && webProgressData->isTopLevel()) { + locationChangeData.emplace(); + + document->GetContentType(locationChangeData->contentType()); + locationChangeData->isNavigating() = docShell->GetIsNavigating(); + locationChangeData->documentURI() = document->GetDocumentURIObject(); + document->GetTitle(locationChangeData->title()); + document->GetCharacterSet(locationChangeData->charset()); + + locationChangeData->mayEnableCharacterEncodingMenu() = + docShell->GetMayEnableCharacterEncodingMenu(); + locationChangeData->charsetAutodetected() = + docShell->GetCharsetAutodetected(); + + locationChangeData->contentPrincipal() = document->NodePrincipal(); + locationChangeData->contentPartitionedPrincipal() = + document->PartitionedPrincipal(); + locationChangeData->csp() = document->GetCsp(); + locationChangeData->referrerInfo() = document->ReferrerInfo(); + locationChangeData->isSyntheticDocument() = document->IsSyntheticDocument(); + + if (nsCOMPtr<nsILoadGroup> loadGroup = document->GetDocumentLoadGroup()) { + uint64_t requestContextID = 0; + MOZ_TRY(loadGroup->GetRequestContextID(&requestContextID)); + locationChangeData->requestContextID() = Some(requestContextID); + } + +#ifdef MOZ_CRASHREPORTER + if (CrashReporter::GetEnabled()) { + nsCOMPtr<nsIURI> annotationURI; + + nsresult rv = + NS_MutateURI(aLocation).SetUserPass(""_ns).Finalize(annotationURI); + + if (NS_FAILED(rv)) { + // Ignore failures on about: URIs. + annotationURI = aLocation; + } + + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, + annotationURI->GetSpecOrDefault()); + } +#endif + } + + Unused << SendOnLocationChange(webProgressData, requestData, aLocation, + aFlags, canGoBack, canGoForward, + locationChangeData); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const char16_t* aMessage) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + Maybe<WebProgressData> webProgressData; + RequestData requestData; + + nsresult rv = PrepareProgressListenerData(aWebProgress, aRequest, + webProgressData, requestData); + + NS_ENSURE_SUCCESS(rv, rv); + + const nsString message(aMessage); + + Unused << SendOnStatusChange(webProgressData, requestData, aStatus, message); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) { + // Security changes are now handled entirely in the parent process + // so we don't need to worry about forwarding them (and we shouldn't + // be receiving any to forward). + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnContentBlockingEvent(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aEvent) { + // The OnContentBlockingEvent only happenes in the parent process. It should + // not be seen in the content process. + MOZ_DIAGNOSTIC_ASSERT( + false, "OnContentBlockingEvent should not be seen in content process."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP BrowserChild::OnProgressChange64(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int64_t aCurSelfProgress, + int64_t aMaxSelfProgress, + int64_t aCurTotalProgress, + int64_t aMaxTotalProgress) { + // All the events we receive are filtered through an nsBrowserStatusFilter, + // which accepts ProgressChange64 events, but truncates the progress values to + // uint32_t and calls OnProgressChange. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP BrowserChild::OnRefreshAttempted(nsIWebProgress* aWebProgress, + nsIURI* aRefreshURI, + int32_t aMillis, bool aSameURI, + bool* aOut) { + NS_ENSURE_ARG_POINTER(aOut); + *aOut = true; + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::NotifyNavigationFinished() { + Unused << SendNavigationFinished(); + return NS_OK; +} + +nsresult BrowserChild::PrepareProgressListenerData( + nsIWebProgress* aWebProgress, nsIRequest* aRequest, + Maybe<WebProgressData>& aWebProgressData, RequestData& aRequestData) { + if (aWebProgress) { + aWebProgressData.emplace(); + + bool isTopLevel = false; + nsresult rv = aWebProgress->GetIsTopLevel(&isTopLevel); + NS_ENSURE_SUCCESS(rv, rv); + aWebProgressData->isTopLevel() = isTopLevel; + + bool isLoadingDocument = false; + rv = aWebProgress->GetIsLoadingDocument(&isLoadingDocument); + NS_ENSURE_SUCCESS(rv, rv); + aWebProgressData->isLoadingDocument() = isLoadingDocument; + + uint32_t loadType = 0; + rv = aWebProgress->GetLoadType(&loadType); + NS_ENSURE_SUCCESS(rv, rv); + aWebProgressData->loadType() = loadType; + } + + nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); + if (channel) { + nsCOMPtr<nsIURI> uri; + nsresult rv = channel->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + aRequestData.requestURI() = uri; + + rv = channel->GetOriginalURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + aRequestData.originalRequestURI() = uri; + + nsCOMPtr<nsIClassifiedChannel> classifiedChannel = + do_QueryInterface(channel); + if (classifiedChannel) { + nsAutoCString matchedList; + rv = classifiedChannel->GetMatchedList(matchedList); + NS_ENSURE_SUCCESS(rv, rv); + aRequestData.matchedList() = std::move(matchedList); + } + } + return NS_OK; +} + +bool BrowserChild::UpdateSessionStore(uint32_t aFlushId, bool aIsFinal) { + if (!mSessionStoreListener) { + return false; + } + RefPtr<ContentSessionStore> store = mSessionStoreListener->GetSessionStore(); + + Maybe<nsCString> docShellCaps; + if (store->IsDocCapChanged()) { + docShellCaps.emplace(store->GetDocShellCaps()); + } + + Maybe<bool> privatedMode; + if (store->IsPrivateChanged()) { + privatedMode.emplace(store->GetPrivateModeEnabled()); + } + + nsTArray<int32_t> positionDescendants; + nsTArray<nsCString> positions; + if (store->IsScrollPositionChanged()) { + store->GetScrollPositions(positions, positionDescendants); + } + + nsTArray<InputFormData> inputs; + nsTArray<CollectedInputDataValue> idVals, xPathVals; + if (store->IsFormDataChanged()) { + inputs = store->GetInputs(idVals, xPathVals); + } + + nsTArray<nsCString> origins; + nsTArray<nsString> keys, values; + bool isFullStorage = false; + if (store->IsStorageUpdated()) { + isFullStorage = store->GetAndClearStorageChanges(origins, keys, values); + } + + Unused << SendSessionStoreUpdate( + docShellCaps, privatedMode, positions, positionDescendants, inputs, + idVals, xPathVals, origins, keys, values, isFullStorage, + store->GetAndClearSHistoryChanged(), aFlushId, aIsFinal, + mSessionStoreListener->GetEpoch()); + return true; +} + +#ifdef XP_WIN +RefPtr<PBrowserChild::IsWindowSupportingProtectedMediaPromise> +BrowserChild::DoesWindowSupportProtectedMedia() { + MOZ_ASSERT( + NS_IsMainThread(), + "Protected media support check should be done on main thread only."); + if (mWindowSupportsProtectedMedia) { + // If we've already checked and have a cached result, resolve with that. + return IsWindowSupportingProtectedMediaPromise::CreateAndResolve( + mWindowSupportsProtectedMedia.value(), __func__); + } + RefPtr<BrowserChild> self = this; + // We chain off the promise rather than passing it directly so we can cache + // the result and use that for future calls. + return SendIsWindowSupportingProtectedMedia(ChromeOuterWindowID()) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self](bool isSupported) { + // If a result was cached while this check was inflight, ensure the + // results match. + MOZ_ASSERT_IF( + self->mWindowSupportsProtectedMedia, + self->mWindowSupportsProtectedMedia.value() == isSupported); + // Cache the response as it will not change during the lifetime + // of this object. + self->mWindowSupportsProtectedMedia = Some(isSupported); + return IsWindowSupportingProtectedMediaPromise::CreateAndResolve( + self->mWindowSupportsProtectedMedia.value(), __func__); + }, + [](ResponseRejectReason reason) { + return IsWindowSupportingProtectedMediaPromise::CreateAndReject( + reason, __func__); + }); +} +#endif + +void BrowserChild::NotifyContentBlockingEvent( + uint32_t aEvent, nsIChannel* aChannel, bool aBlocked, + const nsACString& aTrackingOrigin, + const nsTArray<nsCString>& aTrackingFullHashes, + const Maybe< + mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>& + aReason) { + if (!IPCOpen()) { + return; + } + + Maybe<WebProgressData> webProgressData; + RequestData requestData; + nsresult rv = PrepareProgressListenerData(nullptr, aChannel, webProgressData, + requestData); + NS_ENSURE_SUCCESS_VOID(rv); + + Unused << SendNotifyContentBlockingEvent(aEvent, requestData, aBlocked, + PromiseFlatCString(aTrackingOrigin), + aTrackingFullHashes, aReason); +} + +BrowserChildMessageManager::BrowserChildMessageManager( + BrowserChild* aBrowserChild) + : ContentFrameMessageManager(new nsFrameMessageManager(aBrowserChild)), + mBrowserChild(aBrowserChild) {} + +BrowserChildMessageManager::~BrowserChildMessageManager() = default; + +NS_IMPL_CYCLE_COLLECTION_CLASS(BrowserChildMessageManager) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BrowserChildMessageManager, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild); + NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BrowserChildMessageManager, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserChildMessageManager) + NS_INTERFACE_MAP_ENTRY(nsIMessageSender) + NS_INTERFACE_MAP_ENTRY(ContentFrameMessageManager) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(BrowserChildMessageManager, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(BrowserChildMessageManager, DOMEventTargetHelper) + +JSObject* BrowserChildMessageManager::WrapObject( + JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { + return ContentFrameMessageManager_Binding::Wrap(aCx, this, aGivenProto); +} + +void BrowserChildMessageManager::MarkForCC() { + if (mBrowserChild) { + mBrowserChild->MarkScopesForCC(); + } + EventListenerManager* elm = GetExistingListenerManager(); + if (elm) { + elm->MarkForCC(); + } + MessageManagerGlobal::MarkForCC(); +} + +Nullable<WindowProxyHolder> BrowserChildMessageManager::GetContent( + ErrorResult& aError) { + nsCOMPtr<nsIDocShell> docShell = GetDocShell(aError); + if (!docShell) { + return nullptr; + } + return WindowProxyHolder(docShell->GetBrowsingContext()); +} + +already_AddRefed<nsIDocShell> BrowserChildMessageManager::GetDocShell( + ErrorResult& aError) { + if (!mBrowserChild) { + aError.Throw(NS_ERROR_NULL_POINTER); + return nullptr; + } + nsCOMPtr<nsIDocShell> window = + do_GetInterface(mBrowserChild->WebNavigation()); + return window.forget(); +} + +already_AddRefed<nsIEventTarget> +BrowserChildMessageManager::GetTabEventTarget() { + nsCOMPtr<nsIEventTarget> target = EventTargetFor(TaskCategory::Other); + return target.forget(); +} + +nsresult BrowserChildMessageManager::Dispatch( + TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) { + return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable)); +} + +nsISerialEventTarget* BrowserChildMessageManager::EventTargetFor( + TaskCategory aCategory) const { + return DispatcherTrait::EventTargetFor(aCategory); +} + +AbstractThread* BrowserChildMessageManager::AbstractMainThreadFor( + TaskCategory aCategory) { + return DispatcherTrait::AbstractMainThreadFor(aCategory); +} |