Merge inbound to mozilla-central. a=merge

This commit is contained in:
shindli 2018-12-05 23:33:31 +02:00
commit 077738eeba
55 changed files with 1753 additions and 119 deletions

View File

@ -17,7 +17,7 @@
class="header"
hidden="true"
data-category="paneContainers">
<label class="header-name" flex="1" data-l10n-id="containers-header"/>
<html:h1 data-l10n-id="containers-header"/>
</hbox>
<!-- Containers -->

View File

@ -11,7 +11,7 @@
class="subcategory"
hidden="true"
data-category="paneHome">
<label class="header-name" data-l10n-id="pane-home-title" flex="1"/>
<html:h1 style="-moz-box-flex: 1;" data-l10n-id="pane-home-title"/>
<button id="restoreDefaultHomePageBtn"
class="homepage-button check-home-page-controlled"
data-preference-related="browser.startup.homepage"

View File

@ -20,7 +20,7 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="pane-general-title"/>
<html:h1 data-l10n-id="pane-general-title"/>
</hbox>
<!-- Startup -->
@ -139,7 +139,7 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="language-and-appearance-header"/>
<html:h1 data-l10n-id="language-and-appearance-header"/>
</hbox>
<!-- Fonts and Colors -->
@ -354,7 +354,7 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="files-and-applications-title"/>
<html:h1 data-l10n-id="files-and-applications-title"/>
</hbox>
<!--Downloads-->
@ -436,7 +436,7 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="update-application-title"/>
<html:h1 data-l10n-id="update-application-title"/>
</hbox>
<!-- Update -->
@ -582,7 +582,7 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="performance-title"/>
<html:h1 data-l10n-id="performance-title"/>
</hbox>
<!-- Performance -->
@ -628,7 +628,7 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="browsing-title"/>
<html:h1 data-l10n-id="browsing-title"/>
</hbox>
<!-- Browsing -->
@ -667,7 +667,7 @@
class="subcategory"
hidden="true"
data-category="paneGeneral">
<label class="header-name" flex="1" data-l10n-id="network-settings-title"/>
<html:h1 data-l10n-id="network-settings-title"/>
</hbox>
<!-- Network Settings-->

View File

@ -13,7 +13,7 @@
class="subcategory"
hidden="true"
data-category="panePrivacy">
<label class="header-name" flex="1" data-l10n-id="privacy-header"/>
<html:h1 data-l10n-id="privacy-header"/>
</hbox>
<!-- Tracking / Content Blocking -->
@ -440,7 +440,7 @@
class="subcategory"
hidden="true"
data-category="panePrivacy">
<label class="header-name" flex="1" data-l10n-id="permissions-header"/>
<html:h1 data-l10n-id="permissions-header"/>
</hbox>
<!-- Permissions -->
@ -671,7 +671,7 @@
class="subcategory"
hidden="true"
data-category="panePrivacy">
<label class="header-name" flex="1" data-l10n-id="collection-header"/>
<html:h1 data-l10n-id="collection-header"/>
</hbox>
<groupbox id="dataCollectionGroup" data-category="panePrivacy" hidden="true">
@ -750,7 +750,7 @@
class="subcategory"
hidden="true"
data-category="panePrivacy">
<label class="header-name" flex="1" data-l10n-id="security-header"/>
<html:h1 data-l10n-id="security-header"/>
</hbox>
<!-- addons, forgery (phishing) UI Security -->

View File

@ -5,7 +5,7 @@
class="subcategory"
hidden="true"
data-category="paneSearch">
<label class="header-name" flex="1" data-l10n-id="pane-search-title" />
<html:h1 data-l10n-id="pane-search-title"/>
</hbox>
<groupbox id="searchbarGroup" data-category="paneSearch">

View File

@ -7,7 +7,7 @@
hidden="true"
data-hidden-from-search="true"
data-category="paneSearchResults">
<label class="header-name" flex="1" data-l10n-id="search-results-header" />
<html:h1 data-l10n-id="search-results-header"/>
</hbox>
<groupbox id="no-results-message"

View File

@ -11,7 +11,7 @@
class="subcategory"
hidden="true"
data-category="paneSync">
<label class="header-name" flex="1" data-l10n-id="pane-sync-title" />
<html:h1 data-l10n-id="pane-sync-title"/>
</hbox>
<deck id="weavePrefsDeck" data-category="paneSync" hidden="true"

View File

@ -28,16 +28,19 @@ groupbox[data-category] {
margin: 0 0 32px;
}
html|h1 {
margin: 0 0 8px;
font-size: 1.46em;
font-weight: 300;
line-height: 1.3em;
}
html|h2 {
margin: 16px 0 4px;
font-size: 1.14em;
line-height: normal;
}
.header-name {
margin-bottom: 8px;
}
description.indent,
.indent > description {
color: #737373;

View File

@ -4,7 +4,7 @@
* 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 "BrowsingContext.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/ChromeBrowsingContext.h"
#include "mozilla/dom/BrowsingContextBinding.h"

View File

@ -4,8 +4,8 @@
* 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/. */
#ifndef BrowsingContext_h
#define BrowsingContext_h
#ifndef mozilla_dom_BrowsingContext_h
#define mozilla_dom_BrowsingContext_h
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
@ -112,8 +112,8 @@ class BrowsingContext : public nsWrapperCache,
nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);
nsISupports* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContext)
@ -143,4 +143,5 @@ class BrowsingContext : public nsWrapperCache,
} // namespace dom
} // namespace mozilla
#endif
#endif // !defined(mozilla_dom_BrowsingContext_h)

View File

@ -4,7 +4,8 @@
* 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 "ChromeBrowsingContext.h"
#include "mozilla/dom/ChromeBrowsingContext.h"
#include "mozilla/dom/WindowGlobalParent.h"
namespace mozilla {
namespace dom {
@ -56,5 +57,53 @@ ChromeBrowsingContext::ChromeBrowsingContext(BrowsingContext* aParent,
return static_cast<const ChromeBrowsingContext*>(aContext);
}
void ChromeBrowsingContext::GetWindowGlobals(
nsTArray<RefPtr<WindowGlobalParent>>& aWindows) {
aWindows.SetCapacity(mWindowGlobals.Count());
for (auto iter = mWindowGlobals.Iter(); !iter.Done(); iter.Next()) {
aWindows.AppendElement(iter.Get()->GetKey());
}
}
void ChromeBrowsingContext::RegisterWindowGlobal(WindowGlobalParent* aGlobal) {
MOZ_ASSERT(!mWindowGlobals.Contains(aGlobal), "Global already registered!");
mWindowGlobals.PutEntry(aGlobal);
}
void ChromeBrowsingContext::UnregisterWindowGlobal(
WindowGlobalParent* aGlobal) {
MOZ_ASSERT(mWindowGlobals.Contains(aGlobal), "Global not registered!");
mWindowGlobals.RemoveEntry(aGlobal);
// Our current window global should be in our mWindowGlobals set. If it's not
// anymore, clear that reference.
if (aGlobal == mCurrentWindowGlobal) {
mCurrentWindowGlobal = nullptr;
}
}
void ChromeBrowsingContext::SetCurrentWindowGlobal(
WindowGlobalParent* aGlobal) {
MOZ_ASSERT(mWindowGlobals.Contains(aGlobal), "Global not registered!");
// TODO: This should probably assert that the processes match.
mCurrentWindowGlobal = aGlobal;
}
JSObject* ChromeBrowsingContext::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return ChromeBrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
}
void ChromeBrowsingContext::Traverse(nsCycleCollectionTraversalCallback& cb) {
ChromeBrowsingContext* tmp = this;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobals);
}
void ChromeBrowsingContext::Unlink() {
ChromeBrowsingContext* tmp = this;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobals);
}
} // namespace dom
} // namespace mozilla

View File

@ -4,19 +4,23 @@
* 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/. */
#ifndef ChromeBrowsingContext_h
#define ChromeBrowsingContext_h
#ifndef mozilla_dom_ChromeBrowsingContext_h
#define mozilla_dom_ChromeBrowsingContext_h
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/RefPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
class nsIDocShell;
namespace mozilla {
namespace dom {
class WindowGlobalParent;
// ChromeBrowsingContext is a BrowsingContext living in the parent
// process, with whatever extra data that a BrowsingContext in the
// parent needs.
@ -31,9 +35,24 @@ class ChromeBrowsingContext final : public BrowsingContext {
return mProcessId == aProcessId;
}
void GetWindowGlobals(nsTArray<RefPtr<WindowGlobalParent>>& aWindows);
// Called by WindowGlobalParent to register and unregister window globals.
void RegisterWindowGlobal(WindowGlobalParent* aGlobal);
void UnregisterWindowGlobal(WindowGlobalParent* aGlobal);
// The current active WindowGlobal.
WindowGlobalParent* GetCurrentWindowGlobal() const {
return mCurrentWindowGlobal;
}
void SetCurrentWindowGlobal(WindowGlobalParent* aGlobal);
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
protected:
void Traverse(nsCycleCollectionTraversalCallback& cb) {}
void Unlink() {}
void Traverse(nsCycleCollectionTraversalCallback& cb);
void Unlink();
using Type = BrowsingContext::Type;
ChromeBrowsingContext(BrowsingContext* aParent, BrowsingContext* aOpener,
@ -46,8 +65,13 @@ class ChromeBrowsingContext final : public BrowsingContext {
// XXX(farre): Store a ContentParent pointer here rather than mProcessId?
// Indicates which process owns the docshell.
uint64_t mProcessId;
// All live window globals within this browsing context.
nsTHashtable<nsRefPtrHashKey<WindowGlobalParent>> mWindowGlobals;
RefPtr<WindowGlobalParent> mCurrentWindowGlobal;
};
} // namespace dom
} // namespace mozilla
#endif
#endif // !defined(mozilla_dom_ChromeBrowsingContext_h)

View File

@ -3193,6 +3193,22 @@ nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
}
}
// If we're in the content process and have had a TreeOwner set on us, extract
// our TabChild actor. If we've already had our TabChild set, assert that it
// hasn't changed.
if (mTreeOwner && XRE_IsContentProcess()) {
nsCOMPtr<nsITabChild> newTabChild = do_GetInterface(mTreeOwner);
MOZ_ASSERT(newTabChild, "No TabChild actor for tree owner in Content!");
if (mTabChild) {
nsCOMPtr<nsITabChild> oldTabChild = do_QueryReferent(mTabChild);
MOZ_RELEASE_ASSERT(oldTabChild == newTabChild,
"Cannot cahnge TabChild during nsDocShell lifetime!");
} else {
mTabChild = do_GetWeakReference(newTabChild);
}
}
// Our tree owner has changed. Recompute scriptability.
//
// Note that this is near-redundant with the recomputation in
@ -5045,6 +5061,8 @@ nsDocShell::Destroy() {
SetTreeOwner(nullptr);
mTabChild = nullptr;
mOnePermittedSandboxedNavigator = nullptr;
// required to break ref cycle
@ -13321,8 +13339,7 @@ nsDocShell::GetScriptableTabChild(nsITabChild** aTabChild) {
}
already_AddRefed<nsITabChild> nsDocShell::GetTabChild() {
nsCOMPtr<nsIDocShellTreeOwner> owner(mTreeOwner);
nsCOMPtr<nsITabChild> tc = do_GetInterface(owner);
nsCOMPtr<nsITabChild> tc = do_QueryReferent(mTabChild);
return tc.forget();
}

View File

@ -921,6 +921,9 @@ class nsDocShell final : public nsDocLoader,
nsCOMPtr<nsICommandManager> mCommandManager;
RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
// Weak reference to our TabChild actor.
nsWeakPtr mTabChild;
// Dimensions of the docshell
nsIntRect mBounds;

View File

@ -235,6 +235,7 @@
#include "mozilla/HangAnnotations.h"
#include "mozilla/Encoding.h"
#include "nsXULElement.h"
#include "mozilla/RecordReplay.h"
#include "nsIBidiKeyboard.h"
@ -10096,6 +10097,12 @@ static const uint64_t kIdBits = 64 - kIdProcessBits;
MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
// Set the high bit for middleman processes so it doesn't conflict with the
// content process's generated IDs.
if (recordreplay::IsMiddleman()) {
bits |= uint64_t(1) << (kIdBits - 1);
}
return (processBits << kIdBits) | bits;
}

