/* -*- 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 "BrowserParent.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/EventForwards.h" #ifdef ACCESSIBILITY # include "mozilla/a11y/DocAccessibleParent.h" # include "mozilla/a11y/Platform.h" # include "mozilla/a11y/RemoteAccessibleBase.h" # include "nsAccessibilityService.h" #endif #include "mozilla/Components.h" #include "mozilla/dom/BrowserHost.h" #include "mozilla/dom/BrowserSessionStore.h" #include "mozilla/dom/BrowsingContextGroup.h" #include "mozilla/dom/CancelContentJSOptionsBinding.h" #include "mozilla/dom/ChromeMessageSender.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentProcessManager.h" #include "mozilla/dom/DataTransfer.h" #include "mozilla/dom/DataTransferItemList.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/indexedDB/ActorsParent.h" #include "mozilla/dom/PaymentRequestParent.h" #include "mozilla/dom/PointerEventHandler.h" #include "mozilla/dom/BrowserBridgeParent.h" #include "mozilla/dom/RemoteDragStartData.h" #include "mozilla/dom/RemoteWebProgressRequest.h" #include "mozilla/dom/SessionHistoryEntry.h" #include "mozilla/dom/SessionStoreParent.h" #include "mozilla/dom/UserActivation.h" #include "mozilla/EventStateManager.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/DataSurfaceHelpers.h" #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/IMEStateManager.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/layers/AsyncDragMetrics.h" #include "mozilla/layers/InputAPZContext.h" #include "mozilla/layout/RemoteLayerTreeOwner.h" #include "mozilla/LookAndFeel.h" #include "mozilla/Maybe.h" #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/NativeKeyBindingsType.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/net/CookieJarSettings.h" #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/ProcessHangMonitor.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/TextEventDispatcher.h" #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsDebug.h" #include "nsFocusManager.h" #include "nsFrameLoader.h" #include "nsFrameLoaderOwner.h" #include "nsFrameManager.h" #include "nsIBaseWindow.h" #include "nsIBrowser.h" #include "nsIBrowserController.h" #include "nsIContent.h" #include "nsICookieJarSettings.h" #include "nsIDocShell.h" #include "nsIDocShellTreeOwner.h" #include "nsImportModule.h" #include "nsIInterfaceRequestorUtils.h" #include "nsILoadInfo.h" #include "nsIPromptFactory.h" #include "nsIURI.h" #include "nsIWebBrowserChrome.h" #include "nsIWebProtocolHandlerRegistrar.h" #include "nsIWindowWatcher.h" #include "nsIXPConnect.h" #include "nsIXULBrowserWindow.h" #include "nsIAppWindow.h" #include "nsLayoutUtils.h" #include "nsQueryActor.h" #include "nsSHistory.h" #include "nsViewManager.h" #include "nsVariant.h" #include "nsIWidget.h" #include "nsNetUtil.h" #ifndef XP_WIN # include "nsJARProtocolHandler.h" #endif #include "nsPIDOMWindow.h" #include "nsPrintfCString.h" #include "nsQueryObject.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "PermissionMessageUtils.h" #include "StructuredCloneData.h" #include "ColorPickerParent.h" #include "FilePickerParent.h" #include "BrowserChild.h" #include "nsNetCID.h" #include "nsIAuthInformation.h" #include "nsIAuthPromptCallback.h" #include "nsAuthInformationHolder.h" #include "nsICancelable.h" #include "gfxUtils.h" #include "nsILoginManagerAuthPrompter.h" #include "nsPIWindowRoot.h" #include "nsReadableUtils.h" #include "nsIAuthPrompt2.h" #include "gfxDrawable.h" #include "ImageOps.h" #include "UnitTransforms.h" #include #include "mozilla/NullPrincipal.h" #include "mozilla/WebBrowserPersistDocumentParent.h" #include "ProcessPriorityManager.h" #include "nsString.h" #include "IHistory.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/ProfilerLabels.h" #include "MMPrinter.h" #include "mozilla/dom/CrashReport.h" #include "nsISecureBrowserUI.h" #include "nsIXULRuntime.h" #include "VsyncSource.h" #include "nsSubDocumentFrame.h" #ifdef XP_WIN # include "FxRWindowManager.h" #endif #if defined(XP_WIN) && defined(ACCESSIBILITY) # include "mozilla/a11y/AccessibleWrap.h" # include "mozilla/a11y/Compatibility.h" # include "mozilla/a11y/nsWinUtils.h" #endif #ifdef MOZ_ANDROID_HISTORY # include "GeckoViewHistory.h" #endif #if defined(MOZ_WIDGET_ANDROID) # include "mozilla/widget/nsWindow.h" #endif // defined(MOZ_WIDGET_ANDROID) using namespace mozilla::dom; using namespace mozilla::ipc; using namespace mozilla::layers; using namespace mozilla::layout; using namespace mozilla::services; using namespace mozilla::widget; using namespace mozilla::gfx; using mozilla::LazyLogModule; extern mozilla::LazyLogModule gSHIPBFCacheLog; LazyLogModule gBrowserFocusLog("BrowserFocus"); #define LOGBROWSERFOCUS(args) \ MOZ_LOG(gBrowserFocusLog, mozilla::LogLevel::Debug, args) /* static */ BrowserParent* BrowserParent::sFocus = nullptr; /* static */ BrowserParent* BrowserParent::sTopLevelWebFocus = nullptr; /* static */ BrowserParent* BrowserParent::sLastMouseRemoteTarget = nullptr; // The flags passed by the webProgress notifications are 16 bits shifted // from the ones registered by webProgressListeners. #define NOTIFY_FLAG_SHIFT 16 namespace mozilla { /** * Store data of a keypress event which is requesting to handled it in a remote * process or some remote processes. */ class RequestingAccessKeyEventData { public: RequestingAccessKeyEventData() = delete; static void OnBrowserParentCreated() { MOZ_ASSERT(sBrowserParentCount <= INT32_MAX); sBrowserParentCount++; } static void OnBrowserParentDestroyed() { MOZ_ASSERT(sBrowserParentCount > 0); sBrowserParentCount--; // To avoid memory leak, we need to reset sData when the last BrowserParent // is destroyed. if (!sBrowserParentCount) { Clear(); } } static void Set(const WidgetKeyboardEvent& aKeyPressEvent) { MOZ_ASSERT(aKeyPressEvent.mMessage == eKeyPress); MOZ_ASSERT(sBrowserParentCount > 0); sData = Some(Data{aKeyPressEvent.mAlternativeCharCodes, aKeyPressEvent.mKeyCode, aKeyPressEvent.mCharCode, aKeyPressEvent.mKeyNameIndex, aKeyPressEvent.mCodeNameIndex, aKeyPressEvent.mKeyValue, aKeyPressEvent.mModifiers}); } static void Clear() { sData.reset(); } [[nodiscard]] static bool Equals(const WidgetKeyboardEvent& aKeyPressEvent) { MOZ_ASSERT(sBrowserParentCount > 0); return sData.isSome() && sData->Equals(aKeyPressEvent); } [[nodiscard]] static bool IsSet() { MOZ_ASSERT(sBrowserParentCount > 0); return sData.isSome(); } private: struct Data { [[nodiscard]] bool Equals(const WidgetKeyboardEvent& aKeyPressEvent) { return mKeyCode == aKeyPressEvent.mKeyCode && mCharCode == aKeyPressEvent.mCharCode && mKeyNameIndex == aKeyPressEvent.mKeyNameIndex && mCodeNameIndex == aKeyPressEvent.mCodeNameIndex && mKeyValue == aKeyPressEvent.mKeyValue && mModifiers == aKeyPressEvent.mModifiers && mAlternativeCharCodes == aKeyPressEvent.mAlternativeCharCodes; } CopyableTArray mAlternativeCharCodes; uint32_t mKeyCode; uint32_t mCharCode; KeyNameIndex mKeyNameIndex; CodeNameIndex mCodeNameIndex; nsString mKeyValue; Modifiers mModifiers; }; static Maybe sData; static int32_t sBrowserParentCount; }; int32_t RequestingAccessKeyEventData::sBrowserParentCount = 0; Maybe RequestingAccessKeyEventData::sData; namespace dom { BrowserParent::LayerToBrowserParentTable* BrowserParent::sLayerToBrowserParentTable = nullptr; NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserParent) NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_WEAK(BrowserParent, mFrameLoader, mBrowsingContext) NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserParent) NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserParent) BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId, const TabContext& aContext, CanonicalBrowsingContext* aBrowsingContext, uint32_t aChromeFlags) : TabContext(aContext), mTabId(aTabId), mManager(aManager), mBrowsingContext(aBrowsingContext), mFrameElement(nullptr), mBrowserDOMWindow(nullptr), mFrameLoader(nullptr), mChromeFlags(aChromeFlags), mBrowserBridgeParent(nullptr), mBrowserHost(nullptr), mContentCache(*this), mRemoteLayerTreeOwner{}, mLayerTreeEpoch{1}, mChildToParentConversionMatrix{}, mRect(0, 0, 0, 0), mDimensions(0, 0), mDPI(0), mRounding(0), mDefaultScale(0), mUpdatedDimensions(false), mSizeMode(nsSizeMode_Normal), mClientOffset{}, mChromeOffset{}, mCreatingWindow(false), mDelayedFrameScripts{}, mVsyncParent(nullptr), mMarkedDestroying(false), mIsDestroyed(false), mRemoteTargetSetsCursor(false), mIsPreservingLayers(false), mRenderLayers(true), mPriorityHint(false), mHasLayers(false), mHasPresented(false), mIsReadyToHandleInputEvents(false), mIsMouseEnterIntoWidgetEventSuppressed(false), mLockedNativePointer(false), mShowingTooltip(false) { MOZ_ASSERT(aManager); RequestingAccessKeyEventData::OnBrowserParentCreated(); // When the input event queue is disabled, we don't need to handle the case // that some input events are dispatched before PBrowserConstructor. mIsReadyToHandleInputEvents = !ContentParent::IsInputEventQueueSupported(); // Make sure to compute our process priority if needed before the block of // code below. This makes sure the block below prioritizes our process if // needed. if (aBrowsingContext->IsTop()) { RecomputeProcessPriority(); } // If we're in a BC tree that is active with respect to the priority manager, // ensure that this new BrowserParent is marked as active. This ensures that // the process will be prioritized in a cross-site iframe navigation in an // active tab, and also that the process is correctly prioritized if we got // created for a browsing context which was already active. if (aBrowsingContext->Top()->IsPriorityActive()) { ProcessPriorityManager::BrowserPriorityChanged(this, true); } } BrowserParent::~BrowserParent() { RequestingAccessKeyEventData::OnBrowserParentDestroyed(); } /* static */ BrowserParent* BrowserParent::GetFocused() { return sFocus; } /* static */ BrowserParent* BrowserParent::GetLastMouseRemoteTarget() { return sLastMouseRemoteTarget; } /*static*/ BrowserParent* BrowserParent::GetFrom(nsFrameLoader* aFrameLoader) { if (!aFrameLoader) { return nullptr; } return aFrameLoader->GetBrowserParent(); } /*static*/ BrowserParent* BrowserParent::GetFrom(PBrowserParent* aBrowserParent) { return static_cast(aBrowserParent); } /*static*/ BrowserParent* BrowserParent::GetFrom(nsIContent* aContent) { RefPtr loaderOwner = do_QueryObject(aContent); if (!loaderOwner) { return nullptr; } RefPtr frameLoader = loaderOwner->GetFrameLoader(); return GetFrom(frameLoader); } /* static */ BrowserParent* BrowserParent::GetBrowserParentFromLayersId( layers::LayersId aLayersId) { if (!sLayerToBrowserParentTable) { return nullptr; } return sLayerToBrowserParentTable->Get(uint64_t(aLayersId)); } /*static*/ TabId BrowserParent::GetTabIdFrom(nsIDocShell* docShell) { nsCOMPtr browserChild(BrowserChild::GetFrom(docShell)); if (browserChild) { return static_cast(browserChild.get())->GetTabId(); } return TabId(0); } void BrowserParent::AddBrowserParentToTable(layers::LayersId aLayersId, BrowserParent* aBrowserParent) { if (!sLayerToBrowserParentTable) { sLayerToBrowserParentTable = new LayerToBrowserParentTable(); } sLayerToBrowserParentTable->InsertOrUpdate(uint64_t(aLayersId), aBrowserParent); } void BrowserParent::RemoveBrowserParentFromTable(layers::LayersId aLayersId) { if (!sLayerToBrowserParentTable) { return; } sLayerToBrowserParentTable->Remove(uint64_t(aLayersId)); if (sLayerToBrowserParentTable->Count() == 0) { delete sLayerToBrowserParentTable; sLayerToBrowserParentTable = nullptr; } } already_AddRefed BrowserParent::GetLoadContext() { return do_AddRef(mBrowsingContext); } /** * Will return nullptr if there is no outer window available for the * document hosting the owner element of this BrowserParent. Also will return * nullptr if that outer window is in the process of closing. */ already_AddRefed BrowserParent::GetParentWindowOuter() { nsCOMPtr frame = GetOwnerElement(); if (!frame) { return nullptr; } nsCOMPtr parent = frame->OwnerDoc()->GetWindow(); if (!parent || parent->Closed()) { return nullptr; } return parent.forget(); } already_AddRefed BrowserParent::GetTopLevelWidget() { if (RefPtr element = mFrameElement) { if (PresShell* presShell = element->OwnerDoc()->GetPresShell()) { return do_AddRef(presShell->GetViewManager()->GetRootWidget()); } } return nullptr; } already_AddRefed BrowserParent::GetTextInputHandlingWidget() const { if (!mFrameElement) { return nullptr; } PresShell* presShell = mFrameElement->OwnerDoc()->GetPresShell(); if (!presShell) { return nullptr; } nsPresContext* presContext = presShell->GetPresContext(); if (!presContext) { return nullptr; } nsCOMPtr widget = presContext->GetTextInputHandlingWidget(); return widget.forget(); } already_AddRefed BrowserParent::GetWidget() const { if (!mFrameElement) { return nullptr; } nsCOMPtr widget = nsContentUtils::WidgetForContent(mFrameElement); if (!widget) { widget = nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc()); } return widget.forget(); } already_AddRefed BrowserParent::GetDocWidget() const { if (!mFrameElement) { return nullptr; } return do_AddRef( nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc())); } nsIXULBrowserWindow* BrowserParent::GetXULBrowserWindow() { if (!mFrameElement) { return nullptr; } nsCOMPtr docShell = mFrameElement->OwnerDoc()->GetDocShell(); if (!docShell) { return nullptr; } nsCOMPtr treeOwner; docShell->GetTreeOwner(getter_AddRefs(treeOwner)); if (!treeOwner) { return nullptr; } nsCOMPtr window = do_GetInterface(treeOwner); if (!window) { return nullptr; } nsCOMPtr xulBrowserWindow; window->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow)); return xulBrowserWindow; } uint32_t BrowserParent::GetMaxTouchPoints(Element* aElement) { if (!aElement) { return 0; } if (StaticPrefs::dom_maxtouchpoints_testing_value() >= 0) { return StaticPrefs::dom_maxtouchpoints_testing_value(); } nsIWidget* widget = nsContentUtils::WidgetForDocument(aElement->OwnerDoc()); return widget ? widget->GetMaxTouchPoints() : 0; } a11y::DocAccessibleParent* BrowserParent::GetTopLevelDocAccessible() const { #ifdef ACCESSIBILITY // XXX Consider managing non top level PDocAccessibles with their parent // document accessible. const ManagedContainer& docs = ManagedPDocAccessibleParent(); for (auto* key : docs) { auto* doc = static_cast(key); // We want the document for this BrowserParent even if it's for an // embedded out-of-process iframe. Therefore, we use // IsTopLevelInContentProcess. In contrast, using IsToplevel would only // include documents that aren't embedded; e.g. tab documents. if (doc->IsTopLevelInContentProcess() && !doc->IsShutdown()) { return doc; } } #endif return nullptr; } LayersId BrowserParent::GetLayersId() const { if (!mRemoteLayerTreeOwner.IsInitialized()) { return LayersId{}; } return mRemoteLayerTreeOwner.GetLayersId(); } BrowserBridgeParent* BrowserParent::GetBrowserBridgeParent() const { return mBrowserBridgeParent; } BrowserHost* BrowserParent::GetBrowserHost() const { return mBrowserHost; } ParentShowInfo BrowserParent::GetShowInfo() { TryCacheDPIAndScale(); if (mFrameElement) { nsAutoString name; mFrameElement->GetAttr(nsGkAtoms::name, name); bool isTransparent = nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) && mFrameElement->HasAttr(nsGkAtoms::transparent); return ParentShowInfo(name, false, isTransparent, mDPI, mRounding, mDefaultScale.scale); } return ParentShowInfo(u""_ns, false, false, mDPI, mRounding, mDefaultScale.scale); } already_AddRefed BrowserParent::GetContentPrincipal() const { nsCOMPtr browser = mFrameElement ? mFrameElement->AsBrowser() : nullptr; NS_ENSURE_TRUE(browser, nullptr); RefPtr principal; nsresult rv; rv = browser->GetContentPrincipal(getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, nullptr); return principal.forget(); } void BrowserParent::SetOwnerElement(Element* aElement) { // If we held previous content then unregister for its events. RemoveWindowListeners(); // If we change top-level documents then we need to change our // registration with them. RefPtr curTopLevelWin, newTopLevelWin; if (mFrameElement) { curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc()); } if (aElement) { newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc()); } bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin; if (mBrowserHost && curTopLevelWin && !isSameTopLevelWin) { curTopLevelWin->RemoveBrowser(mBrowserHost); } // Update to the new content, and register to listen for events from it. mFrameElement = aElement; if (mBrowserHost && newTopLevelWin && !isSameTopLevelWin) { newTopLevelWin->AddBrowser(mBrowserHost); } #if defined(XP_WIN) && defined(ACCESSIBILITY) if (!mIsDestroyed) { uintptr_t newWindowHandle = 0; if (nsCOMPtr widget = GetWidget()) { newWindowHandle = reinterpret_cast(widget->GetNativeData(NS_NATIVE_WINDOW)); } Unused << SendUpdateNativeWindowHandle(newWindowHandle); a11y::DocAccessibleParent* doc = GetTopLevelDocAccessible(); if (doc) { HWND hWnd = reinterpret_cast(doc->GetEmulatedWindowHandle()); if (hWnd) { HWND parentHwnd = reinterpret_cast(newWindowHandle); if (parentHwnd != ::GetParent(hWnd)) { ::SetParent(hWnd, parentHwnd); } } } } #endif AddWindowListeners(); // The DPI depends on our frame element's widget, so invalidate now in case // we've tried to cache it already. mDPI = -1; TryCacheDPIAndScale(); if (mRemoteLayerTreeOwner.IsInitialized()) { mRemoteLayerTreeOwner.OwnerContentChanged(); } // Set our BrowsingContext's embedder if we're not embedded within a // BrowserBridgeParent. if (!GetBrowserBridgeParent() && mBrowsingContext && mFrameElement) { mBrowsingContext->SetEmbedderElement(mFrameElement); } UpdateVsyncParentVsyncDispatcher(); VisitChildren([aElement](BrowserBridgeParent* aBrowser) { if (auto* browserParent = aBrowser->GetBrowserParent()) { browserParent->SetOwnerElement(aElement); } }); } void BrowserParent::CacheFrameLoader(nsFrameLoader* aFrameLoader) { mFrameLoader = aFrameLoader; } void BrowserParent::AddWindowListeners() { if (mFrameElement) { if (nsCOMPtr window = mFrameElement->OwnerDoc()->GetWindow()) { nsCOMPtr eventTarget = window->GetTopWindowRoot(); if (eventTarget) { eventTarget->AddEventListener(u"MozUpdateWindowPos"_ns, this, false, false); eventTarget->AddEventListener(u"fullscreenchange"_ns, this, false, false); } } } } void BrowserParent::RemoveWindowListeners() { if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) { nsCOMPtr window = mFrameElement->OwnerDoc()->GetWindow(); nsCOMPtr eventTarget = window->GetTopWindowRoot(); if (eventTarget) { eventTarget->RemoveEventListener(u"MozUpdateWindowPos"_ns, this, false); eventTarget->RemoveEventListener(u"fullscreenchange"_ns, this, false); } } } void BrowserParent::Deactivated() { if (mShowingTooltip) { // Reuse the normal tooltip hiding method. mozilla::Unused << RecvHideTooltip(); } UnlockNativePointer(); UnsetTopLevelWebFocus(this); UnsetLastMouseRemoteTarget(this); PointerLockManager::ReleaseLockedRemoteTarget(this); PointerEventHandler::ReleasePointerCaptureRemoteTarget(this); PresShell::ReleaseCapturingRemoteTarget(this); ProcessPriorityManager::BrowserPriorityChanged(this, /* aPriority = */ false); } void BrowserParent::DestroyInternal() { Deactivated(); RemoveWindowListeners(); #ifdef ACCESSIBILITY if (a11y::DocAccessibleParent* tabDoc = GetTopLevelDocAccessible()) { # if defined(ANDROID) MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); # endif tabDoc->Destroy(); } #endif // If this fails, it's most likely due to a content-process crash, // and auto-cleanup will kick in. Otherwise, the child side will // destroy itself and send back __delete__(). Unused << SendDestroy(); } void BrowserParent::Destroy() { // Aggressively release the window to avoid leaking the world in shutdown // corner cases. mBrowserDOMWindow = nullptr; if (mIsDestroyed) { return; } // If we are shutting down everything or we know to be the last // BrowserParent, signal the impending shutdown early to the content process // to avoid to run the SendDestroy before we know we are ExpectingShutdown. Manager()->NotifyTabWillDestroy(); DestroyInternal(); mIsDestroyed = true; Manager()->NotifyTabDestroying(); // This `AddKeepAlive` will be cleared if `mMarkedDestroying` is set in // `ActorDestroy`. Out of caution, we don't add the `KeepAlive` if our IPC // actor has somehow already been destroyed, as that would mean `ActorDestroy` // won't be called. if (CanRecv()) { mBrowsingContext->Group()->AddKeepAlive(); } mMarkedDestroying = true; } mozilla::ipc::IPCResult BrowserParent::RecvDidUnsuppressPainting() { if (!mFrameElement) { return IPC_OK(); } nsSubDocumentFrame* subdocFrame = do_QueryFrame(mFrameElement->GetPrimaryFrame()); if (subdocFrame && subdocFrame->HasRetainedPaintData()) { subdocFrame->ClearRetainedPaintData(); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvEnsureLayersConnected( CompositorOptions* aCompositorOptions) { if (mRemoteLayerTreeOwner.IsInitialized()) { mRemoteLayerTreeOwner.EnsureLayersConnected(aCompositorOptions); } return IPC_OK(); } void BrowserParent::ActorDestroy(ActorDestroyReason why) { Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying); ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); if (cpm) { cpm->UnregisterRemoteFrame(mTabId); } if (mRemoteLayerTreeOwner.IsInitialized()) { auto layersId = mRemoteLayerTreeOwner.GetLayersId(); if (mFrameElement) { nsSubDocumentFrame* f = do_QueryFrame(mFrameElement->GetPrimaryFrame()); if (f && f->HasRetainedPaintData() && f->GetRemotePaintData().mLayersId == layersId) { f->ClearRetainedPaintData(); } } // It's important to unmap layers after the remote browser has been // destroyed, otherwise it may still send messages to the compositor which // will reject them, causing assertions. RemoveBrowserParentFromTable(layersId); mRemoteLayerTreeOwner.Destroy(); } // Even though BrowserParent::Destroy calls this, we need to do it here too in // case of a crash. Deactivated(); if (why == AbnormalShutdown) { // dom_reporting_header must also be enabled for the report to be sent. if (StaticPrefs::dom_reporting_crash_enabled()) { nsCOMPtr principal = GetContentPrincipal(); if (principal) { nsAutoCString crash_reason; CrashReporter::GetAnnotation(OtherPid(), CrashReporter::Annotation::MozCrashReason, crash_reason); // FIXME(arenevier): Find a less fragile way to identify that a crash // was caused by OOM bool is_oom = false; if (crash_reason == "OOM" || crash_reason == "OOM!" || StringBeginsWith(crash_reason, "[unhandlable oom]"_ns) || StringBeginsWith(crash_reason, "Unhandlable OOM"_ns)) { is_oom = true; } CrashReport::Deliver(principal, is_oom); } } } // If we were shutting down normally, we held a reference to our // BrowsingContextGroup in `BrowserParent::Destroy`. Clear that reference // here. if (mMarkedDestroying) { mBrowsingContext->Group()->RemoveKeepAlive(); } // Tell our embedder that the tab is now going away unless we're an // out-of-process iframe. RefPtr frameLoader = GetFrameLoader(true); if (frameLoader) { ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr); if (mBrowsingContext->IsTop()) { // If this is a top-level BrowsingContext, tell the frameloader it's time // to go away. Otherwise, this is a subframe crash, and we can keep the // frameloader around. frameLoader->DestroyComplete(); } // If this was a crash, tell our nsFrameLoader to fire crash events. if (why == AbnormalShutdown) { frameLoader->MaybeNotifyCrashed(mBrowsingContext, Manager()->ChildID(), GetIPCChannel()); } else if (why == ManagedEndpointDropped) { // If we instead failed due to a constructor error, don't include process // information, as the process did not crash. frameLoader->MaybeNotifyCrashed(mBrowsingContext, ContentParentId{}, nullptr); } } mFrameLoader = nullptr; // If we were destroyed due to our ManagedEndpoints being dropped, make a // point of showing the subframe crashed UI. We don't fire the full // `MaybeNotifyCrashed` codepath, as the entire process hasn't crashed on us, // and it may confuse the frontend. mBrowsingContext->BrowserParentDestroyed( this, why == AbnormalShutdown || why == ManagedEndpointDropped); } mozilla::ipc::IPCResult BrowserParent::RecvMoveFocus( const bool& aForward, const bool& aForDocumentNavigation) { LOGBROWSERFOCUS(("RecvMoveFocus %p, aForward: %d, aForDocumentNavigation: %d", this, aForward, aForDocumentNavigation)); BrowserBridgeParent* bridgeParent = GetBrowserBridgeParent(); if (bridgeParent) { mozilla::Unused << bridgeParent->SendMoveFocus(aForward, aForDocumentNavigation); return IPC_OK(); } RefPtr fm = nsFocusManager::GetFocusManager(); if (fm) { RefPtr dummy; uint32_t type = aForward ? (aForDocumentNavigation ? static_cast( nsIFocusManager::MOVEFOCUS_FORWARDDOC) : static_cast(nsIFocusManager::MOVEFOCUS_FORWARD)) : (aForDocumentNavigation ? static_cast( nsIFocusManager::MOVEFOCUS_BACKWARDDOC) : static_cast( nsIFocusManager::MOVEFOCUS_BACKWARD)); fm->MoveFocus(nullptr, mFrameElement, type, nsIFocusManager::FLAG_BYKEY, getter_AddRefs(dummy)); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvDropLinks( nsTArray&& aLinks) { nsCOMPtr browser = mFrameElement ? mFrameElement->AsBrowser() : nullptr; if (browser) { // Verify that links have not been modified by the child. If links have // not been modified then it's safe to load those links using the // SystemPrincipal. If they have been modified by web content, then // we use a NullPrincipal which still allows to load web links. bool loadUsingSystemPrincipal = true; if (aLinks.Length() != mVerifyDropLinks.Length()) { loadUsingSystemPrincipal = false; } for (uint32_t i = 0; i < aLinks.Length(); i++) { if (loadUsingSystemPrincipal) { if (!aLinks[i].Equals(mVerifyDropLinks[i])) { loadUsingSystemPrincipal = false; } } } mVerifyDropLinks.Clear(); nsCOMPtr triggeringPrincipal; if (loadUsingSystemPrincipal) { triggeringPrincipal = nsContentUtils::GetSystemPrincipal(); } else { triggeringPrincipal = NullPrincipal::CreateWithoutOriginAttributes(); } browser->DropLinks(aLinks, triggeringPrincipal); } return IPC_OK(); } bool BrowserParent::SendLoadRemoteScript(const nsAString& aURL, const bool& aRunInGlobalScope) { if (mCreatingWindow) { mDelayedFrameScripts.AppendElement( FrameScriptInfo(nsString(aURL), aRunInGlobalScope)); return true; } MOZ_ASSERT(mDelayedFrameScripts.IsEmpty()); return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope); } void BrowserParent::LoadURL(nsDocShellLoadState* aLoadState) { MOZ_ASSERT(aLoadState); MOZ_ASSERT(aLoadState->URI()); if (mIsDestroyed) { return; } if (mCreatingWindow) { // Don't send the message if the child wants to load its own URL. return; } Unused << SendLoadURL(WrapNotNull(aLoadState), GetShowInfo()); } void BrowserParent::ResumeLoad(uint64_t aPendingSwitchID) { MOZ_ASSERT(aPendingSwitchID != 0); if (NS_WARN_IF(mIsDestroyed)) { return; } Unused << SendResumeLoad(aPendingSwitchID, GetShowInfo()); } void BrowserParent::InitRendering() { if (mRemoteLayerTreeOwner.IsInitialized()) { return; } mRemoteLayerTreeOwner.Initialize(this); layers::LayersId layersId = mRemoteLayerTreeOwner.GetLayersId(); AddBrowserParentToTable(layersId, this); RefPtr frameLoader = GetFrameLoader(); if (frameLoader) { nsIFrame* frame = frameLoader->GetPrimaryFrameOfOwningContent(); if (frame) { frame->InvalidateFrame(); } } TextureFactoryIdentifier textureFactoryIdentifier; mRemoteLayerTreeOwner.GetTextureFactoryIdentifier(&textureFactoryIdentifier); Unused << SendInitRendering(textureFactoryIdentifier, layersId, mRemoteLayerTreeOwner.GetCompositorOptions(), mRemoteLayerTreeOwner.IsLayersConnected()); RefPtr widget = GetTopLevelWidget(); if (widget) { ScreenIntMargin safeAreaInsets = widget->GetSafeAreaInsets(); Unused << SendSafeAreaInsetsChanged(safeAreaInsets); } #if defined(MOZ_WIDGET_ANDROID) MOZ_ASSERT(widget); if (GetBrowsingContext()->IsTopContent()) { Unused << SendDynamicToolbarMaxHeightChanged( widget->GetDynamicToolbarMaxHeight()); } #endif } bool BrowserParent::AttachWindowRenderer() { return mRemoteLayerTreeOwner.AttachWindowRenderer(); } void BrowserParent::MaybeShowFrame() { RefPtr frameLoader = GetFrameLoader(); if (!frameLoader) { return; } frameLoader->MaybeShowFrame(); } bool BrowserParent::Show(const OwnerShowInfo& aOwnerInfo) { mDimensions = aOwnerInfo.size(); if (mIsDestroyed) { return false; } MOZ_ASSERT(mRemoteLayerTreeOwner.IsInitialized()); if (!mRemoteLayerTreeOwner.AttachWindowRenderer()) { return false; } mSizeMode = aOwnerInfo.sizeMode(); Unused << SendShow(GetShowInfo(), aOwnerInfo); return true; } mozilla::ipc::IPCResult BrowserParent::RecvSetDimensions( mozilla::DimensionRequest aRequest, const double& aScale) { NS_ENSURE_TRUE(mFrameElement, IPC_OK()); nsCOMPtr docShell = mFrameElement->OwnerDoc()->GetDocShell(); NS_ENSURE_TRUE(docShell, IPC_OK()); nsCOMPtr treeOwner; docShell->GetTreeOwner(getter_AddRefs(treeOwner)); nsCOMPtr treeOwnerAsWin = do_QueryInterface(treeOwner); NS_ENSURE_TRUE(treeOwnerAsWin, IPC_OK()); // `BrowserChild` only sends the values to actually be changed, see more // details in `BrowserChild::SetDimensions()`. // Note that `BrowserChild::SetDimensions()` may be called before receiving // our `SendUIResolutionChanged()` call. Therefore, if given each coordinate // shouldn't be ignored, we need to recompute it if DPI has been changed. // And also note that don't use `mDefaultScale.scale` here since it may be // different from the result of `GetWidgetCSSToDeviceScale()`. // NOTE(emilio): We use GetWidgetCSSToDeviceScale() because the old scale is a // widget scale, and we only use the current scale to scale up/down the // relevant values. CSSToLayoutDeviceScale oldScale((float)aScale); CSSToLayoutDeviceScale currentScale( (float)treeOwnerAsWin->GetWidgetCSSToDeviceScale()); if (oldScale != currentScale) { auto rescaleFunc = [&oldScale, ¤tScale](LayoutDeviceIntCoord& aVal) { aVal = (LayoutDeviceCoord(aVal) / oldScale * currentScale).Rounded(); }; aRequest.mX.apply(rescaleFunc); aRequest.mY.apply(rescaleFunc); aRequest.mWidth.apply(rescaleFunc); aRequest.mHeight.apply(rescaleFunc); } // treeOwner is the chrome tree owner, but we wan't the content tree owner. nsCOMPtr webBrowserChrome = do_GetInterface(treeOwner); NS_ENSURE_TRUE(webBrowserChrome, IPC_OK()); webBrowserChrome->SetDimensions(std::move(aRequest)); return IPC_OK(); } nsresult BrowserParent::UpdatePosition() { RefPtr frameLoader = GetFrameLoader(); if (!frameLoader) { return NS_OK; } nsIntRect windowDims; NS_ENSURE_SUCCESS(frameLoader->GetWindowDimensions(windowDims), NS_ERROR_FAILURE); // Avoid updating sizes here. windowDims.SizeTo(mRect.Size()); UpdateDimensions(windowDims, mDimensions); return NS_OK; } void BrowserParent::NotifyPositionUpdatedForContentsInPopup() { if (CanonicalBrowsingContext* bc = GetBrowsingContext()) { bc->PreOrderWalk([](BrowsingContext* aContext) { if (WindowGlobalParent* windowGlobalParent = aContext->Canonical()->GetCurrentWindowGlobal()) { if (RefPtr browserParent = windowGlobalParent->GetBrowserParent()) { browserParent->UpdatePosition(); } } }); } } void BrowserParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size) { if (mIsDestroyed) { return; } nsCOMPtr widget = GetWidget(); if (!widget) { NS_WARNING("No widget found in BrowserParent::UpdateDimensions"); return; } LayoutDeviceIntPoint clientOffset = GetClientOffset(); LayoutDeviceIntPoint chromeOffset = !GetBrowserBridgeParent() ? -GetChildProcessOffset() : LayoutDeviceIntPoint(); if (!mUpdatedDimensions || mDimensions != size || !mRect.IsEqualEdges(rect) || clientOffset != mClientOffset || chromeOffset != mChromeOffset) { mUpdatedDimensions = true; mRect = rect; mDimensions = size; mClientOffset = clientOffset; mChromeOffset = chromeOffset; Unused << SendUpdateDimensions(GetDimensionInfo()); UpdateNativePointerLockCenter(widget); } } DimensionInfo BrowserParent::GetDimensionInfo() { LayoutDeviceIntRect devicePixelRect = ViewAs( mRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims); LayoutDeviceIntSize devicePixelSize = ViewAs( mDimensions, PixelCastJustification::LayoutDeviceIsScreenForTabDims); CSSRect unscaledRect = devicePixelRect / mDefaultScale; CSSSize unscaledSize = devicePixelSize / mDefaultScale; DimensionInfo di(unscaledRect, unscaledSize, mClientOffset, mChromeOffset); return di; } void BrowserParent::UpdateNativePointerLockCenter(nsIWidget* aWidget) { if (!mLockedNativePointer) { return; } LayoutDeviceIntRect dims( {0, 0}, ViewAs( mDimensions, PixelCastJustification::LayoutDeviceIsScreenForTabDims)); aWidget->SetNativePointerLockCenter((dims + mChromeOffset).Center()); } void BrowserParent::SizeModeChanged(const nsSizeMode& aSizeMode) { if (!mIsDestroyed && aSizeMode != mSizeMode) { mSizeMode = aSizeMode; Unused << SendSizeModeChanged(aSizeMode); } } #if defined(MOZ_WIDGET_ANDROID) void BrowserParent::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) { if (!mIsDestroyed) { Unused << SendDynamicToolbarMaxHeightChanged(aHeight); } } void BrowserParent::DynamicToolbarOffsetChanged(ScreenIntCoord aOffset) { if (!mIsDestroyed) { Unused << SendDynamicToolbarOffsetChanged(aOffset); } } #endif void BrowserParent::HandleAccessKey(const WidgetKeyboardEvent& aEvent, nsTArray& aCharCodes) { if (!mIsDestroyed) { // Note that we don't need to mark aEvent is posted to a remote process // because the event may be dispatched to it as normal keyboard event. // Therefore, we should use local copy to send it. WidgetKeyboardEvent localEvent(aEvent); RequestingAccessKeyEventData::Set(localEvent); Unused << SendHandleAccessKey(localEvent, aCharCodes); } } void BrowserParent::Activate(uint64_t aActionId) { LOGBROWSERFOCUS(("Activate %p actionid: %" PRIu64, this, aActionId)); if (!mIsDestroyed) { SetTopLevelWebFocus(this); // Intentionally inside "if" Unused << SendActivate(aActionId); } } void BrowserParent::Deactivate(bool aWindowLowering, uint64_t aActionId) { LOGBROWSERFOCUS(("Deactivate %p actionid: %" PRIu64, this, aActionId)); if (!aWindowLowering) { UnsetTopLevelWebFocus(this); // Intentionally outside the next "if" } if (!mIsDestroyed) { Unused << SendDeactivate(aActionId); } } #ifdef ACCESSIBILITY a11y::PDocAccessibleParent* BrowserParent::AllocPDocAccessibleParent( PDocAccessibleParent* aParent, const uint64_t&, const MaybeDiscardedBrowsingContext&) { // Reference freed in DeallocPDocAccessibleParent. return a11y::DocAccessibleParent::New().take(); } bool BrowserParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent) { // Free reference from AllocPDocAccessibleParent. static_cast(aParent)->Release(); return true; } mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor( PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID, const MaybeDiscardedBrowsingContext& aBrowsingContext) { # if defined(ANDROID) MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); # endif auto doc = static_cast(aDoc); // If this tab is already shutting down just mark the new actor as shutdown // and ignore it. When the tab actor is destroyed it will be too. if (mIsDestroyed) { doc->MarkAsShutdown(); return IPC_OK(); } if (aParentDoc) { // Iframe document rendered in the same process as its embedder. // A document should never directly be the parent of another document. // There should always be an outer doc accessible child of the outer // document containing the child. MOZ_ASSERT(aParentID); if (!aParentID) { return IPC_FAIL_NO_REASON(this); } auto parentDoc = static_cast(aParentDoc); if (parentDoc->IsShutdown()) { // This can happen if parentDoc is an OOP iframe, but its embedder has // been destroyed. (DocAccessibleParent::Destroy destroys any child // documents.) The OOP iframe (and anything it embeds) will die soon // anyway, so mark this document as shutdown and ignore it. doc->MarkAsShutdown(); return IPC_OK(); } if (aBrowsingContext) { doc->SetBrowsingContext(aBrowsingContext.get_canonical()); } mozilla::ipc::IPCResult added = parentDoc->AddChildDoc(doc, aParentID); if (!added) { # ifdef DEBUG return added; # else return IPC_OK(); # endif } # ifdef XP_WIN if (a11y::nsWinUtils::IsWindowEmulationStarted()) { doc->SetEmulatedWindowHandle(parentDoc->GetEmulatedWindowHandle()); } # endif return IPC_OK(); } if (aBrowsingContext) { doc->SetBrowsingContext(aBrowsingContext.get_canonical()); } if (auto* bridge = GetBrowserBridgeParent()) { // Iframe document rendered in a different process to its embedder. // In this case, we don't get aParentDoc and aParentID. MOZ_ASSERT(!aParentDoc && !aParentID); doc->SetTopLevelInContentProcess(); a11y::ProxyCreated(doc); // It's possible the embedder accessible hasn't been set yet; e.g. // a hidden iframe. In that case, embedderDoc will be null and this will // be handled when the embedder is set. if (a11y::DocAccessibleParent* embedderDoc = bridge->GetEmbedderAccessibleDoc()) { mozilla::ipc::IPCResult added = embedderDoc->AddChildDoc(bridge); if (!added) { # ifdef DEBUG return added; # else return IPC_OK(); # endif } } return IPC_OK(); } else { // null aParentDoc means this document is at the top level in the child // process. That means it makes no sense to get an id for an accessible // that is its parent. MOZ_ASSERT(!aParentID); if (aParentID) { return IPC_FAIL_NO_REASON(this); } if (auto* prevTopLevel = GetTopLevelDocAccessible()) { // Sometimes, we can get a new top level DocAccessibleParent before the // old one gets destroyed. The old one will die pretty shortly anyway, // so just destroy it now. If we don't do this, GetTopLevelDocAccessible() // might return the wrong document for a short while. prevTopLevel->Destroy(); } doc->SetTopLevel(); a11y::DocManager::RemoteDocAdded(doc); # ifdef XP_WIN doc->MaybeInitWindowEmulation(); # endif } return IPC_OK(); } #endif already_AddRefed BrowserParent::AllocPFilePickerParent( const nsString& aTitle, const nsIFilePicker::Mode& aMode) { return MakeAndAddRef(aTitle, aMode); } already_AddRefed BrowserParent::AllocPSessionStoreParent() { RefPtr sessionStore = BrowserSessionStore::GetOrCreate(mBrowsingContext->Top()); if (!sessionStore) { return nullptr; } return do_AddRef(new SessionStoreParent(mBrowsingContext, sessionStore)); } IPCResult BrowserParent::RecvNewWindowGlobal( ManagedEndpoint&& aEndpoint, const WindowGlobalInit& aInit) { RefPtr browsingContext = CanonicalBrowsingContext::Get(aInit.context().mBrowsingContextId); if (!browsingContext) { return IPC_FAIL(this, "Cannot create for missing BrowsingContext"); } if (!aInit.principal()) { return IPC_FAIL(this, "Cannot create without valid principal"); } // Ensure we never load a document with a content principal in // the wrong type of webIsolated process EnumSet validationOptions = {}; nsCOMPtr docURI = aInit.documentURI(); if (docURI->SchemeIs("about") || docURI->SchemeIs("blob") || docURI->SchemeIs("chrome")) { // XXXckerschb TODO - Do not use SystemPrincipal for: // Bug 1700639: about:plugins // Bug 1699385: Remove allowSystem for blobs // Bug 1698087: chrome://devtools/content/shared/webextension-fallback.html // chrome reftests, e.g. // * chrome://reftest/content/writing-mode/ua-style-sheet-button-1a-ref.html // * chrome://reftest/content/xul-document-load/test003.xhtml // * chrome://reftest/content/forms/input/text/centering-1.xhtml validationOptions = {ContentParent::ValidatePrincipalOptions::AllowSystem}; } if (!mManager->ValidatePrincipal(aInit.principal(), validationOptions)) { ContentParent::LogAndAssertFailedPrincipalValidationInfo(aInit.principal(), __func__); } // Construct our new WindowGlobalParent, bind, and initialize it. RefPtr wgp = WindowGlobalParent::CreateDisconnected(aInit); BindPWindowGlobalEndpoint(std::move(aEndpoint), wgp); wgp->Init(); return IPC_OK(); } PVsyncParent* BrowserParent::AllocPVsyncParent() { MOZ_ASSERT(!mVsyncParent); mVsyncParent = new VsyncParent(); UpdateVsyncParentVsyncDispatcher(); return mVsyncParent.get(); } bool BrowserParent::DeallocPVsyncParent(PVsyncParent* aActor) { MOZ_ASSERT(aActor); mVsyncParent = nullptr; return true; } void BrowserParent::UpdateVsyncParentVsyncDispatcher() { if (!mVsyncParent) { return; } if (nsCOMPtr widget = GetWidget()) { RefPtr vsyncDispatcher = widget->GetVsyncDispatcher(); if (!vsyncDispatcher) { vsyncDispatcher = gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher(); } mVsyncParent->UpdateVsyncDispatcher(vsyncDispatcher); } } void BrowserParent::MouseEnterIntoWidget() { if (nsCOMPtr widget = GetWidget()) { // When we mouseenter the remote target, the remote target's cursor should // become the current cursor. When we mouseexit, we stop. mRemoteTargetSetsCursor = true; widget->SetCursor(mCursor); } // Mark that we have missed a mouse enter event, so that // the next mouse event will create a replacement mouse // enter event and send it to the child. mIsMouseEnterIntoWidgetEventSuppressed = true; } void BrowserParent::SendRealMouseEvent(WidgetMouseEvent& aEvent) { if (mIsDestroyed) { return; } // XXXedgar, if the synthesized mouse events could deliver to the correct // process directly (see // https://bugzilla.mozilla.org/show_bug.cgi?id=1549355), we probably don't // need to check mReason then. if (aEvent.mReason == WidgetMouseEvent::eReal) { if (aEvent.mMessage == eMouseExitFromWidget) { // Since we are leaving this remote target, so don't need to update // sLastMouseRemoteTarget, and if we are sLastMouseRemoteTarget, reset it // to null. BrowserParent::UnsetLastMouseRemoteTarget(this); } else { // Last remote target should not be changed without eMouseExitFromWidget. MOZ_ASSERT_IF(sLastMouseRemoteTarget, sLastMouseRemoteTarget == this); sLastMouseRemoteTarget = this; } } aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); nsCOMPtr widget = GetWidget(); if (widget) { // When we mouseenter the remote target, the remote target's cursor should // become the current cursor. When we mouseexit, we stop. if (eMouseEnterIntoWidget == aEvent.mMessage) { mRemoteTargetSetsCursor = true; widget->SetCursor(mCursor); } else if (eMouseExitFromWidget == aEvent.mMessage) { mRemoteTargetSetsCursor = false; } } if (!mIsReadyToHandleInputEvents) { if (eMouseEnterIntoWidget == aEvent.mMessage) { mIsMouseEnterIntoWidgetEventSuppressed = true; } else if (eMouseExitFromWidget == aEvent.mMessage) { mIsMouseEnterIntoWidgetEventSuppressed = false; } return; } ScrollableLayerGuid guid; uint64_t blockId; ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); bool isInputPriorityEventEnabled = Manager()->IsInputPriorityEventEnabled(); if (mIsMouseEnterIntoWidgetEventSuppressed) { // In the case that the BrowserParent suppressed the eMouseEnterWidget event // due to its corresponding BrowserChild wasn't ready to handle it, we have // to resend it when the BrowserChild is ready. mIsMouseEnterIntoWidgetEventSuppressed = false; WidgetMouseEvent localEvent(aEvent); localEvent.mMessage = eMouseEnterIntoWidget; DebugOnly ret = isInputPriorityEventEnabled ? SendRealMouseEnterExitWidgetEvent(localEvent, guid, blockId) : SendNormalPriorityRealMouseEnterExitWidgetEvent(localEvent, guid, blockId); NS_WARNING_ASSERTION(ret, "SendRealMouseEnterExitWidgetEvent() failed"); MOZ_ASSERT(!ret || localEvent.HasBeenPostedToRemoteProcess()); } if (eMouseMove == aEvent.mMessage) { if (aEvent.mReason == WidgetMouseEvent::eSynthesized) { DebugOnly ret = isInputPriorityEventEnabled ? SendSynthMouseMoveEvent(aEvent, guid, blockId) : SendNormalPrioritySynthMouseMoveEvent(aEvent, guid, blockId); NS_WARNING_ASSERTION(ret, "SendSynthMouseMoveEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); return; } if (!aEvent.mFlags.mIsSynthesizedForTests) { DebugOnly ret = isInputPriorityEventEnabled ? SendRealMouseMoveEvent(aEvent, guid, blockId) : SendNormalPriorityRealMouseMoveEvent(aEvent, guid, blockId); NS_WARNING_ASSERTION(ret, "SendRealMouseMoveEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); return; } DebugOnly ret = isInputPriorityEventEnabled ? SendRealMouseMoveEventForTests(aEvent, guid, blockId) : SendNormalPriorityRealMouseMoveEventForTests(aEvent, guid, blockId); NS_WARNING_ASSERTION(ret, "SendRealMouseMoveEventForTests() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); return; } if (eMouseEnterIntoWidget == aEvent.mMessage || eMouseExitFromWidget == aEvent.mMessage) { DebugOnly ret = isInputPriorityEventEnabled ? SendRealMouseEnterExitWidgetEvent(aEvent, guid, blockId) : SendNormalPriorityRealMouseEnterExitWidgetEvent(aEvent, guid, blockId); NS_WARNING_ASSERTION(ret, "SendRealMouseEnterExitWidgetEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); return; } DebugOnly ret = isInputPriorityEventEnabled ? SendRealMouseButtonEvent(aEvent, guid, blockId) : SendNormalPriorityRealMouseButtonEvent(aEvent, guid, blockId); NS_WARNING_ASSERTION(ret, "SendRealMouseButtonEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); } LayoutDeviceToCSSScale BrowserParent::GetLayoutDeviceToCSSScale() { Document* doc = (mFrameElement ? mFrameElement->OwnerDoc() : nullptr); nsPresContext* ctx = (doc ? doc->GetPresContext() : nullptr); return LayoutDeviceToCSSScale( ctx ? (float)ctx->AppUnitsPerDevPixel() / AppUnitsPerCSSPixel() : 0.0f); } bool BrowserParent::QueryDropLinksForVerification() { // Before sending the dragEvent, we query the links being dragged and // store them on the parent, to make sure the child can not modify links. nsCOMPtr dragSession = nsContentUtils::GetDragSession(); if (!dragSession) { NS_WARNING("No dragSession to query links for verification"); return false; } RefPtr initialDataTransfer = dragSession->GetDataTransfer(); if (!initialDataTransfer) { NS_WARNING("No initialDataTransfer to query links for verification"); return false; } nsCOMPtr dropHandler = do_GetService("@mozilla.org/content/dropped-link-handler;1"); if (!dropHandler) { NS_WARNING("No dropHandler to query links for verification"); return false; } // No more than one drop event can happen simultaneously; reset the link // verification array and store all links that are being dragged. mVerifyDropLinks.Clear(); nsTArray> droppedLinkItems; dropHandler->QueryLinks(initialDataTransfer, droppedLinkItems); // Since the entire event is cancelled if one of the links is invalid, // we can store all links on the parent side without any prior // validation checks. nsresult rv = NS_OK; for (nsIDroppedLinkItem* item : droppedLinkItems) { nsString tmp; rv = item->GetUrl(tmp); if (NS_FAILED(rv)) { NS_WARNING("Failed to query url for verification"); break; } mVerifyDropLinks.AppendElement(tmp); rv = item->GetName(tmp); if (NS_FAILED(rv)) { NS_WARNING("Failed to query name for verification"); break; } mVerifyDropLinks.AppendElement(tmp); rv = item->GetType(tmp); if (NS_FAILED(rv)) { NS_WARNING("Failed to query type for verification"); break; } mVerifyDropLinks.AppendElement(tmp); } if (NS_FAILED(rv)) { mVerifyDropLinks.Clear(); return false; } return true; } void BrowserParent::SendRealDragEvent(WidgetDragEvent& aEvent, uint32_t aDragAction, uint32_t aDropEffect, nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCsp) { if (mIsDestroyed || !mIsReadyToHandleInputEvents) { return; } MOZ_ASSERT(!Manager()->IsInputPriorityEventEnabled()); aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); if (aEvent.mMessage == eDrop) { if (!QueryDropLinksForVerification()) { return; } } DebugOnly ret = PBrowserParent::SendRealDragEvent( aEvent, aDragAction, aDropEffect, aPrincipal, aCsp); NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealDragEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); } void BrowserParent::SendMouseWheelEvent(WidgetWheelEvent& aEvent) { if (mIsDestroyed || !mIsReadyToHandleInputEvents) { return; } ScrollableLayerGuid guid; uint64_t blockId; ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); DebugOnly ret = Manager()->IsInputPriorityEventEnabled() ? PBrowserParent::SendMouseWheelEvent(aEvent, guid, blockId) : PBrowserParent::SendNormalPriorityMouseWheelEvent(aEvent, guid, blockId); NS_WARNING_ASSERTION(ret, "PBrowserParent::SendMouseWheelEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); } mozilla::ipc::IPCResult BrowserParent::RecvDispatchWheelEvent( const mozilla::WidgetWheelEvent& aEvent) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); nsCOMPtr widget = GetWidget(); if (!widget) { return IPC_OK(); } WidgetWheelEvent localEvent(aEvent); localEvent.mWidget = widget; localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint); widget->DispatchInputEvent(&localEvent); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvDispatchMouseEvent( const mozilla::WidgetMouseEvent& aEvent) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); nsCOMPtr widget = GetWidget(); if (!widget) { return IPC_OK(); } WidgetMouseEvent localEvent(aEvent); localEvent.mWidget = widget; localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint); widget->DispatchInputEvent(&localEvent); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvDispatchKeyboardEvent( const mozilla::WidgetKeyboardEvent& aEvent) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); nsCOMPtr widget = GetWidget(); if (!widget) { return IPC_OK(); } WidgetKeyboardEvent localEvent(aEvent); localEvent.mWidget = widget; localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint); widget->DispatchInputEvent(&localEvent); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvDispatchTouchEvent( const mozilla::WidgetTouchEvent& aEvent) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); nsCOMPtr widget = GetWidget(); if (!widget) { return IPC_OK(); } WidgetTouchEvent localEvent(aEvent); localEvent.mWidget = widget; for (uint32_t i = 0; i < localEvent.mTouches.Length(); i++) { localEvent.mTouches[i]->mRefPoint = TransformChildToParent(localEvent.mTouches[i]->mRefPoint); } widget->DispatchInputEvent(&localEvent); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvRequestNativeKeyBindings( const uint32_t& aType, const WidgetKeyboardEvent& aEvent, nsTArray* aCommands) { MOZ_ASSERT(aCommands); MOZ_ASSERT(aCommands->IsEmpty()); NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); NativeKeyBindingsType keyBindingsType = static_cast(aType); switch (keyBindingsType) { case NativeKeyBindingsType::SingleLineEditor: case NativeKeyBindingsType::MultiLineEditor: case NativeKeyBindingsType::RichTextEditor: break; default: return IPC_FAIL(this, "Invalid aType value"); } nsCOMPtr widget = GetWidget(); if (!widget) { return IPC_OK(); } WidgetKeyboardEvent localEvent(aEvent); localEvent.mWidget = widget; if (NS_FAILED(widget->AttachNativeKeyEvent(localEvent))) { return IPC_OK(); } Maybe writingMode; if (RefPtr dispatcher = widget->GetTextEventDispatcher()) { writingMode = dispatcher->MaybeQueryWritingModeAtSelection(); } if (localEvent.InitEditCommandsFor(keyBindingsType, writingMode)) { *aCommands = localEvent.EditCommandsConstRef(keyBindingsType).Clone(); } return IPC_OK(); } class SynthesizedEventObserver : public nsIObserver { NS_DECL_ISUPPORTS public: SynthesizedEventObserver(BrowserParent* aBrowserParent, const uint64_t& aObserverId) : mBrowserParent(aBrowserParent), mObserverId(aObserverId) { MOZ_ASSERT(mBrowserParent); } NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override { if (!mBrowserParent || !mObserverId) { // We already sent the notification, or we don't actually need to // send any notification at all. return NS_OK; } if (mBrowserParent->IsDestroyed()) { // If this happens it's probably a bug in the test that's triggering this. NS_WARNING( "BrowserParent was unexpectedly destroyed during event " "synthesization!"); } else if (!mBrowserParent->SendNativeSynthesisResponse( mObserverId, nsCString(aTopic))) { NS_WARNING("Unable to send native event synthesization response!"); } // Null out browserParent to indicate we already sent the response mBrowserParent = nullptr; return NS_OK; } private: virtual ~SynthesizedEventObserver() = default; RefPtr mBrowserParent; uint64_t mObserverId; }; NS_IMPL_ISUPPORTS(SynthesizedEventObserver, nsIObserver) class MOZ_STACK_CLASS AutoSynthesizedEventResponder { public: AutoSynthesizedEventResponder(BrowserParent* aBrowserParent, const uint64_t& aObserverId, const char* aTopic) : mObserver(new SynthesizedEventObserver(aBrowserParent, aObserverId)), mTopic(aTopic) {} ~AutoSynthesizedEventResponder() { // This may be a no-op if the observer already sent a response. mObserver->Observe(nullptr, mTopic, nullptr); } nsIObserver* GetObserver() { return mObserver; } private: nsCOMPtr mObserver; const char* mTopic; }; mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeKeyEvent( const int32_t& aNativeKeyboardLayout, const int32_t& aNativeKeyCode, const uint32_t& aModifierFlags, const nsString& aCharacters, const nsString& aUnmodifiedCharacters, const uint64_t& aObserverId) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); AutoSynthesizedEventResponder responder(this, aObserverId, "keyevent"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeKeyEvent( aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters, aUnmodifiedCharacters, responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseEvent( const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage, const int16_t& aButton, const uint32_t& aModifierFlags, const uint64_t& aObserverId) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); const uint32_t last = static_cast(nsIWidget::NativeMouseMessage::LeaveWindow); NS_ENSURE_TRUE(aNativeMessage <= last, IPC_FAIL(this, "Bogus message")); AutoSynthesizedEventResponder responder(this, aObserverId, "mouseevent"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeMouseEvent( aPoint, static_cast(aNativeMessage), static_cast(aButton), static_cast(aModifierFlags), responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseMove( const LayoutDeviceIntPoint& aPoint, const uint64_t& aObserverId) { // This is used by pointer lock API. So, even if it's not in the automation // mode, we need to accept the request. AutoSynthesizedEventResponder responder(this, aObserverId, "mousemove"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeMouseMove(aPoint, responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseScrollEvent( const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage, const double& aDeltaX, const double& aDeltaY, const double& aDeltaZ, const uint32_t& aModifierFlags, const uint32_t& aAdditionalFlags, const uint64_t& aObserverId) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); AutoSynthesizedEventResponder responder(this, aObserverId, "mousescrollevent"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeMouseScrollEvent( aPoint, aNativeMessage, aDeltaX, aDeltaY, aDeltaZ, aModifierFlags, aAdditionalFlags, responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchPoint( const uint32_t& aPointerId, const TouchPointerState& aPointerState, const LayoutDeviceIntPoint& aPoint, const double& aPointerPressure, const uint32_t& aPointerOrientation, const uint64_t& aObserverId) { // This is used by DevTools to emulate touch events from mouse events in the // responsive design mode. Therefore, we should accept the IPC messages even // if it's not in the automation mode but the browsing context is in RDM pane. // And the IPC message could be just delayed after closing the responsive // design mode. Therefore, we shouldn't return IPC_FAIL since doing it makes // the tab crash. if (!xpc::IsInAutomation()) { NS_ENSURE_TRUE(mBrowsingContext, IPC_OK()); NS_ENSURE_TRUE(mBrowsingContext->Top()->GetInRDMPane(), IPC_OK()); } AutoSynthesizedEventResponder responder(this, aObserverId, "touchpoint"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeTouchPoint(aPointerId, aPointerState, aPoint, aPointerPressure, aPointerOrientation, responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchPadPinch( const TouchpadGesturePhase& aEventPhase, const float& aScale, const LayoutDeviceIntPoint& aPoint, const int32_t& aModifierFlags) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeTouchPadPinch(aEventPhase, aScale, aPoint, aModifierFlags); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchTap( const LayoutDeviceIntPoint& aPoint, const bool& aLongTap, const uint64_t& aObserverId) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); AutoSynthesizedEventResponder responder(this, aObserverId, "touchtap"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeTouchTap(aPoint, aLongTap, responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvClearNativeTouchSequence( const uint64_t& aObserverId) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); AutoSynthesizedEventResponder responder(this, aObserverId, "cleartouch"); nsCOMPtr widget = GetWidget(); if (widget) { widget->ClearNativeTouchSequence(responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativePenInput( const uint32_t& aPointerId, const TouchPointerState& aPointerState, const LayoutDeviceIntPoint& aPoint, const double& aPressure, const uint32_t& aRotation, const int32_t& aTiltX, const int32_t& aTiltY, const int32_t& aButton, const uint64_t& aObserverId) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); AutoSynthesizedEventResponder responder(this, aObserverId, "peninput"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativePenInput(aPointerId, aPointerState, aPoint, aPressure, aRotation, aTiltX, aTiltY, aButton, responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchpadDoubleTap( const LayoutDeviceIntPoint& aPoint, const uint32_t& aModifierFlags) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeTouchpadDoubleTap(aPoint, aModifierFlags); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchpadPan( const TouchpadGesturePhase& aEventPhase, const LayoutDeviceIntPoint& aPoint, const double& aDeltaX, const double& aDeltaY, const int32_t& aModifierFlags, const uint64_t& aObserverId) { NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); AutoSynthesizedEventResponder responder(this, aObserverId, "touchpadpanevent"); nsCOMPtr widget = GetWidget(); if (widget) { widget->SynthesizeNativeTouchpadPan(aEventPhase, aPoint, aDeltaX, aDeltaY, aModifierFlags, responder.GetObserver()); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvLockNativePointer() { if (nsCOMPtr widget = GetWidget()) { mLockedNativePointer = true; // do before updating the center UpdateNativePointerLockCenter(widget); widget->LockNativePointer(); } return IPC_OK(); } void BrowserParent::UnlockNativePointer() { if (!mLockedNativePointer) { return; } if (nsCOMPtr widget = GetWidget()) { widget->UnlockNativePointer(); mLockedNativePointer = false; } } mozilla::ipc::IPCResult BrowserParent::RecvUnlockNativePointer() { UnlockNativePointer(); return IPC_OK(); } void BrowserParent::SendRealKeyEvent(WidgetKeyboardEvent& aEvent) { if (mIsDestroyed || !mIsReadyToHandleInputEvents) { return; } aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); // NOTE: If you call `InitAllEditCommands()` for the other messages too, // you also need to update // TextEventDispatcher::DispatchKeyboardEventInternal(). if (aEvent.mMessage == eKeyPress) { // If current input context is editable, the edit commands are initialized // by TextEventDispatcher::DispatchKeyboardEventInternal(). Otherwise, // we need to do it here (they are not necessary for the parent process, // therefore, we need to do it here for saving the runtime cost). if (!aEvent.AreAllEditCommandsInitialized()) { // XXX Is it good thing that the keypress event will be handled in an // editor even though the user pressed the key combination before the // focus change has not been completed in the parent process yet or // focus change will happen? If no, we can stop doing this. Maybe writingMode; if (aEvent.mWidget) { if (RefPtr dispatcher = aEvent.mWidget->GetTextEventDispatcher()) { writingMode = dispatcher->MaybeQueryWritingModeAtSelection(); } } aEvent.InitAllEditCommands(writingMode); } } else { aEvent.PreventNativeKeyBindings(); } SentKeyEventData sendKeyEventData{ aEvent.mKeyCode, aEvent.mCharCode, aEvent.mPseudoCharCode, aEvent.mKeyNameIndex, aEvent.mCodeNameIndex, aEvent.mModifiers, nsID::GenerateUUID()}; const bool ok = Manager()->IsInputPriorityEventEnabled() ? PBrowserParent::SendRealKeyEvent(aEvent, sendKeyEventData.mUUID) : PBrowserParent::SendNormalPriorityRealKeyEvent( aEvent, sendKeyEventData.mUUID); NS_WARNING_ASSERTION(ok, "PBrowserParent::SendRealKeyEvent() failed"); MOZ_ASSERT(!ok || aEvent.HasBeenPostedToRemoteProcess()); if (ok && aEvent.IsWaitingReplyFromRemoteProcess()) { mWaitingReplyKeyboardEvents.AppendElement(sendKeyEventData); } } void BrowserParent::SendRealTouchEvent(WidgetTouchEvent& aEvent) { if (mIsDestroyed || !mIsReadyToHandleInputEvents) { return; } // PresShell::HandleEventInternal adds touches on touch end/cancel. This // confuses remote content and the panning and zooming logic into thinking // that the added touches are part of the touchend/cancel, when actually // they're not. if (aEvent.mMessage == eTouchEnd || aEvent.mMessage == eTouchCancel) { aEvent.mTouches.RemoveElementsBy( [](const auto& touch) { return !touch->mChanged; }); } APZData apzData; ApzAwareEventRoutingToChild(&apzData.guid, &apzData.blockId, &apzData.apzResponse); if (mIsDestroyed) { return; } for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) { aEvent.mTouches[i]->mRefPoint = TransformParentToChild(aEvent.mTouches[i]->mRefPoint); } static uint32_t sConsecutiveTouchMoveCount = 0; if (aEvent.mMessage == eTouchMove) { ++sConsecutiveTouchMoveCount; SendRealTouchMoveEvent(aEvent, apzData, sConsecutiveTouchMoveCount); return; } sConsecutiveTouchMoveCount = 0; DebugOnly ret = Manager()->IsInputPriorityEventEnabled() ? PBrowserParent::SendRealTouchEvent( aEvent, apzData.guid, apzData.blockId, apzData.apzResponse) : PBrowserParent::SendNormalPriorityRealTouchEvent( aEvent, apzData.guid, apzData.blockId, apzData.apzResponse); NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealTouchEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); } void BrowserParent::SendRealTouchMoveEvent( WidgetTouchEvent& aEvent, APZData& aAPZData, uint32_t aConsecutiveTouchMoveCount) { // Touchmove handling is complicated, since IPC compression should be used // only when there are consecutive touch objects for the same touch on the // same BrowserParent. IPC compression can be disabled by switching to // different IPC message. static bool sIPCMessageType1 = true; static TabId sLastTargetBrowserParent(0); static Maybe sPreviousAPZData; // Artificially limit max touch points to 10. That should be in practise // more than enough. const uint32_t kMaxTouchMoveIdentifiers = 10; static Maybe sLastTouchMoveIdentifiers[kMaxTouchMoveIdentifiers]; // Returns true if aIdentifiers contains all the touches in // sLastTouchMoveIdentifiers. auto LastTouchMoveIdentifiersContainedIn = [&](const nsTArray& aIdentifiers) -> bool { for (Maybe& entry : sLastTouchMoveIdentifiers) { if (entry.isSome() && !aIdentifiers.Contains(entry.value())) { return false; } } return true; }; // Cache touch identifiers in sLastTouchMoveIdentifiers array to be used // when checking whether compression can be done for the next touchmove. auto SetLastTouchMoveIdentifiers = [&](const nsTArray& aIdentifiers) { for (Maybe& entry : sLastTouchMoveIdentifiers) { entry.reset(); } MOZ_ASSERT(aIdentifiers.Length() <= kMaxTouchMoveIdentifiers); for (uint32_t j = 0; j < aIdentifiers.Length(); ++j) { sLastTouchMoveIdentifiers[j].emplace(aIdentifiers[j]); } }; AutoTArray changedTouches; bool preventCompression = !StaticPrefs::dom_events_compress_touchmove() || // Ensure the very first touchmove isn't overridden // by the second one, so that web pages can get // accurate coordinates for the first touchmove. aConsecutiveTouchMoveCount < 3 || sPreviousAPZData.isNothing() || sPreviousAPZData.value() != aAPZData || sLastTargetBrowserParent != GetTabId() || aEvent.mTouches.Length() > kMaxTouchMoveIdentifiers; if (!preventCompression) { for (RefPtr& touch : aEvent.mTouches) { if (touch->mChanged) { changedTouches.AppendElement(touch->mIdentifier); } } // Prevent compression if the new event has fewer or different touches // than the old one. preventCompression = !LastTouchMoveIdentifiersContainedIn(changedTouches); } if (preventCompression) { sIPCMessageType1 = !sIPCMessageType1; } // Update the last touch move identifiers always, so that when the next // event comes in, the new identifiers can be compared to the old ones. // If the pref is disabled, this just does a quick small loop. SetLastTouchMoveIdentifiers(changedTouches); sPreviousAPZData.reset(); sPreviousAPZData.emplace(aAPZData); sLastTargetBrowserParent = GetTabId(); DebugOnly ret = true; if (sIPCMessageType1) { ret = Manager()->IsInputPriorityEventEnabled() ? PBrowserParent::SendRealTouchMoveEvent( aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse) : PBrowserParent::SendNormalPriorityRealTouchMoveEvent( aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse); } else { ret = Manager()->IsInputPriorityEventEnabled() ? PBrowserParent::SendRealTouchMoveEvent2( aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse) : PBrowserParent::SendNormalPriorityRealTouchMoveEvent2( aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse); } NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealTouchMoveEvent() failed"); MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); } bool BrowserParent::SendHandleTap(TapType aType, const LayoutDevicePoint& aPoint, Modifiers aModifiers, const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId) { if (mIsDestroyed || !mIsReadyToHandleInputEvents) { return false; } if ((aType == TapType::eSingleTap || aType == TapType::eSecondTap)) { if (RefPtr fm = nsFocusManager::GetFocusManager()) { if (RefPtr frameLoader = GetFrameLoader()) { if (RefPtr element = frameLoader->GetOwnerContent()) { fm->SetFocus(element, nsIFocusManager::FLAG_BYMOUSE | nsIFocusManager::FLAG_BYTOUCH | nsIFocusManager::FLAG_NOSCROLL); } } } } return Manager()->IsInputPriorityEventEnabled() ? PBrowserParent::SendHandleTap(aType, TransformParentToChild(aPoint), aModifiers, aGuid, aInputBlockId) : PBrowserParent::SendNormalPriorityHandleTap( aType, TransformParentToChild(aPoint), aModifiers, aGuid, aInputBlockId); } mozilla::ipc::IPCResult BrowserParent::RecvSyncMessage( const nsString& aMessage, const ClonedMessageData& aData, nsTArray* aRetVal) { AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserParent::RecvSyncMessage", OTHER, aMessage); MMPrinter::Print("BrowserParent::RecvSyncMessage", aMessage, aData); StructuredCloneData data; ipc::UnpackClonedMessageData(aData, data); if (!ReceiveMessage(aMessage, true, &data, aRetVal)) { return IPC_FAIL_NO_REASON(this); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvAsyncMessage( const nsString& aMessage, const ClonedMessageData& aData) { AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserParent::RecvAsyncMessage", OTHER, aMessage); MMPrinter::Print("BrowserParent::RecvAsyncMessage", aMessage, aData); StructuredCloneData data; ipc::UnpackClonedMessageData(aData, data); if (!ReceiveMessage(aMessage, false, &data, nullptr)) { return IPC_FAIL_NO_REASON(this); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSetCursor( const nsCursor& aCursor, const bool& aHasCustomCursor, Maybe&& aCursorData, const uint32_t& aWidth, const uint32_t& aHeight, const float& aResolutionX, const float& aResolutionY, const uint32_t& aStride, const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX, const uint32_t& aHotspotY, const bool& aForce) { nsCOMPtr widget = GetWidget(); if (!widget) { return IPC_OK(); } if (aForce) { widget->ClearCachedCursor(); } nsCOMPtr cursorImage; if (aHasCustomCursor) { const bool cursorDataValid = [&] { if (!aCursorData) { return false; } auto expectedSize = CheckedInt(aHeight) * aStride; if (!expectedSize.isValid() || expectedSize.value() != aCursorData->Size()) { return false; } auto minStride = CheckedInt(aWidth) * gfx::BytesPerPixel(aFormat); if (!minStride.isValid() || aStride < minStride.value()) { return false; } return true; }(); if (!cursorDataValid) { return IPC_FAIL(this, "Invalid custom cursor data"); } const gfx::IntSize size(aWidth, aHeight); RefPtr customCursor = gfx::CreateDataSourceSurfaceFromData(size, aFormat, aCursorData->Data(), aStride); RefPtr drawable = new gfxSurfaceDrawable(customCursor, size); cursorImage = image::ImageOps::CreateFromDrawable(drawable); } mCursor = nsIWidget::Cursor{aCursor, std::move(cursorImage), aHotspotX, aHotspotY, {aResolutionX, aResolutionY}}; if (!mRemoteTargetSetsCursor) { return IPC_OK(); } widget->SetCursor(mCursor); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvSetLinkStatus( const nsString& aStatus) { nsCOMPtr xulBrowserWindow = GetXULBrowserWindow(); if (!xulBrowserWindow) { return IPC_OK(); } xulBrowserWindow->SetOverLink(aStatus); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvShowTooltip( const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip, const nsString& aDirection) { nsCOMPtr xulBrowserWindow = GetXULBrowserWindow(); if (!xulBrowserWindow) { return IPC_OK(); } // ShowTooltip will end up accessing XULElement properties in JS (specifically // BoxObject). However, to get it to JS, we need to make sure we're a // nsFrameLoaderOwner, which implies we're a XULFrameElement. We can then // safely pass Element into JS. RefPtr flo = do_QueryObject(mFrameElement); if (!flo) return IPC_OK(); nsCOMPtr el = do_QueryInterface(flo); if (!el) return IPC_OK(); if (NS_SUCCEEDED( xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection, el))) { mShowingTooltip = true; } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvHideTooltip() { mShowingTooltip = false; nsCOMPtr xulBrowserWindow = GetXULBrowserWindow(); if (!xulBrowserWindow) { return IPC_OK(); } xulBrowserWindow->HideTooltip(); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEFocus( const ContentCache& aContentCache, const IMENotification& aIMENotification, NotifyIMEFocusResolver&& aResolve) { if (mIsDestroyed) { return IPC_OK(); } nsCOMPtr widget = GetTextInputHandlingWidget(); if (!widget) { aResolve(IMENotificationRequests()); return IPC_OK(); } if (NS_WARN_IF(!aContentCache.IsValid())) { return IPC_FAIL(this, "Invalid content cache data"); } mContentCache.AssignContent(aContentCache, widget, &aIMENotification); IMEStateManager::NotifyIME(aIMENotification, widget, this); IMENotificationRequests requests; if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) { requests = widget->IMENotificationRequestsRef(); } aResolve(requests); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMETextChange( const ContentCache& aContentCache, const IMENotification& aIMENotification) { nsCOMPtr widget = GetTextInputHandlingWidget(); if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { return IPC_OK(); } if (NS_WARN_IF(!aContentCache.IsValid())) { return IPC_FAIL(this, "Invalid content cache data"); } mContentCache.AssignContent(aContentCache, widget, &aIMENotification); mContentCache.MaybeNotifyIME(widget, aIMENotification); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMECompositionUpdate( const ContentCache& aContentCache, const IMENotification& aIMENotification) { nsCOMPtr widget = GetTextInputHandlingWidget(); if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { return IPC_OK(); } if (NS_WARN_IF(!aContentCache.IsValid())) { return IPC_FAIL(this, "Invalid content cache data"); } mContentCache.AssignContent(aContentCache, widget, &aIMENotification); mContentCache.MaybeNotifyIME(widget, aIMENotification); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMESelection( const ContentCache& aContentCache, const IMENotification& aIMENotification) { nsCOMPtr widget = GetTextInputHandlingWidget(); if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { return IPC_OK(); } if (NS_WARN_IF(!aContentCache.IsValid())) { return IPC_FAIL(this, "Invalid content cache data"); } mContentCache.AssignContent(aContentCache, widget, &aIMENotification); mContentCache.MaybeNotifyIME(widget, aIMENotification); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvUpdateContentCache( const ContentCache& aContentCache) { nsCOMPtr widget = GetTextInputHandlingWidget(); if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { return IPC_OK(); } if (NS_WARN_IF(!aContentCache.IsValid())) { return IPC_FAIL(this, "Invalid content cache data"); } mContentCache.AssignContent(aContentCache, widget); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEMouseButtonEvent( const IMENotification& aIMENotification, bool* aConsumedByIME) { nsCOMPtr widget = GetTextInputHandlingWidget(); if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { *aConsumedByIME = false; return IPC_OK(); } nsresult rv = IMEStateManager::NotifyIME(aIMENotification, widget, this); *aConsumedByIME = rv == NS_SUCCESS_EVENT_CONSUMED; return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEPositionChange( const ContentCache& aContentCache, const IMENotification& aIMENotification) { nsCOMPtr widget = GetTextInputHandlingWidget(); if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { return IPC_OK(); } if (NS_WARN_IF(!aContentCache.IsValid())) { return IPC_FAIL(this, "Invalid content cache data"); } mContentCache.AssignContent(aContentCache, widget, &aIMENotification); mContentCache.MaybeNotifyIME(widget, aIMENotification); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvOnEventNeedingAckHandled( const EventMessage& aMessage) { // This is called when the child process receives WidgetCompositionEvent or // WidgetSelectionEvent. // FYI: Don't check if widget is nullptr here because it's more important to // notify mContentCahce of this than handling something in it. nsCOMPtr widget = GetTextInputHandlingWidget(); // While calling OnEventNeedingAckHandled(), BrowserParent *might* be // destroyed since it may send notifications to IME. RefPtr kungFuDeathGrip(this); mContentCache.OnEventNeedingAckHandled(widget, aMessage); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvRequestFocus( const bool& aCanRaise, const CallerType aCallerType) { LOGBROWSERFOCUS(("RecvRequestFocus %p, aCanRaise: %d", this, aCanRaise)); if (BrowserBridgeParent* bridgeParent = GetBrowserBridgeParent()) { mozilla::Unused << bridgeParent->SendRequestFocus(aCanRaise, aCallerType); return IPC_OK(); } if (!mFrameElement) { return IPC_OK(); } nsContentUtils::RequestFrameFocus(*mFrameElement, aCanRaise, aCallerType); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvWheelZoomChange(bool aIncrease) { RefPtr bc = GetBrowsingContext(); if (!bc) { return IPC_OK(); } bc->Canonical()->DispatchWheelZoomChange(aIncrease); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvEnableDisableCommands( const MaybeDiscarded& aContext, const nsString& aAction, nsTArray&& aEnabledCommands, nsTArray&& aDisabledCommands) { if (aContext.IsNullOrDiscarded()) { return IPC_OK(); } nsCOMPtr browserController = do_QueryActor( "Controllers", aContext.get_canonical()->GetCurrentWindowGlobal()); if (browserController) { browserController->EnableDisableCommands(aAction, aEnabledCommands, aDisabledCommands); } return IPC_OK(); } LayoutDeviceIntPoint BrowserParent::TransformPoint( const LayoutDeviceIntPoint& aPoint, const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix) { LayoutDevicePoint floatPoint(aPoint); LayoutDevicePoint floatTransformed = TransformPoint(floatPoint, aMatrix); // The next line loses precision if an out-of-process iframe // has been scaled or rotated. return RoundedToInt(floatTransformed); } LayoutDevicePoint BrowserParent::TransformPoint( const LayoutDevicePoint& aPoint, const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix) { return aMatrix.TransformPoint(aPoint); } LayoutDeviceIntPoint BrowserParent::TransformParentToChild( const LayoutDeviceIntPoint& aPoint) { LayoutDeviceToLayoutDeviceMatrix4x4 matrix = GetChildToParentConversionMatrix(); if (!matrix.Invert()) { return LayoutDeviceIntPoint(); } auto transformed = UntransformBy(matrix, aPoint); if (!transformed) { return LayoutDeviceIntPoint(); } return transformed.ref(); } LayoutDevicePoint BrowserParent::TransformParentToChild( const LayoutDevicePoint& aPoint) { LayoutDeviceToLayoutDeviceMatrix4x4 matrix = GetChildToParentConversionMatrix(); if (!matrix.Invert()) { return LayoutDevicePoint(); } auto transformed = UntransformBy(matrix, aPoint); if (!transformed) { return LayoutDeviceIntPoint(); } return transformed.ref(); } LayoutDeviceIntPoint BrowserParent::TransformChildToParent( const LayoutDeviceIntPoint& aPoint) { return TransformPoint(aPoint, GetChildToParentConversionMatrix()); } LayoutDevicePoint BrowserParent::TransformChildToParent( const LayoutDevicePoint& aPoint) { return TransformPoint(aPoint, GetChildToParentConversionMatrix()); } LayoutDeviceIntRect BrowserParent::TransformChildToParent( const LayoutDeviceIntRect& aRect) { LayoutDeviceToLayoutDeviceMatrix4x4 matrix = GetChildToParentConversionMatrix(); LayoutDeviceRect floatRect(aRect); // The outcome is not ideal if an out-of-process iframe has been rotated LayoutDeviceRect floatTransformed = matrix.TransformBounds(floatRect); // The next line loses precision if an out-of-process iframe // has been scaled or rotated. return RoundedToInt(floatTransformed); } LayoutDeviceToLayoutDeviceMatrix4x4 BrowserParent::GetChildToParentConversionMatrix() { if (mChildToParentConversionMatrix) { return *mChildToParentConversionMatrix; } LayoutDevicePoint offset(-GetChildProcessOffset()); return LayoutDeviceToLayoutDeviceMatrix4x4::Translation(offset); } void BrowserParent::SetChildToParentConversionMatrix( const Maybe& aMatrix, const ScreenRect& aRemoteDocumentRect) { if (mChildToParentConversionMatrix == aMatrix && mRemoteDocumentRect.isSome() && mRemoteDocumentRect.value() == aRemoteDocumentRect) { return; } mChildToParentConversionMatrix = aMatrix; mRemoteDocumentRect = Some(aRemoteDocumentRect); if (mIsDestroyed) { return; } mozilla::Unused << SendChildToParentMatrix(ToUnknownMatrix(aMatrix), aRemoteDocumentRect); } LayoutDeviceIntPoint BrowserParent::GetChildProcessOffset() { // The "toplevel widget" in child processes is always at position // 0,0. Map the event coordinates to match that. LayoutDeviceIntPoint offset(0, 0); RefPtr frameLoader = GetFrameLoader(); if (!frameLoader) { return offset; } nsIFrame* targetFrame = frameLoader->GetPrimaryFrameOfOwningContent(); if (!targetFrame) { return offset; } nsCOMPtr widget = GetWidget(); if (!widget) { return offset; } nsPresContext* presContext = targetFrame->PresContext(); nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame(); nsView* rootView = rootFrame ? rootFrame->GetView() : nullptr; if (!rootView) { return offset; } // Note that we don't want to take into account transforms here: #if 0 nsPoint pt(0, 0); nsLayoutUtils::TransformPoint(targetFrame, rootFrame, pt); #endif // In practice, when transforms are applied to this frameLoader, we currently // get the wrong results whether we take transforms into account here or not. // But applying transforms here gives us the wrong results in all // circumstances when transforms are applied, unless they're purely // translational. It also gives us the wrong results whenever CSS transitions // are used to apply transforms, since the offeets aren't updated as the // transition is animated. // // What we actually need to do is apply the transforms to the coordinates of // any events we send to the child, and reverse them for any screen // coordinates that we retrieve from the child. // TODO: Once we take into account transforms here, set viewportType // correctly. For now we use Visual as this means we don't apply // the layout-to-visual transform in TranslateViewToWidget(). ViewportType viewportType = ViewportType::Visual; nsPoint pt = targetFrame->GetOffsetTo(rootFrame); return -nsLayoutUtils::TranslateViewToWidget(presContext, rootView, pt, viewportType, widget); } LayoutDeviceIntPoint BrowserParent::GetClientOffset() { nsCOMPtr widget = GetWidget(); nsCOMPtr docWidget = GetDocWidget(); if (widget == docWidget) { return widget->GetClientOffset(); } return (docWidget->GetClientOffset() + nsLayoutUtils::WidgetToWidgetOffset(widget, docWidget)); } void BrowserParent::StopIMEStateManagement() { if (mIsDestroyed) { return; } Unused << SendStopIMEStateManagement(); } mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent( const WidgetKeyboardEvent& aEvent, const nsID& aUUID) { NS_ENSURE_TRUE(mFrameElement, IPC_OK()); // First, verify aEvent is what we've sent to a remote process. Maybe index = [&]() -> Maybe { for (const size_t i : IntegerRange(mWaitingReplyKeyboardEvents.Length())) { const SentKeyEventData& data = mWaitingReplyKeyboardEvents[i]; if (data.mUUID.Equals(aUUID)) { if (NS_WARN_IF(data.mKeyCode != aEvent.mKeyCode) || NS_WARN_IF(data.mCharCode != aEvent.mCharCode) || NS_WARN_IF(data.mPseudoCharCode != aEvent.mPseudoCharCode) || NS_WARN_IF(data.mKeyNameIndex != aEvent.mKeyNameIndex) || NS_WARN_IF(data.mCodeNameIndex != aEvent.mCodeNameIndex) || NS_WARN_IF(data.mModifiers != aEvent.mModifiers)) { // Got different event data from what we stored before dispatching an // event with the ID. return Nothing(); } return Some(i); } } // No entry found. return Nothing(); }(); if (MOZ_UNLIKELY(index.isNothing())) { return IPC_FAIL(this, "Bogus reply keyboard event"); } // Don't discard the older keyboard events because the order may be changed if // the remote process has a event listener which takes too long time and while // the freezing, user may switch the tab, or if the remote process sends // synchronous XMLHttpRequest. mWaitingReplyKeyboardEvents.RemoveElementAt(*index); // If the event propagation was stopped by the child, it means that the event // was ignored in the child. In the case, we should ignore it too because the // focused web app didn't have a chance to prevent its default. if (aEvent.PropagationStopped()) { return IPC_OK(); } WidgetKeyboardEvent localEvent(aEvent); localEvent.MarkAsHandledInRemoteProcess(); // Here we convert the WidgetEvent that we received to an Event // to be able to dispatch it to the element as the target element. RefPtr presContext = mFrameElement->OwnerDoc()->GetPresContext(); NS_ENSURE_TRUE(presContext, IPC_OK()); AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(), &localEvent); nsEventStatus status = nsEventStatus_eIgnore; // Handle access key in this process before dispatching reply event because // ESM handles it before dispatching the event to the DOM tree. if (localEvent.mMessage == eKeyPress && (localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eChrome) || localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eContent))) { RefPtr esm = presContext->EventStateManager(); AutoTArray accessCharCodes; localEvent.GetAccessKeyCandidates(accessCharCodes); if (esm->HandleAccessKey(&localEvent, presContext, accessCharCodes)) { status = nsEventStatus_eConsumeNoDefault; } } RefPtr frameElement = mFrameElement; EventDispatcher::Dispatch(frameElement, presContext, &localEvent, nullptr, &status); if (!localEvent.DefaultPrevented() && !localEvent.mFlags.mIsSynthesizedForTests) { nsCOMPtr widget = GetWidget(); if (widget) { widget->PostHandleKeyEvent(&localEvent); localEvent.StopPropagation(); } } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvAccessKeyNotHandled( const WidgetKeyboardEvent& aEvent) { NS_ENSURE_TRUE(mFrameElement, IPC_OK()); // This is called only when this process had focus and HandleAccessKey // message was posted to all remote process and each remote process didn't // execute any content access keys. if (MOZ_UNLIKELY(aEvent.mMessage != eKeyPress || !aEvent.IsTrusted())) { return IPC_FAIL(this, "Called with unexpected event"); } // If there is no requesting event, the event may have already been handled // when it's returned from another remote process. if (MOZ_UNLIKELY(!RequestingAccessKeyEventData::IsSet())) { return IPC_OK(); } // If the event does not match with the one which we requested a remote // process to handle access key of (that means that we has already requested // for another key press), we should ignore this call because user focuses // to the last key press. if (MOZ_UNLIKELY(!RequestingAccessKeyEventData::Equals(aEvent))) { return IPC_OK(); } RequestingAccessKeyEventData::Clear(); WidgetKeyboardEvent localEvent(aEvent); localEvent.MarkAsHandledInRemoteProcess(); localEvent.mMessage = eAccessKeyNotFound; // Here we convert the WidgetEvent that we received to an Event // to be able to dispatch it to the element as the target element. Document* doc = mFrameElement->OwnerDoc(); PresShell* presShell = doc->GetPresShell(); NS_ENSURE_TRUE(presShell, IPC_OK()); if (presShell->CanDispatchEvent()) { RefPtr presContext = presShell->GetPresContext(); NS_ENSURE_TRUE(presContext, IPC_OK()); RefPtr frameElement = mFrameElement; EventDispatcher::Dispatch(frameElement, presContext, &localEvent); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvRegisterProtocolHandler( const nsString& aScheme, nsIURI* aHandlerURI, const nsString& aTitle, nsIURI* aDocURI) { nsCOMPtr registrar = do_GetService(NS_WEBPROTOCOLHANDLERREGISTRAR_CONTRACTID); if (registrar) { registrar->RegisterProtocolHandler(aScheme, aHandlerURI, aTitle, aDocURI, mFrameElement); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvOnStateChange( const WebProgressData& aWebProgressData, const RequestData& aRequestData, const uint32_t aStateFlags, const nsresult aStatus, const Maybe& aStateChangeData) { RefPtr browsingContext = BrowsingContextForWebProgress(aWebProgressData); if (!browsingContext) { return IPC_OK(); } nsCOMPtr request; if (aRequestData.requestURI()) { request = MakeAndAddRef( aRequestData.requestURI(), aRequestData.originalRequestURI(), aRequestData.matchedList()); } if (aStateChangeData.isSome()) { if (!browsingContext->IsTopContent()) { return IPC_FAIL( this, "Unexpected WebProgressStateChangeData for non toplevel webProgress"); } if (nsCOMPtr browser = GetBrowser()) { Unused << browser->SetIsNavigating(aStateChangeData->isNavigating()); Unused << browser->SetMayEnableCharacterEncodingMenu( aStateChangeData->mayEnableCharacterEncodingMenu()); Unused << browser->UpdateForStateChange(aStateChangeData->charset(), aStateChangeData->documentURI(), aStateChangeData->contentType()); } } if (auto* listener = browsingContext->GetWebProgress()) { listener->OnStateChange(listener, request, aStateFlags, aStatus); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvOnProgressChange( const int32_t aCurTotalProgress, const int32_t aMaxTotalProgress) { // We only collect progress change notifications for the toplevel // BrowserParent. // FIXME: In the future, consider merging in progress change information from // oop subframes. if (!GetBrowsingContext()->IsTopContent() || !GetBrowsingContext()->GetWebProgress()) { return IPC_OK(); } GetBrowsingContext()->GetWebProgress()->OnProgressChange( nullptr, nullptr, 0, 0, aCurTotalProgress, aMaxTotalProgress); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvOnLocationChange( const WebProgressData& aWebProgressData, const RequestData& aRequestData, nsIURI* aLocation, const uint32_t aFlags, const bool aCanGoBack, const bool aCanGoForward, const Maybe& aLocationChangeData) { RefPtr browsingContext = BrowsingContextForWebProgress(aWebProgressData); if (!browsingContext) { return IPC_OK(); } nsCOMPtr request; if (aRequestData.requestURI()) { request = MakeAndAddRef( aRequestData.requestURI(), aRequestData.originalRequestURI(), aRequestData.matchedList()); } browsingContext->SetCurrentRemoteURI(aLocation); nsCOMPtr browser = GetBrowser(); if (!mozilla::SessionHistoryInParent() && browser) { Unused << browser->UpdateWebNavigationForLocationChange(aCanGoBack, aCanGoForward); } if (aLocationChangeData.isSome()) { if (!browsingContext->IsTopContent()) { return IPC_FAIL(this, "Unexpected WebProgressLocationChangeData for non " "toplevel webProgress"); } if (browser) { Unused << browser->SetIsNavigating(aLocationChangeData->isNavigating()); Unused << browser->UpdateForLocationChange( aLocation, aLocationChangeData->charset(), aLocationChangeData->mayEnableCharacterEncodingMenu(), aLocationChangeData->documentURI(), aLocationChangeData->title(), aLocationChangeData->contentPrincipal(), aLocationChangeData->contentPartitionedPrincipal(), aLocationChangeData->csp(), aLocationChangeData->referrerInfo(), aLocationChangeData->isSyntheticDocument(), aLocationChangeData->requestContextID().isSome(), aLocationChangeData->requestContextID().valueOr(0), aLocationChangeData->contentType()); } } if (auto* listener = browsingContext->GetWebProgress()) { listener->OnLocationChange(listener, request, aLocation, aFlags); } // Since we've now changed Documents, notify the BrowsingContext that we've // changed. Ideally we'd just let the BrowsingContext do this when it changes // the current window global, but that happens before this and we have a lot // of tests that depend on the specific ordering of messages. if (browsingContext->IsTopContent() && !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) { browsingContext->UpdateSecurityState(); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvOnStatusChange( const nsString& aMessage) { // We only collect status change notifications for the toplevel // BrowserParent. // FIXME: In the future, consider merging in status change information from // oop subframes. if (!GetBrowsingContext()->IsTopContent() || !GetBrowsingContext()->GetWebProgress()) { return IPC_OK(); } GetBrowsingContext()->GetWebProgress()->OnStatusChange(nullptr, nullptr, NS_OK, aMessage.get()); return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNavigationFinished() { nsCOMPtr browser = mFrameElement ? mFrameElement->AsBrowser() : nullptr; if (browser) { browser->SetIsNavigating(false); } return IPC_OK(); } mozilla::ipc::IPCResult BrowserParent::RecvNotifyContentBlockingEvent( const uint32_t& aEvent, const RequestData& aRequestData, const bool aBlocked, const nsACString& aTrackingOrigin, nsTArray&& aTrackingFullHashes, const Maybe< mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>& aReason) { RefPtr bc = GetBrowsingContext(); if (!bc || bc->IsDiscarded()) { return IPC_OK(); } // Get the top-level browsing context. bc = bc->Top(); RefPtr wgp = bc->Canonical()->GetCurrentWindowGlobal(); // The WindowGlobalParent would be null while running the test // browser_339445.js. This is unexpected and we will address this in a // following bug. For now, we first workaround this issue. if (!wgp) { return IPC_OK(); } nsCOMPtr request = MakeAndAddRef( aRequestData.requestURI(), aRequestData.originalRequestURI(), aRequestData.matchedList()); wgp->NotifyContentBlockingEvent(aEvent, request, aBlocked, aTrackingOrigin, aTrackingFullHashes, aReason); return IPC_OK(); } already_AddRefed BrowserParent::GetBrowser() { nsCOMPtr browser; RefPtr currentElement = mFrameElement; // In Responsive Design Mode, mFrameElement will be the