View File

@ -271,6 +271,8 @@
#include "mozilla/DocLoadingTimelineMarker.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "nsISpeculativeConnect.h"
#include "mozilla/MediaManager.h"
@ -2897,6 +2899,13 @@ void nsIDocument::SetDocumentURI(nsIURI* aURI) {
if (!equalBases) {
RefreshLinkHrefs();
}
// Tell our WindowGlobalParent that the document's URI has been changed.
nsPIDOMWindowInner* inner = GetInnerWindow();
WindowGlobalChild* wgc = inner ? inner->GetWindowGlobalChild() : nullptr;
if (wgc) {
Unused << wgc->SendUpdateDocumentURI(mDocumentURI);
}
}
static void GetFormattedTimeString(PRTime aTime,

View File

@ -100,6 +100,7 @@
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/ParentSHistory.h"
#include "mozilla/dom/ChildSHistory.h"
#include "mozilla/dom/ChromeBrowsingContext.h"
#include "mozilla/dom/HTMLBodyElement.h"
@ -3022,6 +3023,16 @@ already_AddRefed<nsILoadContext> nsFrameLoader::LoadContext() {
return loadContext.forget();
}
already_AddRefed<BrowsingContext> nsFrameLoader::GetBrowsingContext() {
RefPtr<BrowsingContext> browsingContext;
if (IsRemoteFrame() && (mRemoteBrowser || TryRemoteBrowser())) {
browsingContext = mRemoteBrowser->GetBrowsingContext();
} else if (GetDocShell(IgnoreErrors())) {
browsingContext = nsDocShell::Cast(mDocShell)->GetBrowsingContext();
}
return browsingContext.forget();
}
void nsFrameLoader::InitializeBrowserAPI() {
if (!OwnerIsMozBrowserFrame()) {
return;

View File

@ -48,6 +48,7 @@ namespace mozilla {
class OriginAttributes;
namespace dom {
class BrowsingContext;
class ChromeMessageSender;
class ContentParent;
class InProcessTabChildMessageManager;
@ -123,6 +124,8 @@ class nsFrameLoader final : public nsStubMutationObserver,
already_AddRefed<nsILoadContext> LoadContext();
already_AddRefed<mozilla::dom::BrowsingContext> GetBrowsingContext();
/**
* Start loading the frame. This method figures out what to load
* from the owner content in the frame loader.

View File

@ -260,6 +260,8 @@
#include "mozilla/dom/ClientSource.h"
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/WindowGlobalChild.h"
// Apple system headers seem to have a check() macro. <sigh>
#ifdef check
class nsIScriptTimeoutHandler;
@ -1266,6 +1268,11 @@ void nsGlobalWindowInner::FreeInnerObjects(bool aForDocumentOpen) {
}
}
if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
mWindowGlobalChild->Send__delete__(mWindowGlobalChild);
}
mWindowGlobalChild = nullptr;
mIntlUtils = nullptr;
}
@ -1635,6 +1642,15 @@ void nsGlobalWindowInner::InnerSetNewDocument(JSContext* aCx,
// out of sync.
ClearDocumentDependentSlots(aCx);
// FIXME: Currently, devtools can crete a fallback webextension window global
// in the content process which does not have a corresponding TabChild actor.
// This means we have no actor to be our parent. (Bug 1498293)
MOZ_DIAGNOSTIC_ASSERT(!mWindowGlobalChild,
"Shouldn't have created WindowGlobalChild yet!");
if (XRE_IsParentProcess() || mTabChild) {
mWindowGlobalChild = WindowGlobalChild::Create(this);
}
#ifdef DEBUG
mLastOpenedURI = aDocument->GetDocumentURI();
#endif

View File

@ -68,6 +68,7 @@
#include "mozilla/intl/LocaleService.h"
#include "WindowDestroyedEvent.h"
#include "nsDocShellLoadState.h"
#include "mozilla/dom/WindowGlobalChild.h"
// Helper Classes
#include "nsJSUtils.h"
@ -1962,6 +1963,10 @@ nsresult nsGlobalWindowOuter::SetNewDocument(nsIDocument* aDocument,
newInnerWindow->MigrateStateForDocumentOpen(currentInner);
}
// Tell the WindowGlobalParent that it should become the current window global
// for our BrowsingContext if it isn't already.
mInnerWindow->GetWindowGlobalChild()->SendBecomeCurrentWindowGlobal();
// We no longer need the old inner window. Start its destruction if
// its not being reused and clear our reference.
if (doomCurrentInner) {

View File

@ -68,6 +68,7 @@ class ServiceWorker;
class ServiceWorkerDescriptor;
class Timeout;
class TimeoutManager;
class WindowGlobalChild;
class CustomElementRegistry;
enum class CallerType : uint32_t;
} // namespace dom
@ -378,6 +379,10 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
return mDoc;
}
mozilla::dom::WindowGlobalChild* GetWindowGlobalChild() {
return mWindowGlobalChild;
}
virtual PopupControlState GetPopupControlState() const = 0;
// Determine if the window is suspended or frozen. Outer windows
@ -695,6 +700,12 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
// also set as permissions, but it could happen that we need to access them
// synchronously in this context, and for this, we need a copy here.
nsTArray<nsCString> mStorageAccessGranted;
// The WindowGlobalChild actor for this window.
//
// This will be non-null during the full lifetime of the window, initialized
// during SetNewDocument, and cleared during FreeInnerObjects.
RefPtr<mozilla::dom::WindowGlobalChild> mWindowGlobalChild;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)

View File

@ -17,3 +17,10 @@ interface BrowsingContext {
readonly attribute BrowsingContext? opener;
};
[Exposed=Window, ChromeOnly]
interface ChromeBrowsingContext : BrowsingContext {
sequence<WindowGlobalParent> getWindowGlobals();
readonly attribute WindowGlobalParent? currentWindowGlobal;
};

View File

@ -0,0 +1,46 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
interface Principal;
interface URI;
interface nsIDocShell;
[Exposed=Window, ChromeOnly]
interface WindowGlobalParent {
readonly attribute boolean isClosed;
readonly attribute boolean isInProcess;
readonly attribute ChromeBrowsingContext browsingContext;
readonly attribute boolean isCurrentGlobal;
readonly attribute unsigned long long innerWindowId;
readonly attribute unsigned long long outerWindowId;
readonly attribute FrameLoader? rootFrameLoader; // Embedded (browser) only
readonly attribute WindowGlobalChild? childActor; // in-process only
// Information about the currently loaded document.
readonly attribute Principal documentPrincipal;
readonly attribute URI? documentURI;
static WindowGlobalParent? getByInnerWindowId(unsigned long long innerWindowId);
};
[Exposed=Window, ChromeOnly]
interface WindowGlobalChild {
readonly attribute boolean isClosed;
readonly attribute boolean isInProcess;
readonly attribute BrowsingContext browsingContext;
readonly attribute boolean isCurrentGlobal;
readonly attribute unsigned long long innerWindowId;
readonly attribute unsigned long long outerWindowId;
readonly attribute WindowGlobalParent? parentActor; // in-process only
static WindowGlobalChild? getByInnerWindowId(unsigned long long innerWIndowId);
};

View File

@ -54,6 +54,7 @@ WEBIDL_FILES = [
'TelemetryStopwatch.webidl',
'WebExtensionContentScript.webidl',
'WebExtensionPolicy.webidl',
'WindowGlobalActors.webidl',
'XULFrameElement.webidl',
'XULMenuElement.webidl',
'XULScrollElement.webidl',

View File

@ -890,9 +890,6 @@ nsresult ContentChild::ProvideWindowCommon(
TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext();
RefPtr<TabChild> newChild =
new TabChild(this, tabId, tabGroup, newTabContext, aChromeFlags);
if (NS_FAILED(newChild->Init(aParent))) {
return NS_ERROR_ABORT;
}
if (aTabOpener) {
MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext);
@ -908,6 +905,12 @@ nsresult ContentChild::ProvideWindowCommon(
RefPtr<TabChild>(newChild).forget().take(), tabId, TabId(0), *ipcContext,
aChromeFlags, GetID(), IsForBrowser());
// Now that |newChild| has had its IPC link established, call |Init| to set it
// up.
if (NS_FAILED(newChild->Init(aParent))) {
return NS_ERROR_ABORT;
}
nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
if (aParent) {
nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow =
@ -2952,6 +2955,13 @@ uint64_t NextWindowID() {
MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits));
uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1);
// Make sure that the middleman process doesn't generate WindowIDs which
// conflict with the process it's wrapping (which shares a ContentParentID
// with it).
if (recordreplay::IsMiddleman()) {
windowBits |= uint64_t(1) << (kWindowIDWindowBits - 1);
}
return (processBits << kWindowIDWindowBits) | windowBits;
}

View File

@ -26,6 +26,8 @@ using CSSSize from "Units.h";
using mozilla::LayoutDeviceIntPoint from "Units.h";
using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h";
using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
using refcounted class nsIPrincipal from "mozilla/dom/PermissionMessageUtils.h";
using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h";
namespace mozilla {
@ -183,5 +185,13 @@ struct PerformanceInfo
CategoryDispatch[] items;
};
struct WindowGlobalInit
{
nsIPrincipal principal;
BrowsingContextId browsingContextId;
uint64_t innerWindowId;
uint64_t outerWindowId;
};
} // namespace dom
} // namespace mozilla

View File

@ -18,6 +18,7 @@ include protocol PParentToChildStream;
include protocol PFileDescriptorSet;
include protocol PIPCBlobInputStream;
include protocol PPaymentRequest;
include protocol PWindowGlobal;
include DOMTypes;
include IPCBlob;
@ -86,6 +87,7 @@ using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
using mozilla::FontRange from "ipc/nsGUIEventIPC.h";
using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h";
namespace mozilla {
namespace dom {
@ -118,6 +120,7 @@ nested(upto inside_cpow) sync protocol PBrowser
manages PIndexedDBPermissionRequest;
manages PPluginWidget;
manages PPaymentRequest;
manages PWindowGlobal;
both:
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
@ -144,6 +147,12 @@ parent:
async PPaymentRequest();
/**
* Construct a new WindowGlobal actor for a window global in the given
* BrowsingContext and with the given principal.
*/
async PWindowGlobal(WindowGlobalInit init);
/**
* Sends an NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW to be adopted by the
* widget's shareable window on the chrome side. Only used on Windows.
@ -579,6 +588,8 @@ parent:
sync SetPrefersReducedMotionOverrideForTest(bool aValue);
sync ResetPrefersReducedMotionOverrideForTest();
async RootBrowsingContext(BrowsingContextId aId);
child:
/**
* Notify the remote browser that it has been Show()n on this

View File

@ -0,0 +1,38 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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 protocol PBrowser;
include protocol PInProcess;
include DOMTypes;
using refcounted class nsIURI from "mozilla/ipc/URIUtils.h";
namespace mozilla {
namespace dom {
/**
* A PWindowGlobal actor has a lifetime matching that of a single Window Global,
* specifically a |nsGlobalWindowInner|. These actors will form a parent/child
* link either between the chrome/content process, or will be in-process, for
* documents which are loaded in the chrome process.
*/
async protocol PWindowGlobal
{
manager PBrowser or PInProcess;
parent:
/// Update the URI of the document in this WindowGlobal.
async UpdateDocumentURI(nsIURI aUri);
/// Notify the parent that this PWindowGlobal is now the current global.
async BecomeCurrentWindowGlobal();
async __delete__();
};
} // namespace dom
} // namespace mozilla

View File

@ -122,6 +122,7 @@
#include "mozilla/Telemetry.h"
#include "nsDocShellLoadState.h"
#include "nsWebBrowser.h"
#include "mozilla/dom/WindowGlobalChild.h"
#ifdef XP_WIN
#include "mozilla/plugins/PluginWidgetChild.h"
@ -537,6 +538,11 @@ nsresult TabChild::Init(mozIDOMWindowProxy* aParent) {
loadContext->SetRemoteTabs(mChromeFlags &
nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
// Send our browsing context to the parent process.
RefPtr<BrowsingContext> browsingContext =
nsDocShell::Cast(docShell)->GetBrowsingContext();
SendRootBrowsingContext(BrowsingContextId(browsingContext->Id()));
// Few lines before, baseWindow->Create() will end up creating a new
// window root in nsGlobalWindow::SetDocShell.
// Then this chrome event handler, will be inherited to inner windows.
@ -3137,6 +3143,17 @@ bool TabChild::DeallocPPaymentRequestChild(PPaymentRequestChild* actor) {
return true;
}
PWindowGlobalChild* TabChild::AllocPWindowGlobalChild(const WindowGlobalInit&) {
MOZ_CRASH("We should never be manually allocating PWindowGlobalChild actors");
return nullptr;
}
bool TabChild::DeallocPWindowGlobalChild(PWindowGlobalChild* aActor) {
// This reference was added in WindowGlobalChild::Create.
static_cast<WindowGlobalChild*>(aActor)->Release();
return true;
}
ScreenIntSize TabChild::GetInnerSize() {
LayoutDeviceIntSize innerSize =
RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale());

View File

@ -657,6 +657,11 @@ class TabChild final : public TabChildBase,
protected:
virtual ~TabChild();
virtual PWindowGlobalChild* AllocPWindowGlobalChild(
const WindowGlobalInit& aInit) override;
virtual bool DeallocPWindowGlobalChild(PWindowGlobalChild* aActor) override;
virtual mozilla::ipc::IPCResult RecvDestroy() override;
virtual mozilla::ipc::IPCResult RecvSetDocShellIsActive(

View File

@ -100,6 +100,8 @@
#include "ProcessPriorityManager.h"
#include "nsString.h"
#include "IHistory.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/ChromeBrowsingContext.h"
#ifdef XP_WIN
#include "mozilla/plugins/PluginWidgetParent.h"
@ -988,6 +990,24 @@ bool TabParent::DeallocPIndexedDBPermissionRequestParent(
aActor);
}
IPCResult TabParent::RecvPWindowGlobalConstructor(
PWindowGlobalParent* aActor, const WindowGlobalInit& aInit) {
static_cast<WindowGlobalParent*>(aActor)->Init(aInit);
return IPC_OK();
}
PWindowGlobalParent* TabParent::AllocPWindowGlobalParent(
const WindowGlobalInit& aInit) {
// Reference freed in DeallocPWindowGlobalParent.
return do_AddRef(new WindowGlobalParent(aInit, /* inproc */ false)).take();
}
bool TabParent::DeallocPWindowGlobalParent(PWindowGlobalParent* aActor) {
// Free reference from AllocPWindowGlobalParent.
static_cast<WindowGlobalParent*>(aActor)->Release();
return true;
}
void TabParent::SendMouseEvent(const nsAString& aType, float aX, float aY,
int32_t aButton, int32_t aClickCount,
int32_t aModifiers,
@ -3371,6 +3391,14 @@ mozilla::ipc::IPCResult TabParent::RecvGetSystemFont(nsCString* aFontName) {
return IPC_OK();
}
mozilla::ipc::IPCResult TabParent::RecvRootBrowsingContext(
const BrowsingContextId& aId) {
MOZ_ASSERT(!mBrowsingContext, "May only set browsing context once!");
mBrowsingContext = ChromeBrowsingContext::Get(aId);
MOZ_ASSERT(mBrowsingContext, "Invalid ID!");
return IPC_OK();
}
NS_IMETHODIMP
FakeChannel::OnAuthAvailable(nsISupports* aContext,
nsIAuthInformation* aAuthInfo) {

View File

@ -67,6 +67,7 @@ class DataSourceSurface;
namespace dom {
class ChromeBrowsingContext;
class ClonedMessageData;
class nsIContentParent;
class Element;
@ -125,6 +126,8 @@ class TabParent final : public PBrowserParent,
nsIXULBrowserWindow* GetXULBrowserWindow();
ChromeBrowsingContext* GetBrowsingContext() { return mBrowsingContext; }
void Destroy();
void RemoveWindowListeners();
@ -305,6 +308,14 @@ class TabParent final : public PBrowserParent,
*/
a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
virtual PWindowGlobalParent* AllocPWindowGlobalParent(
const WindowGlobalInit& aInit) override;
virtual bool DeallocPWindowGlobalParent(PWindowGlobalParent* aActor) override;
virtual mozilla::ipc::IPCResult RecvPWindowGlobalConstructor(
PWindowGlobalParent* aActor, const WindowGlobalInit& aInit) override;
void LoadURL(nsIURI* aURI);
void InitRendering();
@ -576,6 +587,9 @@ class TabParent final : public PBrowserParent,
virtual mozilla::ipc::IPCResult RecvShowCanvasPermissionPrompt(
const nsCString& aFirstPartyURI) override;
virtual mozilla::ipc::IPCResult RecvRootBrowsingContext(
const BrowsingContextId& aId) override;
mozilla::ipc::IPCResult RecvSetSystemFont(
const nsCString& aFontName) override;
mozilla::ipc::IPCResult RecvGetSystemFont(nsCString* aFontName) override;
@ -653,6 +667,9 @@ class TabParent final : public PBrowserParent,
// dispatch message manager messages during this time.
RefPtr<nsFrameLoader> mFrameLoader;
// The root browsing context loaded in this TabParent.
RefPtr<ChromeBrowsingContext> mBrowsingContext;
TabId mTabId;
// When loading a new tab or window via window.open, the child is

View File

@ -0,0 +1,135 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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 "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/ipc/InProcessChild.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/WindowGlobalActorsBinding.h"
namespace mozilla {
namespace dom {
typedef nsRefPtrHashtable<nsUint64HashKey, WindowGlobalChild> WGCByIdMap;
static StaticAutoPtr<WGCByIdMap> gWindowGlobalChildById;
WindowGlobalChild::WindowGlobalChild(nsGlobalWindowInner* aWindow,
dom::BrowsingContext* aBrowsingContext)
: mWindowGlobal(aWindow)
, mBrowsingContext(aBrowsingContext)
, mInnerWindowId(aWindow->WindowID())
, mOuterWindowId(aWindow->GetOuterWindow()->WindowID())
, mIPCClosed(true)
{
}
already_AddRefed<WindowGlobalChild>
WindowGlobalChild::Create(nsGlobalWindowInner* aWindow)
{
nsCOMPtr<nsIPrincipal> principal = aWindow->GetPrincipal();
MOZ_ASSERT(principal);
RefPtr<nsDocShell> docshell = nsDocShell::Cast(aWindow->GetDocShell());
MOZ_ASSERT(docshell);
// Initalize our WindowGlobalChild object.
RefPtr<dom::BrowsingContext> bc = docshell->GetBrowsingContext();
RefPtr<WindowGlobalChild> wgc = new WindowGlobalChild(aWindow, bc);
WindowGlobalInit init(principal,
BrowsingContextId(wgc->BrowsingContext()->Id()),
wgc->mInnerWindowId,
wgc->mOuterWindowId);
// Send the link constructor over PInProcessChild or PBrowser.
if (XRE_IsParentProcess()) {
InProcessChild* ipc = InProcessChild::Singleton();
if (!ipc) {
return nullptr;
}
// Note: ref is released in DeallocPWindowGlobalChild
ipc->SendPWindowGlobalConstructor(do_AddRef(wgc).take(), init);
} else {
RefPtr<TabChild> tabChild = TabChild::GetFrom(static_cast<mozIDOMWindow*>(aWindow));
MOZ_ASSERT(tabChild);
// Note: ref is released in DeallocPWindowGlobalChild
tabChild->SendPWindowGlobalConstructor(do_AddRef(wgc).take(), init);
}
wgc->mIPCClosed = false;
// Register this WindowGlobal in the gWindowGlobalParentsById map.
if (!gWindowGlobalChildById) {
gWindowGlobalChildById = new WGCByIdMap();
ClearOnShutdown(&gWindowGlobalChildById);
}
auto entry = gWindowGlobalChildById->LookupForAdd(wgc->mInnerWindowId);
MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalChild entry for ID!");
entry.OrInsert([&] { return wgc; });
return wgc.forget();
}
/* static */ already_AddRefed<WindowGlobalChild>
WindowGlobalChild::GetByInnerWindowId(uint64_t aInnerWindowId)
{
if (!gWindowGlobalChildById) {
return nullptr;
}
return gWindowGlobalChildById->Get(aInnerWindowId);
}
bool
WindowGlobalChild::IsCurrentGlobal()
{
return !mIPCClosed && mWindowGlobal->IsCurrentInnerWindow();
}
already_AddRefed<WindowGlobalParent>
WindowGlobalChild::GetParentActor()
{
if (mIPCClosed) {
return nullptr;
}
IProtocol* otherSide = InProcessChild::ParentActorFor(this);
return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
}
void
WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy)
{
mIPCClosed = true;
gWindowGlobalChildById->Remove(mInnerWindowId);
}
WindowGlobalChild::~WindowGlobalChild()
{
MOZ_ASSERT(!gWindowGlobalChildById ||
!gWindowGlobalChildById->Contains(mInnerWindowId));
}
JSObject*
WindowGlobalChild::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto);
}
nsISupports*
WindowGlobalChild::GetParentObject()
{
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalChild,
mWindowGlobal,
mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalChild, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalChild, Release)
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,87 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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/. */
#ifndef mozilla_dom_WindowGlobalChild_h
#define mozilla_dom_WindowGlobalChild_h
#include "mozilla/RefPtr.h"
#include "mozilla/dom/PWindowGlobalChild.h"
#include "nsWrapperCache.h"
class nsGlobalWindowInner;
class nsDocShell;
namespace mozilla {
namespace dom {
class BrowsingContext;
class WindowGlobalParent;
/**
* Actor for a single nsGlobalWindowInner. This actor is used to communicate
* information to the parent process asynchronously.
*/
class WindowGlobalChild : public nsWrapperCache
, public PWindowGlobalChild
{
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalChild)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WindowGlobalChild)
static already_AddRefed<WindowGlobalChild>
GetByInnerWindowId(uint64_t aInnerWindowId);
static already_AddRefed<WindowGlobalChild>
GetByInnerWindowId(const GlobalObject& aGlobal, uint64_t aInnerWindowId) {
return GetByInnerWindowId(aInnerWindowId);
}
dom::BrowsingContext* BrowsingContext() { return mBrowsingContext; }
nsGlobalWindowInner* WindowGlobal() { return mWindowGlobal; }
// Has this actor been shut down
bool IsClosed() { return mIPCClosed; }
// Check if this actor is managed by PInProcess, as-in the document is loaded
// in the chrome process.
bool IsInProcess() { return XRE_IsParentProcess(); }
// The Window ID for this WindowGlobal
uint64_t InnerWindowId() { return mInnerWindowId; }
uint64_t OuterWindowId() { return mOuterWindowId; }
bool IsCurrentGlobal();
// Get the other side of this actor if it is an in-process actor. Returns
// |nullptr| if the actor has been torn down, or is not in-process.
already_AddRefed<WindowGlobalParent> GetParentActor();
// Create and initialize the WindowGlobalChild object.
static already_AddRefed<WindowGlobalChild>
Create(nsGlobalWindowInner* aWindow);
nsISupports* GetParentObject();
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
protected:
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private:
WindowGlobalChild(nsGlobalWindowInner* aWindow, dom::BrowsingContext* aBc);
~WindowGlobalChild();
RefPtr<nsGlobalWindowInner> mWindowGlobal;
RefPtr<dom::BrowsingContext> mBrowsingContext;
uint64_t mInnerWindowId;
uint64_t mOuterWindowId;
bool mIPCClosed;
};
} // namespace dom
} // namespace mozilla
#endif // !defined(mozilla_dom_WindowGlobalChild_h)

View File

@ -0,0 +1,175 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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 "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/ipc/InProcessParent.h"
#include "mozilla/dom/ChromeBrowsingContext.h"
#include "mozilla/dom/WindowGlobalActorsBinding.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace dom {
typedef nsRefPtrHashtable<nsUint64HashKey, WindowGlobalParent> WGPByIdMap;
static StaticAutoPtr<WGPByIdMap> gWindowGlobalParentsById;
WindowGlobalParent::WindowGlobalParent(const WindowGlobalInit& aInit,
bool aInProcess)
: mDocumentPrincipal(aInit.principal())
, mInnerWindowId(aInit.innerWindowId())
, mOuterWindowId(aInit.outerWindowId())
, mInProcess(aInProcess)
, mIPCClosed(true) // Closed until WGP::Init
{
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only");
MOZ_RELEASE_ASSERT(mDocumentPrincipal, "Must have a valid principal");
// NOTE: mBrowsingContext initialized in Init()
MOZ_RELEASE_ASSERT(aInit.browsingContextId() != 0,
"Must be made in BrowsingContext");
}
void
WindowGlobalParent::Init(const WindowGlobalInit& aInit)
{
MOZ_ASSERT(Manager(), "Should have a manager!");
MOZ_ASSERT(!mFrameLoader, "Cannot Init() a WindowGlobalParent twice!");
MOZ_ASSERT(mIPCClosed, "IPC shouldn't be open yet");
mIPCClosed = false;
// Register this WindowGlobal in the gWindowGlobalParentsById map.
if (!gWindowGlobalParentsById) {
gWindowGlobalParentsById = new WGPByIdMap();
ClearOnShutdown(&gWindowGlobalParentsById);
}
auto entry = gWindowGlobalParentsById->LookupForAdd(mInnerWindowId);
MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalParent entry for ID!");
entry.OrInsert([&] { return this; });
// Determine which content process the window global is coming from.
ContentParentId processId(0);
if (!mInProcess) {
processId = static_cast<ContentParent*>(Manager()->Manager())->ChildID();
}
mBrowsingContext = ChromeBrowsingContext::Get(aInit.browsingContextId());
MOZ_ASSERT(mBrowsingContext);
// XXX(nika): This won't be the case soon, but for now this is a good
// assertion as we can't switch processes. We should relax this eventually.
MOZ_ASSERT(mBrowsingContext->IsOwnedByProcess(processId));
// Attach ourself to the browsing context.
mBrowsingContext->RegisterWindowGlobal(this);
// Determine what toplevel frame element our WindowGlobalParent is being
// embedded in.
RefPtr<Element> frameElement;
if (mInProcess) {
// In the in-process case, we can get it from the other side's
// WindowGlobalChild.
MOZ_ASSERT(Manager()->GetProtocolTypeId() == PInProcessMsgStart);
RefPtr<WindowGlobalChild> otherSide = GetChildActor();
if (otherSide && otherSide->WindowGlobal()) {
// Get the toplevel window from the other side.
RefPtr<nsDocShell> docShell = nsDocShell::Cast(otherSide->WindowGlobal()->GetDocShell());
if (docShell) {
docShell->GetTopFrameElement(getter_AddRefs(frameElement));
}
}
} else {
// In the cross-process case, we can get the frame element from our manager.
MOZ_ASSERT(Manager()->GetProtocolTypeId() == PBrowserMsgStart);
frameElement = static_cast<TabParent*>(Manager())->GetOwnerElement();
}
// Extract the nsFrameLoader from the current frame element. We may not have a
// nsFrameLoader if we are a chrome document.
nsCOMPtr<nsIFrameLoaderOwner> flOwner = do_QueryInterface(frameElement);
if (flOwner) {
mFrameLoader = flOwner->GetFrameLoader();
}
}
/* static */ already_AddRefed<WindowGlobalParent>
WindowGlobalParent::GetByInnerWindowId(uint64_t aInnerWindowId)
{
if (!gWindowGlobalParentsById) {
return nullptr;
}
return gWindowGlobalParentsById->Get(aInnerWindowId);
}
already_AddRefed<WindowGlobalChild>
WindowGlobalParent::GetChildActor()
{
if (mIPCClosed) {
return nullptr;
}
IProtocol* otherSide = InProcessParent::ChildActorFor(this);
return do_AddRef(static_cast<WindowGlobalChild*>(otherSide));
}
IPCResult
WindowGlobalParent::RecvUpdateDocumentURI(nsIURI* aURI)
{
// XXX(nika): Assert that the URI change was one which makes sense (either
// about:blank -> a real URI, or a legal push/popstate URI change?)
mDocumentURI = aURI;
return IPC_OK();
}
IPCResult
WindowGlobalParent::RecvBecomeCurrentWindowGlobal()
{
mBrowsingContext->SetCurrentWindowGlobal(this);
return IPC_OK();
}
bool
WindowGlobalParent::IsCurrentGlobal()
{
return !mIPCClosed && mBrowsingContext->GetCurrentWindowGlobal() == this;
}
void
WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy)
{
mIPCClosed = true;
gWindowGlobalParentsById->Remove(mInnerWindowId);
mBrowsingContext->UnregisterWindowGlobal(this);
}
WindowGlobalParent::~WindowGlobalParent()
{
MOZ_ASSERT(!gWindowGlobalParentsById ||
!gWindowGlobalParentsById->Contains(mInnerWindowId));
}
JSObject*
WindowGlobalParent::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
}
nsISupports*
WindowGlobalParent::GetParentObject()
{
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalParent,
mFrameLoader,
mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalParent, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalParent, Release)
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,113 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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/. */
#ifndef mozilla_dom_WindowGlobalParent_h
#define mozilla_dom_WindowGlobalParent_h
#include "mozilla/RefPtr.h"
#include "mozilla/dom/PWindowGlobalParent.h"
#include "nsWrapperCache.h"
class nsIPrincipal;
class nsIURI;
class nsFrameLoader;
namespace mozilla {
namespace dom {
class ChromeBrowsingContext;
class WindowGlobalChild;
/**
* A handle in the parent process to a specific nsGlobalWindowInner object.
*/
class WindowGlobalParent final : public nsWrapperCache
, public PWindowGlobalParent
{
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalParent)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WindowGlobalParent)
static already_AddRefed<WindowGlobalParent>
GetByInnerWindowId(uint64_t aInnerWindowId);
static already_AddRefed<WindowGlobalParent>
GetByInnerWindowId(const GlobalObject& aGlobal, uint64_t aInnerWindowId) {
return GetByInnerWindowId(aInnerWindowId);
}
// Has this actor been shut down
bool IsClosed() { return mIPCClosed; }
// Check if this actor is managed by PInProcess, as-in the document is loaded
// in-process.
bool IsInProcess() { return mInProcess; }
// Get the other side of this actor if it is an in-process actor. Returns
// |nullptr| if the actor has been torn down, or is not in-process.
already_AddRefed<WindowGlobalChild> GetChildActor();
// The principal of this WindowGlobal. This value will not change over the
// lifetime of the WindowGlobal object, even to reflect changes in
// |document.domain|.
nsIPrincipal* DocumentPrincipal() { return mDocumentPrincipal; }
// The BrowsingContext which this WindowGlobal has been loaded into.
ChromeBrowsingContext* BrowsingContext() { return mBrowsingContext; }
// Get the root nsFrameLoader object for the tree of BrowsingContext nodes
// which this WindowGlobal is a part of. This will be the nsFrameLoader
// holding the TabParent for remote tabs, and the root content frameloader for
// non-remote tabs.
nsFrameLoader* GetRootFrameLoader() { return mFrameLoader; }
// The current URI which loaded in the document.
nsIURI* GetDocumentURI() { return mDocumentURI; }
// Window IDs for inner/outer windows.
uint64_t OuterWindowId() { return mOuterWindowId; }
uint64_t InnerWindowId() { return mInnerWindowId; }
bool IsCurrentGlobal();
// Create a WindowGlobalParent from over IPC. This method should not be called
// from outside of the IPC constructors.
WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess);
// Initialize the mFrameLoader fields for a created WindowGlobalParent. Must
// be called after setting the Manager actor.
void Init(const WindowGlobalInit& aInit);
nsISupports* GetParentObject();
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
protected:
// IPC messages
mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI) override;
mozilla::ipc::IPCResult RecvBecomeCurrentWindowGlobal() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
private:
~WindowGlobalParent();
// NOTE: This document principal doesn't reflect possible |document.domain|
// mutations which may have been made in the actual document.
nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
nsCOMPtr<nsIURI> mDocumentURI;
RefPtr<nsFrameLoader> mFrameLoader;
RefPtr<ChromeBrowsingContext> mBrowsingContext;
uint64_t mInnerWindowId;
uint64_t mOuterWindowId;
bool mInProcess;
bool mIPCClosed;
};
} // namespace dom
} // namespace mozilla
#endif // !defined(mozilla_dom_WindowGlobalParent_h)

View File

@ -49,6 +49,8 @@ EXPORTS.mozilla.dom += [
'TabParent.h',
'URLClassifierChild.h',
'URLClassifierParent.h',
'WindowGlobalChild.h',
'WindowGlobalParent.h',
]
EXPORTS.mozilla += [
@ -83,6 +85,8 @@ UNIFIED_SOURCES += [
'TabMessageUtils.cpp',
'TabParent.cpp',
'URLClassifierParent.cpp',
'WindowGlobalChild.cpp',
'WindowGlobalParent.cpp',
]
# ContentChild.cpp cannot be compiled in unified mode on linux due to Time conflict
@ -110,6 +114,7 @@ IPDL_SOURCES += [
'PURLClassifier.ipdl',
'PURLClassifierInfo.ipdlh',
'PURLClassifierLocal.ipdl',
'PWindowGlobal.ipdl',
'ServiceWorkerConfiguration.ipdlh',
]

View File

@ -33,6 +33,12 @@ interface FrameLoader {
*/
readonly attribute LoadContext loadContext;
/**
* Get the root BrowsingContext within the frame.
* This may be null immediately after creating a remote frame.
*/
readonly attribute BrowsingContext? browsingContext;
/**
* Get the ParentSHistory for the nsFrameLoader. May return null if this
* frameloader is not for a toplevel frame.

View File

@ -35,6 +35,7 @@ bool AnimationFrameRetainedBuffer::InsertInternal(RefPtr<imgFrame>&& aFrame) {
MOZ_ASSERT(!mSizeKnown);
MOZ_ASSERT(mFrames.Length() < mThreshold);
++mSize;
mFrames.AppendElement(std::move(aFrame));
MOZ_ASSERT(mSize == mFrames.Length());
return mSize < mThreshold;
@ -140,20 +141,32 @@ AnimationFrameDiscardingQueue::AnimationFrameDiscardingQueue(
AnimationFrameRetainedBuffer&& aQueue)
: AnimationFrameBuffer(aQueue),
mInsertIndex(aQueue.mFrames.Length()),
mFirstFrame(std::move(aQueue.mFrames[0])) {
mFirstFrame(aQueue.mFrames[0]) {
MOZ_ASSERT(!mSizeKnown);
MOZ_ASSERT(!mRedecodeError);
MOZ_ASSERT(mInsertIndex > 0);
MOZ_ASSERT(mGetIndex > 0);
mMayDiscard = true;
for (size_t i = aQueue.mGetIndex; i < mInsertIndex; ++i) {
// We avoided moving aQueue.mFrames[0] for mFirstFrame above because it is
// possible the animation was reset back to the beginning, and then we crossed
// the threshold without advancing further. That would mean mGetIndex is 0.
for (size_t i = mGetIndex; i < mInsertIndex; ++i) {
MOZ_ASSERT(aQueue.mFrames[i]);
mDisplay.push_back(std::move(aQueue.mFrames[i]));
}
}
bool AnimationFrameDiscardingQueue::InsertInternal(RefPtr<imgFrame>&& aFrame) {
if (mInsertIndex == mSize) {
if (mSizeKnown) {
// We produced more frames on a subsequent decode than on the first pass.
mRedecodeError = true;
mPending = 0;
return true;
}
++mSize;
}
// Even though we don't use redecoded first frames for display purposes, we
// will still use them for recycling, so we still need to insert it.
mDisplay.push_back(std::move(aFrame));
@ -174,7 +187,6 @@ bool AnimationFrameDiscardingQueue::ResetInternal() {
bool AnimationFrameDiscardingQueue::MarkComplete(
const gfx::IntRect& aFirstFrameRefreshArea) {
if (NS_WARN_IF(mInsertIndex != mSize)) {
MOZ_ASSERT(mSizeKnown);
mRedecodeError = true;
mPending = 0;
}
@ -296,6 +308,10 @@ AnimationFrameRecyclingQueue::AnimationFrameRecyclingQueue(
// recycling but none of the frames were marked as recyclable. We will incur
// the extra allocation cost for a few more frames.
mRecycling = true;
// Until we reach the end of the animation, set the first frame refresh area
// to match that of the full area of the first frame.
mFirstFrameRefreshArea = mFirstFrame->GetRect();
}
void AnimationFrameRecyclingQueue::AddSizeOfExcludingThis(
@ -370,7 +386,20 @@ void AnimationFrameRecyclingQueue::AdvanceInternal() {
}
bool AnimationFrameRecyclingQueue::ResetInternal() {
mRecycle.clear();
// We should save any display frames that we can to save on at least the
// allocation. The first frame refresh area is guaranteed to be the aggregate
// dirty rect or the entire frame, and so the bare minimum area we can
// recycle. We don't need to worry about updating the dirty rect for the
// existing mRecycle entries, because that will happen in RecycleFrame when
// we try to pull out a frame to redecode the first frame.
for (RefPtr<imgFrame>& frame : mDisplay) {
if (frame->ShouldRecycle()) {
RecycleEntry newEntry(mFirstFrameRefreshArea);
newEntry.mFrame = std::move(frame);
mRecycle.push_back(std::move(newEntry));
}
}
return AnimationFrameDiscardingQueue::ResetInternal();
}
@ -382,8 +411,6 @@ RawAccessFrameRef AnimationFrameRecyclingQueue::RecycleFrame(
// area. We know that all of the frames still in the recycling queue
// need to take into account the same dirty rect because they are also
// frames which cross the boundary.
MOZ_ASSERT(mSizeKnown);
MOZ_ASSERT(!mFirstFrameRefreshArea.IsEmpty());
for (RecycleEntry& entry : mRecycle) {
MOZ_ASSERT(mFirstFrameRefreshArea.Contains(entry.mDirtyRect));
entry.mDirtyRect = mFirstFrameRefreshArea;
@ -439,11 +466,10 @@ bool AnimationFrameRecyclingQueue::MarkComplete(
bool continueDecoding =
AnimationFrameDiscardingQueue::MarkComplete(aFirstFrameRefreshArea);
MOZ_ASSERT_IF(!mRedecodeError, mFirstFrameRefreshArea.IsEmpty() ||
mFirstFrameRefreshArea.IsEqualEdges(
aFirstFrameRefreshArea));
mFirstFrameRefreshArea = aFirstFrameRefreshArea;
// If we encounter a redecode error, just make the first frame refresh area to
// be the full frame, because we don't really know what we can safely recycle.
mFirstFrameRefreshArea = mRedecodeError ? mFirstFrame->GetRect()
: aFirstFrameRefreshArea;
return continueDecoding;
}

View File

@ -189,10 +189,6 @@ class AnimationFrameBuffer {
MOZ_ASSERT(aFrame);
--mPending;
if (!mSizeKnown) {
++mSize;
}
bool retain = InsertInternal(std::move(aFrame));
if (mAdvance > 0 && mSize > 1) {

View File

@ -289,6 +289,16 @@ bool AnimationSurfaceProvider::CheckForNewFrameAtYield() {
// Append the new frame to the list.
AnimationFrameBuffer::InsertStatus status =
mFrames->Insert(std::move(frame));
// If we hit a redecode error, then we actually want to stop. This happens
// when we tried to insert more frames than we originally had (e.g. the
// original decoder attempt hit an OOM error sooner than we did). Better to
// stop the animation than to get out of sync with FrameAnimator.
if (mFrames->HasRedecodeError()) {
mDecoder = nullptr;
return false;
}
switch (status) {
case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
continueDecoding = true;
@ -353,6 +363,13 @@ bool AnimationSurfaceProvider::CheckForNewFrameAtTerminalState() {
// Append the new frame to the list.
AnimationFrameBuffer::InsertStatus status =
mFrames->Insert(std::move(frame));
// If we hit a redecode error, then we actually want to stop. This will be
// fully handled in FinishDecoding.
if (mFrames->HasRedecodeError()) {
return false;
}
switch (status) {
case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD:

View File

@ -146,7 +146,8 @@ VerifyMarkComplete(AnimationFrameBuffer& aQueue,
if (aQueue.IsRecycling() && !aQueue.SizeKnown()) {
const AnimationFrameRecyclingQueue& queue =
*static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
EXPECT_TRUE(queue.FirstFrameRefreshArea().IsEmpty());
EXPECT_EQ(queue.FirstFrame()->GetRect(),
queue.FirstFrameRefreshArea());
}
bool keepDecoding = aQueue.MarkComplete(aRefreshArea);
@ -194,12 +195,6 @@ VerifyReset(AnimationFrameBuffer& aQueue,
EXPECT_EQ(aFirstFrame, queue.FirstFrame());
EXPECT_EQ(nullptr, aQueue.Get(0, false));
}
if (aQueue.IsRecycling()) {
const AnimationFrameRecyclingQueue& queue =
*static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
EXPECT_EQ(size_t(0), queue.Recycle().size());
}
}
class ImageAnimationFrameBuffer : public ::testing::Test
@ -635,6 +630,104 @@ TEST_F(ImageAnimationFrameBuffer, DiscardingReset)
TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
}
TEST_F(ImageAnimationFrameBuffer, ResetBeforeDiscardingThreshold)
{
const size_t kThreshold = 3;
const size_t kBatch = 1;
const size_t kStartFrame = 0;
// Get the starting buffer to just before the point where we need to switch
// to a discarding buffer, reset the animation so advancing points at the
// first frame, and insert the last frame to cross the threshold.
AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
VerifyInsertAndAdvance(retained, 1, AnimationFrameBuffer::InsertStatus::YIELD);
bool restartDecoder = retained.Reset();
EXPECT_FALSE(restartDecoder);
VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);
const imgFrame* firstFrame = retained.Frames()[0].get();
EXPECT_TRUE(firstFrame != nullptr);
AnimationFrameDiscardingQueue buffer(std::move(retained));
const imgFrame* displayFirstFrame = buffer.Get(0, true);
const imgFrame* advanceFirstFrame = buffer.Get(0, false);
EXPECT_EQ(firstFrame, displayFirstFrame);
EXPECT_EQ(firstFrame, advanceFirstFrame);
}
TEST_F(ImageAnimationFrameBuffer, DiscardingTooFewFrames)
{
const size_t kThreshold = 3;
const size_t kBatch = 1;
const size_t kStartFrame = 0;
// First get us to a discarding buffer state.
AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
VerifyInsertAndAdvance(retained, 1, AnimationFrameBuffer::InsertStatus::YIELD);
VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);
// Insert one more frame.
AnimationFrameDiscardingQueue buffer(std::move(retained));
VerifyAdvance(buffer, 2, true);
VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);
// Mark it as complete.
bool restartDecoder = buffer.MarkComplete(IntRect(0, 0, 1, 1));
EXPECT_FALSE(restartDecoder);
EXPECT_FALSE(buffer.HasRedecodeError());
// Insert one fewer frame than before.
VerifyAdvance(buffer, 3, true);
VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD);
VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD);
VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD);
// When we mark it as complete, it should fail due to too few frames.
restartDecoder = buffer.MarkComplete(IntRect(0, 0, 1, 1));
EXPECT_TRUE(buffer.HasRedecodeError());
EXPECT_EQ(size_t(0), buffer.PendingDecode());
EXPECT_EQ(size_t(4), buffer.Size());
}
TEST_F(ImageAnimationFrameBuffer, DiscardingTooManyFrames)
{
const size_t kThreshold = 3;
const size_t kBatch = 1;
const size_t kStartFrame = 0;
// First get us to a discarding buffer state.
AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
VerifyInsertAndAdvance(retained, 1, AnimationFrameBuffer::InsertStatus::YIELD);
VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);
// Insert one more frame.
AnimationFrameDiscardingQueue buffer(std::move(retained));
VerifyAdvance(buffer, 2, true);
VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);
// Mark it as complete.
bool restartDecoder = buffer.MarkComplete(IntRect(0, 0, 1, 1));
EXPECT_FALSE(restartDecoder);
EXPECT_FALSE(buffer.HasRedecodeError());
// Advance and insert to get us back to the end on the redecode.
VerifyAdvance(buffer, 3, true);
VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD);
VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD);
VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD);
VerifyInsertAndAdvance(buffer, 3, AnimationFrameBuffer::InsertStatus::YIELD);
// Attempt to insert a 5th frame, it should fail.
RefPtr<imgFrame> frame = CreateEmptyFrame();
AnimationFrameBuffer::InsertStatus status = buffer.Insert(std::move(frame));
EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
EXPECT_TRUE(buffer.HasRedecodeError());
EXPECT_EQ(size_t(0), buffer.PendingDecode());
EXPECT_EQ(size_t(4), buffer.Size());
}
TEST_F(ImageAnimationFrameBuffer, RecyclingReset)
{
const size_t kThreshold = 8;
@ -647,6 +740,60 @@ TEST_F(ImageAnimationFrameBuffer, RecyclingReset)
TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
}
TEST_F(ImageAnimationFrameBuffer, RecyclingResetBeforeComplete)
{
const size_t kThreshold = 3;
const size_t kBatch = 1;
const size_t kStartFrame = 0;
const IntSize kImageSize(100, 100);
const IntRect kImageRect(IntPoint(0, 0), kImageSize);
AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
// Get the starting buffer to just before the point where we need to switch
// to a discarding buffer, reset the animation so advancing points at the
// first frame, and insert the last frame to cross the threshold.
RefPtr<imgFrame> frame;
frame = CreateEmptyFrame(kImageSize, kImageRect, false);
AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame));
EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
frame = CreateEmptyFrame(kImageSize, IntRect(IntPoint(10, 10), IntSize(1, 1)), false);
status = retained.Insert(std::move(frame));
EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
VerifyAdvance(retained, 1, true);
frame = CreateEmptyFrame(kImageSize, IntRect(IntPoint(20, 10), IntSize(1, 1)), false);
status = retained.Insert(std::move(frame));
EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_YIELD, status);
AnimationFrameRecyclingQueue buffer(std::move(retained));
bool restartDecoding = buffer.Reset();
EXPECT_TRUE(restartDecoding);
// None of the buffers were recyclable.
EXPECT_TRUE(buffer.Recycle().empty());
// Reinsert the first two frames as recyclable and reset again.
frame = CreateEmptyFrame(kImageSize, kImageRect, true);
status = buffer.Insert(std::move(frame));
EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
frame = CreateEmptyFrame(kImageSize, IntRect(IntPoint(10, 10), IntSize(1, 1)), true);
status = buffer.Insert(std::move(frame));
EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
restartDecoding = buffer.Reset();
EXPECT_TRUE(restartDecoding);
// Now both buffers should have been saved and the dirty rect replaced with
// the full image rect since we don't know the first frame refresh area yet.
EXPECT_EQ(size_t(2), buffer.Recycle().size());
for (const auto& entry : buffer.Recycle()) {
EXPECT_EQ(kImageRect, entry.mDirtyRect);
}
}
TEST_F(ImageAnimationFrameBuffer, RecyclingRect)
{
const size_t kThreshold = 5;

View File

@ -0,0 +1,31 @@
/* -*- 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 "mozilla/ipc/InProcessChild.h"
#include "mozilla/dom/WindowGlobalChild.h"
using namespace mozilla::dom;
namespace mozilla {
namespace ipc {
PWindowGlobalChild*
InProcessChild::AllocPWindowGlobalChild(const WindowGlobalInit& aInit)
{
MOZ_ASSERT_UNREACHABLE("PWindowGlobalChild should not be created manually");
return nullptr;
}
bool
InProcessChild::DeallocPWindowGlobalChild(PWindowGlobalChild* aActor)
{
// Free IPC-held reference
static_cast<WindowGlobalChild*>(aActor)->Release();
return true;
}
} // namespace ipc
} // namespace mozilla

66
ipc/glue/InProcessChild.h Normal file
View File

@ -0,0 +1,66 @@
/* -*- 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/. */
#ifndef mozilla_ipc_InProcessChild_h
#define mozilla_ipc_InProcessChild_h
#include "mozilla/ipc/PInProcessChild.h"
#include "mozilla/StaticPtr.h"
namespace mozilla {
namespace dom {
class PWindowGlobalParent;
class PWindowGlobalChild;
} // namespace dom
namespace ipc {
class InProcessParent;
/**
* The `InProcessChild` class represents the child half of a main-thread to
* main-thread actor.
*
* The `PInProcess` actor should be used as an alternate manager to `PContent`
* for async actors which want to communicate uniformly between Content->Chrome
* and Chrome->Chrome situations.
*/
class InProcessChild : public PInProcessChild
{
public:
friend class InProcessParent;
NS_INLINE_DECL_REFCOUNTING(InProcessChild)
// Get the singleton instance of this actor.
static InProcessChild* Singleton();
// Get the parent side of the in-process child actor |aActor|. If |aActor| is
// not an in-process actor, or is not connected, this method will return
// |nullptr|.
static IProtocol* ParentActorFor(IProtocol* aActor);
protected:
virtual mozilla::dom::PWindowGlobalChild*
AllocPWindowGlobalChild(const WindowGlobalInit& aInit) override;
virtual bool
DeallocPWindowGlobalChild(mozilla::dom::PWindowGlobalChild* aActor) override;
private:
// NOTE: PInProcess lifecycle management is declared as staic methods and
// state on InProcessParent, and implemented in InProcessImpl.cpp.
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual void DeallocPInProcessChild() override;
~InProcessChild() = default;
static StaticRefPtr<InProcessChild> sSingleton;
};
} // namespace ipc
} // namespace mozilla
#endif // defined(mozilla_ipc_InProcessChild_h)

212
ipc/glue/InProcessImpl.cpp Normal file
View File

@ -0,0 +1,212 @@
/* -*- 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 "mozilla/ipc/InProcessParent.h"
#include "mozilla/ipc/InProcessChild.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
// This file contains the implementation of core InProcess lifecycle management
// facilities.
namespace mozilla {
namespace ipc {
StaticRefPtr<InProcessParent> InProcessParent::sSingleton;
StaticRefPtr<InProcessChild> InProcessChild::sSingleton;
bool InProcessParent::sShutdown = false;
//////////////////////////////////////////
// InProcess actor lifecycle management //
//////////////////////////////////////////
/* static */ InProcessChild*
InProcessChild::Singleton() {
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton) {
InProcessParent::Startup();
}
return sSingleton;
}
/* static */ InProcessParent*
InProcessParent::Singleton() {
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton) {
InProcessParent::Startup();
}
return sSingleton;
}
/* static */ void
InProcessParent::Startup()
{
MOZ_ASSERT(NS_IsMainThread());
if (sShutdown) {
NS_WARNING("Could not get in-process actor while shutting down!");
return;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
sShutdown = true;
NS_WARNING("Failed to get nsIObserverService for in-process actor");
return;
}
RefPtr<InProcessParent> parent = new InProcessParent();
RefPtr<InProcessChild> child = new InProcessChild();
// Observe the shutdown event to close & clean up after ourselves.
nsresult rv = obs->AddObserver(parent, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Link the two actors
if (!child->OpenOnSameThread(parent->GetIPCChannel(), ChildSide)) {
MOZ_CRASH("Failed to open InProcessChild!");
}
parent->SetOtherProcessId(base::GetCurrentProcId());
// Create references held by the IPC layer which will be freed in
// DeallocPInProcess{Parent,Child}.
parent.get()->AddRef();
child.get()->AddRef();
// Stash global references to fetch the other side of the reference.
InProcessParent::sSingleton = parent.forget();
InProcessChild::sSingleton = child.forget();
}
/* static */ void
InProcessParent::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton || sShutdown) {
return;
}
sShutdown = true;
RefPtr<InProcessParent> parent = sSingleton;
InProcessParent::sSingleton = nullptr;
InProcessChild::sSingleton = nullptr;
// Calling `Close` on the actor will cause the `Dealloc` methods to be called,
// freeing the remaining references.
parent->Close();
}
NS_IMETHODIMP
InProcessParent::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
InProcessParent::Shutdown();
return NS_OK;
}
void
InProcessParent::ActorDestroy(ActorDestroyReason aWhy)
{
InProcessParent::Shutdown();
}
void
InProcessChild::ActorDestroy(ActorDestroyReason aWhy)
{
InProcessParent::Shutdown();
}
void
InProcessParent::DeallocPInProcessParent()
{
MOZ_ASSERT(!InProcessParent::sSingleton);
Release(); // Release the reference taken in InProcessParent::Startup.
}
void
InProcessChild::DeallocPInProcessChild()
{
MOZ_ASSERT(!InProcessChild::sSingleton);
Release(); // Release the reference taken in InProcessParent::Startup.
}
////////////////////////////////
// In-Process Actor Utilities //
////////////////////////////////
// Helper method for implementing ParentActorFor and ChildActorFor.
static IProtocol*
GetOtherInProcessActor(IProtocol* aActor)
{
MOZ_ASSERT(aActor->GetSide() != UnknownSide, "bad unknown side");
// Discover the manager of aActor which is PInProcess.
IProtocol* current = aActor;
while (current) {
if (current->GetProtocolTypeId() == PInProcessMsgStart) {
break; // Found the correct actor.
}
current = current->Manager();
}
if (!current) {
return nullptr; // Not a PInProcess actor, return |nullptr|
}
MOZ_ASSERT(current->GetSide() == aActor->GetSide(), "side changed?");
MOZ_ASSERT_IF(aActor->GetSide() == ParentSide,
current == InProcessParent::Singleton());
MOZ_ASSERT_IF(aActor->GetSide() == ChildSide,
current == InProcessChild::Singleton());
// Check whether this is InProcessParent or InProcessChild, and get the other
// side's toplevel actor.
IProtocol* otherRoot = nullptr;
if (aActor->GetSide() == ParentSide) {
otherRoot = InProcessChild::Singleton();
} else {
otherRoot = InProcessParent::Singleton();
}
if (NS_WARN_IF(!otherRoot)) {
return nullptr;
}
// Look up the actor on the other side, and return it.
IProtocol* otherActor = otherRoot->Lookup(aActor->Id());
if (otherActor) {
MOZ_ASSERT(otherActor->GetSide() != UnknownSide, "bad unknown side");
MOZ_ASSERT(otherActor->GetSide() != aActor->GetSide(), "Wrong side!");
MOZ_ASSERT(otherActor->GetProtocolTypeId() == aActor->GetProtocolTypeId(),
"Wrong type of protocol!");
}
return otherActor;
}
/* static */ IProtocol*
InProcessParent::ChildActorFor(IProtocol* aActor)
{
MOZ_ASSERT(aActor && aActor->GetSide() == ParentSide);
return GetOtherInProcessActor(aActor);
}
/* static */ IProtocol*
InProcessChild::ParentActorFor(IProtocol* aActor)
{
MOZ_ASSERT(aActor && aActor->GetSide() == ChildSide);
return GetOtherInProcessActor(aActor);
}
} // namespace ipc
} // namespace mozilla

View File

@ -0,0 +1,41 @@
/* -*- 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 "mozilla/ipc/InProcessParent.h"
#include "mozilla/dom/WindowGlobalParent.h"
using namespace mozilla::dom;
namespace mozilla {
namespace ipc {
NS_IMPL_ISUPPORTS(InProcessParent, nsIObserver)
IPCResult
InProcessParent::RecvPWindowGlobalConstructor(PWindowGlobalParent* aActor,
const WindowGlobalInit& aInit)
{
static_cast<WindowGlobalParent*>(aActor)->Init(aInit);
return IPC_OK();
}
PWindowGlobalParent*
InProcessParent::AllocPWindowGlobalParent(const WindowGlobalInit& aInit)
{
// Reference freed in DeallocPWindowGlobalParent.
return do_AddRef(new WindowGlobalParent(aInit, /* inproc */ true)).take();
}
bool
InProcessParent::DeallocPWindowGlobalParent(PWindowGlobalParent* aActor)
{
// Free IPC-held reference.
static_cast<WindowGlobalParent*>(aActor)->Release();
return true;
}
} // namespace ipc
} // namespace mozilla

View File

@ -0,0 +1,76 @@
/* -*- 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/. */
#ifndef mozilla_ipc_InProcessParent_h
#define mozilla_ipc_InProcessParent_h
#include "mozilla/ipc/PInProcessParent.h"
#include "mozilla/StaticPtr.h"
namespace mozilla {
namespace dom {
class PWindowGlobalParent;
class PWindowGlobalChild;
} // namespace dom
namespace ipc {
class InProcessChild;
/**
* The `InProcessParent` class represents the parent half of a main-thread to
* main-thread actor.
*
* The `PInProcess` actor should be used as an alternate manager to `PContent`
* for async actors which want to communicate uniformly between Content->Chrome
* and Chrome->Chrome situations.
*/
class InProcessParent : public nsIObserver
, public PInProcessParent
{
public:
friend class InProcessChild;
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// Get the singleton instance of this actor.
static InProcessParent* Singleton();
// Get the child side of the in-process child actor |aActor|. If |aActor| is
// not an in-process actor, or is not connected, this method will return
// |nullptr|.
static IProtocol* ChildActorFor(IProtocol* aActor);
protected:
virtual mozilla::dom::PWindowGlobalParent*
AllocPWindowGlobalParent(const WindowGlobalInit& aInit) override;
virtual bool
DeallocPWindowGlobalParent(mozilla::dom::PWindowGlobalParent* aActor) override;
virtual IPCResult
RecvPWindowGlobalConstructor(mozilla::dom::PWindowGlobalParent* aActor,
const WindowGlobalInit& aInit) override;
private:
// Lifecycle management is implemented in InProcessImpl.cpp
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual void DeallocPInProcessParent() override;
~InProcessParent() = default;
static void Startup();
static void Shutdown();
static StaticRefPtr<InProcessParent> sSingleton;
static bool sShutdown;
};
} // namespace ipc
} // namespace mozilla
#endif // defined(mozilla_ipc_InProcessParent_h)

View File

@ -584,7 +584,8 @@ MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener)
mPeerPidSet(false),
mPeerPid(-1),
mIsPostponingSends(false),
mBuildIDsConfirmedMatch(false) {
mBuildIDsConfirmedMatch(false),
mIsSameThreadChannel(false) {
MOZ_COUNT_CTOR(ipc::MessageChannel);
#ifdef OS_WIN
@ -901,6 +902,38 @@ void MessageChannel::CommonThreadOpenInit(MessageChannel* aTargetChan,
mSide = aSide;
}
bool MessageChannel::OpenOnSameThread(MessageChannel* aTargetChan,
mozilla::ipc::Side aSide) {
CommonThreadOpenInit(aTargetChan, aSide);
Side oppSide = UnknownSide;
switch (aSide) {
case ChildSide:
oppSide = ParentSide;
break;
case ParentSide:
oppSide = ChildSide;
break;
case UnknownSide:
break;
}
mIsSameThreadChannel = true;
// XXX(nika): Avoid setting up a monitor for same thread channels? We
// shouldn't need it.
mMonitor = new RefCountedMonitor();
mChannelState = ChannelOpening;
aTargetChan->CommonThreadOpenInit(this, oppSide);
aTargetChan->mIsSameThreadChannel = true;
aTargetChan->mMonitor = mMonitor;
mChannelState = ChannelConnected;
aTargetChan->mChannelState = ChannelConnected;
return true;
}
bool MessageChannel::Echo(Message* aMsg) {
UniquePtr<Message> msg(aMsg);
AssertWorkerThread();
@ -1377,6 +1410,8 @@ bool MessageChannel::Send(Message* aMsg, Message* aReply) {
// Sanity checks.
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
MOZ_RELEASE_ASSERT(!mIsSameThreadChannel,
"sync send over same-thread channel will deadlock!");
#ifdef OS_WIN
SyncStackFrame frame(this, false);
@ -1585,6 +1620,8 @@ bool MessageChannel::Call(Message* aMsg, Message* aReply) {
UniquePtr<Message> msg(aMsg);
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
MOZ_RELEASE_ASSERT(!mIsSameThreadChannel,
"intr call send over same-thread channel will deadlock!");
#ifdef OS_WIN
SyncStackFrame frame(this, true);
@ -2300,6 +2337,9 @@ bool MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */) {
}
#endif
MOZ_RELEASE_ASSERT(!mIsSameThreadChannel,
"Wait on same-thread channel will deadlock!");
TimeDuration timeout = (kNoTimeout == mTimeoutMs)
? TimeDuration::Forever()
: TimeDuration::FromMilliseconds(mTimeoutMs);
@ -2580,6 +2620,10 @@ void MessageChannel::SynchronouslyClose() {
AssertWorkerThread();
mMonitor->AssertCurrentThreadOwns();
mLink->SendClose();
MOZ_RELEASE_ASSERT(!mIsSameThreadChannel || ChannelClosed == mChannelState,
"same-thread channel failed to synchronously close?");
while (ChannelClosed != mChannelState) mMonitor->Wait();
}

View File

@ -164,6 +164,15 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver {
bool Open(MessageChannel* aTargetChan, nsIEventTarget* aEventTarget,
Side aSide);
// "Open" a connection to an actor on the current thread.
//
// Returns true if the transport layer was successfully connected,
// i.e., mChannelState == ChannelConnected.
//
// Same-thread channels may not perform synchronous or blocking message
// sends, to avoid deadlocks.
bool OpenOnSameThread(MessageChannel* aTargetChan, Side aSide);
// Close the underlying transport channel.
void Close();
@ -530,10 +539,19 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver {
"not on worker thread!");
}
// The "link" thread is either the I/O thread (ProcessLink) or the
// other actor's work thread (ThreadLink). In either case, it is
// NOT our worker thread.
// The "link" thread is either the I/O thread (ProcessLink), the other
// actor's work thread (ThreadLink), or the worker thread (same-thread
// channels).
void AssertLinkThread() const {
if (mIsSameThreadChannel) {
// If we're a same-thread channel, we have to be on our worker
// thread.
AssertWorkerThread();
return;
}
// If we aren't a same-thread channel, our "link" thread is _not_ our
// worker thread!
MOZ_ASSERT(mWorkerThread, "Channel hasn't been opened yet");
MOZ_RELEASE_ASSERT(mWorkerThread != GetCurrentVirtualThread(),
"on worker thread but should not be!");
@ -827,6 +845,10 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver {
std::vector<UniquePtr<Message>> mPostponedSends;
bool mBuildIDsConfirmedMatch;
// If this is true, both ends of this message channel have event targets
// on the same thread.
bool mIsSameThreadChannel;
};
void CancelCPOWs();

35
ipc/glue/PInProcess.ipdl Normal file
View File

@ -0,0 +1,35 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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 protocol PWindowGlobal;
include DOMTypes;
namespace mozilla {
namespace ipc {
/**
* PInProcess is intended for use as an alternative actor manager to PContent
* for async actors which want to be used uniformly in both Content->Chrome and
* Chrome->Chrome circumstances.
*
* `mozilla::ipc::InProcess{Parent, Child}::Singleton()` should be used to get
* an instance of this actor.
*/
async protocol PInProcess
{
manages PWindowGlobal;
parent:
/**
* Construct a new WindowGlobal actor for a window global in the given
* BrowsingContext and with the given principal.
*/
async PWindowGlobal(WindowGlobalInit init);
};
} // namespace ipc
} // namespace mozilla

View File

@ -627,6 +627,11 @@ bool IToplevelProtocol::OpenWithAsyncPid(mozilla::ipc::Transport* aTransport,
return GetIPCChannel()->Open(aTransport, aThread, aSide);
}
bool IToplevelProtocol::OpenOnSameThread(MessageChannel* aChannel, Side aSide) {
SetOtherProcessId(base::GetCurrentProcId());
return GetIPCChannel()->OpenOnSameThread(aChannel, aSide);
}
void IToplevelProtocol::Close() { GetIPCChannel()->Close(); }
void IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs) {
@ -637,13 +642,29 @@ bool IToplevelProtocol::IsOnCxxStack() const {
return GetIPCChannel()->IsOnCxxStack();
}
int32_t IToplevelProtocol::ToplevelState::NextId() {
// Genreate the next ID to use for a shared memory or protocol. Parent and
// Child sides of the protocol use different pools, and actors created in the
// middleman need to use a distinct pool as well.
int32_t tag = 0;
if (recordreplay::IsMiddleman()) {
tag |= 1 << 0;
}
if (mProtocol->GetSide() == ParentSide) {
tag |= 1 << 1;
}
// Compute the ID to use with the low two bits as our tag, and the remaining
// bits as a monotonic.
return (++mLastLocalId << 2) | tag;
}
int32_t IToplevelProtocol::ToplevelState::Register(IProtocol* aRouted) {
if (aRouted->Id() != kNullActorId && aRouted->Id() != kFreedActorId) {
// If there's already an ID, just return that.
return aRouted->Id();
}
int32_t id =
mProtocol->GetSide() == ParentSide ? ++mLastRouteId : --mLastRouteId;
int32_t id = NextId();
mActorMap.AddWithID(aRouted, id);
aRouted->SetId(id);
@ -682,8 +703,7 @@ IToplevelProtocol::ToplevelState::ToplevelState(const char* aName,
Side aSide)
: ProtocolState(),
mProtocol(aProtocol),
mLastRouteId(aSide == ParentSide ? kFreedActorId : kNullActorId),
mLastShmemId(aSide == ParentSide ? kFreedActorId : kNullActorId),
mLastLocalId(0),
mEventTargetMutex("ProtocolEventTargetMutex"),
mChannel(aName, aProtocol) {}
@ -696,8 +716,7 @@ Shmem::SharedMemory* IToplevelProtocol::ToplevelState::CreateSharedMemory(
if (!segment) {
return nullptr;
}
int32_t id =
mProtocol->GetSide() == ParentSide ? ++mLastShmemId : --mLastShmemId;
int32_t id = NextId();
Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), id);
base::ProcessId pid =

View File

@ -438,11 +438,12 @@ class IToplevelProtocol : public IProtocol {
MessageChannel* GetIPCChannel() override;
private:
int32_t NextId();
IToplevelProtocol* const mProtocol;
int32_t mLastLocalId;
IDMap<IProtocol*> mActorMap;
int32_t mLastRouteId;
IDMap<Shmem::SharedMemory*> mShmemMap;
Shmem::id_t mLastShmemId;
Mutex mEventTargetMutex;
IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap;
@ -482,6 +483,15 @@ class IToplevelProtocol : public IProtocol {
MessageLoop* aThread = nullptr,
mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
// Open a toplevel actor such that both ends of the actor's channel are on
// the same thread. This method should be called on the thread to perform
// the link.
//
// WARNING: Attempting to send a sync or intr message on the same thread
// will crash.
bool OpenOnSameThread(MessageChannel* aChannel,
mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
void Close();
void SetReplyTimeoutMs(int32_t aTimeoutMs);

View File

@ -27,6 +27,8 @@ EXPORTS.mozilla.ipc += [
'FileDescriptorSetParent.h',
'FileDescriptorUtils.h',
'GeckoChildProcessHost.h',
'InProcessChild.h',
'InProcessParent.h',
'InputStreamUtils.h',
'IOThreadChild.h',
'IPCStreamAlloc.h',
@ -147,6 +149,9 @@ UNIFIED_SOURCES += [
'CrashReporterMetadataShmem.cpp',
'FileDescriptor.cpp',
'FileDescriptorUtils.cpp',
'InProcessChild.cpp',
'InProcessImpl.cpp',
'InProcessParent.cpp',
'InputStreamUtils.cpp',
'IPCMessageUtils.cpp',
'IPCStreamChild.cpp',
@ -208,6 +213,7 @@ IPDL_SOURCES = [
'PBackgroundTest.ipdl',
'PChildToParentStream.ipdl',
'PFileDescriptorSet.ipdl',
'PInProcess.ipdl',
'PParentToChildStream.ipdl',
'ProtocolTypes.ipdlh',
'URIParams.ipdlh',

View File

@ -3910,13 +3910,31 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
msgvar, stmts = self.makeMessage(md, errfnSendCtor)
sendok, sendstmts = self.sendAsync(md, msgvar)
warnif = StmtIf(ExprNot(sendok))
warnif.addifstmt(_printWarningMessage('Error sending constructor'))
method.addstmts(
# Build our constructor message & verify it.
stmts
+ self.genVerifyMessage(md.decl.type.verify, md.params,
errfnSendCtor, ExprVar('msg__'))
# Notify the other side about the newly created actor.
#
# If the MessageChannel is closing, and we haven't been told yet,
# this send may fail. This error is ignored to treat it like a
# message being lost due to the other side shutting down before
# processing it.
#
# NOTE: We don't free the actor here, as our caller may be
# depending on it being alive after calling SendConstructor.
+ sendstmts
+ self.failCtorIf(md, ExprNot(sendok))
+ [StmtReturn(actor.var())])
# Warn if the message failed to send, and return our newly created
# actor.
+ [warnif,
StmtReturn(actor.var())])
lbl = CaseLabel(md.pqReplyId())
case = StmtBlock()

View File

@ -1,6 +1,7 @@
add_task(async function() {
const kPrefName_AutoScroll = "general.autoScroll";
Services.prefs.setBoolPref(kPrefName_AutoScroll, true);
registerCleanupFunction(() => Services.prefs.clearUserPref(kPrefName_AutoScroll));
const kNoKeyEvents = 0;
const kKeyDownEvent = 1;
@ -61,55 +62,52 @@ add_task(async function() {
var dataUri = 'data:text/html,<body style="height:10000px;"></body>';
let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
BrowserTestUtils.loadURI(gBrowser, dataUri);
await loadedPromise;
await BrowserTestUtils.withNewTab(dataUri, async function(browser) {
info("Loaded data URI in new tab");
await SimpleTest.promiseFocus(browser);
info("Focused selected browser");
await SimpleTest.promiseFocus(gBrowser.selectedBrowser);
window.addEventListener("keydown", onKey);
window.addEventListener("keypress", onKey);
window.addEventListener("keyup", onKey);
registerCleanupFunction(() => {
window.removeEventListener("keydown", onKey);
window.removeEventListener("keypress", onKey);
window.removeEventListener("keyup", onKey);
});
window.addEventListener("keydown", onKey);
window.addEventListener("keypress", onKey);
window.addEventListener("keyup", onKey);
// Test whether the key events are handled correctly under normal condition
expectedKeyEvents = kAllKeyEvents;
sendChar("A");
// Test whether the key events are handled correctly under normal condition
expectedKeyEvents = kAllKeyEvents;
sendChar("A");
// Start autoscrolling by middle button click on the page
info("Creating popup shown promise");
let shownPromise = BrowserTestUtils.waitForEvent(window, "popupshown", false,
event => event.originalTarget.className == "autoscroller");
await BrowserTestUtils.synthesizeMouseAtPoint(10, 10, { button: 1 },
gBrowser.selectedBrowser);
info("Waiting for autoscroll popup to show");
await shownPromise;
// Start autoscrolling by middle button click on the page
let shownPromise = BrowserTestUtils.waitForEvent(window, "popupshown", false,
event => event.originalTarget.className == "autoscroller");
await BrowserTestUtils.synthesizeMouseAtPoint(10, 10, { button: 1 },
gBrowser.selectedBrowser);
await shownPromise;
// Most key events should be eaten by the browser.
expectedKeyEvents = kNoKeyEvents;
sendChar("A");
sendKey("DOWN");
sendKey("RETURN");
sendKey("RETURN");
sendKey("HOME");
sendKey("END");
sendKey("TAB");
sendKey("RETURN");
// Most key events should be eaten by the browser.
expectedKeyEvents = kNoKeyEvents;
sendChar("A");
sendKey("DOWN");
sendKey("RETURN");
sendKey("RETURN");
sendKey("HOME");
sendKey("END");
sendKey("TAB");
sendKey("RETURN");
// Finish autoscrolling by ESC key. Note that only keydown and keypress
// events are eaten because keyup event is fired *after* the autoscrolling
// is finished.
expectedKeyEvents = kKeyUpEvent;
sendKey("ESCAPE");
// Finish autoscrolling by ESC key. Note that only keydown and keypress
// events are eaten because keyup event is fired *after* the autoscrolling
// is finished.
expectedKeyEvents = kKeyUpEvent;
sendKey("ESCAPE");
// Test whether the key events are handled correctly under normal condition
expectedKeyEvents = kAllKeyEvents;
sendChar("A");
window.removeEventListener("keydown", onKey);
window.removeEventListener("keypress", onKey);
window.removeEventListener("keyup", onKey);
// restore the changed prefs
if (Services.prefs.prefHasUserValue(kPrefName_AutoScroll))
Services.prefs.clearUserPref(kPrefName_AutoScroll);
finish();
// Test whether the key events are handled correctly under normal condition
expectedKeyEvents = kAllKeyEvents;
sendChar("A");
});
});