2018-07-26 10:31:00 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2018-10-20 01:00:59 +03:00
|
|
|
#include "mozilla/dom/BrowsingContext.h"
|
2018-07-26 10:31:00 +03:00
|
|
|
|
2019-02-21 23:14:28 +03:00
|
|
|
#include "ipc/IPCMessageUtils.h"
|
|
|
|
|
2019-01-29 20:32:28 +03:00
|
|
|
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
2019-01-30 19:07:21 +03:00
|
|
|
#include "mozilla/dom/BrowsingContextGroup.h"
|
2018-08-29 05:00:00 +03:00
|
|
|
#include "mozilla/dom/BrowsingContextBinding.h"
|
2018-07-26 10:31:00 +03:00
|
|
|
#include "mozilla/dom/ContentChild.h"
|
2018-11-05 15:43:10 +03:00
|
|
|
#include "mozilla/dom/ContentParent.h"
|
2019-04-17 03:51:36 +03:00
|
|
|
#include "mozilla/dom/Element.h"
|
2019-01-02 16:29:18 +03:00
|
|
|
#include "mozilla/dom/Location.h"
|
|
|
|
#include "mozilla/dom/LocationBinding.h"
|
2019-07-02 01:24:09 +03:00
|
|
|
#include "mozilla/dom/StructuredCloneTags.h"
|
2019-01-02 16:29:18 +03:00
|
|
|
#include "mozilla/dom/WindowBinding.h"
|
2019-04-17 03:51:36 +03:00
|
|
|
#include "mozilla/dom/WindowGlobalChild.h"
|
|
|
|
#include "mozilla/dom/WindowGlobalParent.h"
|
2019-01-03 10:11:00 +03:00
|
|
|
#include "mozilla/dom/WindowProxyHolder.h"
|
2018-07-26 10:31:00 +03:00
|
|
|
#include "mozilla/Assertions.h"
|
|
|
|
#include "mozilla/ClearOnShutdown.h"
|
2018-12-17 13:45:37 +03:00
|
|
|
#include "mozilla/HashTable.h"
|
2018-07-26 10:31:00 +03:00
|
|
|
#include "mozilla/Logging.h"
|
|
|
|
#include "mozilla/StaticPtr.h"
|
|
|
|
|
2018-11-05 15:43:10 +03:00
|
|
|
#include "nsDocShell.h"
|
2019-01-02 16:29:18 +03:00
|
|
|
#include "nsGlobalWindowOuter.h"
|
2018-07-26 10:31:00 +03:00
|
|
|
#include "nsContentUtils.h"
|
2019-01-02 16:29:18 +03:00
|
|
|
#include "nsScriptError.h"
|
2018-06-29 02:41:00 +03:00
|
|
|
#include "nsThreadUtils.h"
|
2019-02-28 21:23:15 +03:00
|
|
|
#include "xpcprivate.h"
|
2018-07-26 10:31:00 +03:00
|
|
|
|
2019-03-20 06:15:36 +03:00
|
|
|
#include "AutoplayPolicy.h"
|
|
|
|
|
|
|
|
extern mozilla::LazyLogModule gAutoplayPermissionLog;
|
|
|
|
|
|
|
|
#define AUTOPLAY_LOG(msg, ...) \
|
|
|
|
MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
|
|
|
|
2018-07-26 10:31:00 +03:00
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2019-01-15 02:09:42 +03:00
|
|
|
extern mozilla::LazyLogModule gUserInteractionPRLog;
|
|
|
|
|
|
|
|
#define USER_ACTIVATION_LOG(msg, ...) \
|
|
|
|
MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
|
|
|
|
2018-07-26 10:31:00 +03:00
|
|
|
static LazyLogModule gBrowsingContextLog("BrowsingContext");
|
|
|
|
|
2019-05-24 23:15:53 +03:00
|
|
|
typedef nsDataHashtable<nsUint64HashKey, BrowsingContext*> BrowsingContextMap;
|
2018-12-17 13:45:37 +03:00
|
|
|
|
2019-05-24 23:15:53 +03:00
|
|
|
static StaticAutoPtr<BrowsingContextMap> sBrowsingContexts;
|
2018-07-26 10:31:00 +03:00
|
|
|
|
2018-11-05 15:43:10 +03:00
|
|
|
static void Register(BrowsingContext* aBrowsingContext) {
|
2019-05-24 23:15:53 +03:00
|
|
|
sBrowsingContexts->Put(aBrowsingContext->Id(), aBrowsingContext);
|
2019-01-30 19:07:21 +03:00
|
|
|
|
|
|
|
aBrowsingContext->Group()->Register(aBrowsingContext);
|
2018-11-05 15:43:10 +03:00
|
|
|
}
|
|
|
|
|
2019-07-02 23:48:13 +03:00
|
|
|
void BrowsingContext::Unregister() {
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mGroup);
|
|
|
|
mGroup->Unregister(this);
|
|
|
|
mIsDiscarded = true;
|
2019-07-08 20:27:27 +03:00
|
|
|
|
|
|
|
// NOTE: Doesn't use SetClosed, as it will be set in all processes
|
|
|
|
// automatically by calls to Detach()
|
|
|
|
mClosed = true;
|
2019-07-02 23:48:13 +03:00
|
|
|
}
|
|
|
|
|
2019-04-09 09:19:24 +03:00
|
|
|
BrowsingContext* BrowsingContext::Top() {
|
2019-01-15 02:09:42 +03:00
|
|
|
BrowsingContext* bc = this;
|
|
|
|
while (bc->mParent) {
|
|
|
|
bc = bc->mParent;
|
|
|
|
}
|
|
|
|
return bc;
|
|
|
|
}
|
|
|
|
|
2019-02-26 01:04:59 +03:00
|
|
|
/* static */
|
|
|
|
void BrowsingContext::Init() {
|
2018-07-26 10:31:00 +03:00
|
|
|
if (!sBrowsingContexts) {
|
2019-05-24 23:15:53 +03:00
|
|
|
sBrowsingContexts = new BrowsingContextMap();
|
2018-07-26 10:31:00 +03:00
|
|
|
ClearOnShutdown(&sBrowsingContexts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-26 01:04:59 +03:00
|
|
|
/* static */
|
|
|
|
LogModule* BrowsingContext::GetLog() { return gBrowsingContextLog; }
|
2018-07-26 10:31:00 +03:00
|
|
|
|
2019-02-26 01:04:59 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<BrowsingContext> BrowsingContext::Get(uint64_t aId) {
|
2019-05-24 23:15:53 +03:00
|
|
|
return do_AddRef(sBrowsingContexts->Get(aId));
|
2018-07-26 10:31:00 +03:00
|
|
|
}
|
|
|
|
|
2019-06-19 23:06:32 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<BrowsingContext> BrowsingContext::GetFromWindow(
|
|
|
|
WindowProxyHolder& aProxy) {
|
|
|
|
return do_AddRef(aProxy.get());
|
|
|
|
}
|
|
|
|
|
2019-02-14 00:02:55 +03:00
|
|
|
CanonicalBrowsingContext* BrowsingContext::Canonical() {
|
|
|
|
return CanonicalBrowsingContext::Cast(this);
|
|
|
|
}
|
|
|
|
|
2019-02-26 01:04:59 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<BrowsingContext> BrowsingContext::Create(
|
2018-11-05 15:43:10 +03:00
|
|
|
BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
|
|
|
|
Type aType) {
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->mType == aType);
|
|
|
|
|
|
|
|
uint64_t id = nsContentUtils::GenerateBrowsingContextId();
|
|
|
|
|
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
|
|
("Creating 0x%08" PRIx64 " in %s", id,
|
|
|
|
XRE_IsParentProcess() ? "Parent" : "Child"));
|
|
|
|
|
2019-03-14 21:51:09 +03:00
|
|
|
// Determine which BrowsingContextGroup this context should be created in.
|
|
|
|
RefPtr<BrowsingContextGroup> group =
|
|
|
|
BrowsingContextGroup::Select(aParent, aOpener);
|
|
|
|
|
2018-09-14 17:57:18 +03:00
|
|
|
RefPtr<BrowsingContext> context;
|
|
|
|
if (XRE_IsParentProcess()) {
|
2019-03-14 21:51:09 +03:00
|
|
|
context = new CanonicalBrowsingContext(aParent, group, id,
|
2019-01-29 20:32:28 +03:00
|
|
|
/* aProcessId */ 0, aType);
|
2018-09-14 17:57:18 +03:00
|
|
|
} else {
|
2019-03-14 21:51:09 +03:00
|
|
|
context = new BrowsingContext(aParent, group, id, aType);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The name and opener fields need to be explicitly initialized. Don't bother
|
|
|
|
// using transactions to set them, as we haven't been attached yet.
|
|
|
|
context->mName = aName;
|
2019-03-14 21:51:11 +03:00
|
|
|
context->mOpenerId = aOpener ? aOpener->Id() : 0;
|
2019-06-04 15:24:56 +03:00
|
|
|
context->mCrossOriginPolicy = nsILoadInfo::CROSS_ORIGIN_POLICY_NULL;
|
|
|
|
context->mInheritedCrossOriginPolicy = nsILoadInfo::CROSS_ORIGIN_POLICY_NULL;
|
2019-03-14 21:51:09 +03:00
|
|
|
|
2019-04-09 23:10:13 +03:00
|
|
|
BrowsingContext* inherit = aParent ? aParent : aOpener;
|
|
|
|
if (inherit) {
|
|
|
|
context->mOpenerPolicy = inherit->mOpenerPolicy;
|
2019-06-04 15:24:56 +03:00
|
|
|
context->mInheritedCrossOriginPolicy = inherit->mCrossOriginPolicy;
|
2018-09-14 17:57:18 +03:00
|
|
|
}
|
|
|
|
|
2018-11-05 15:43:10 +03:00
|
|
|
Register(context);
|
|
|
|
|
|
|
|
// Attach the browsing context to the tree.
|
|
|
|
context->Attach();
|
|
|
|
|
2018-09-14 17:57:18 +03:00
|
|
|
return context.forget();
|
|
|
|
}
|
|
|
|
|
2019-02-26 01:04:59 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<BrowsingContext> BrowsingContext::CreateFromIPC(
|
2019-03-14 21:51:09 +03:00
|
|
|
BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup,
|
|
|
|
ContentParent* aOriginProcess) {
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aGroup);
|
|
|
|
|
2019-05-02 11:50:12 +03:00
|
|
|
uint64_t originId = 0;
|
|
|
|
if (aOriginProcess) {
|
|
|
|
originId = aOriginProcess->ChildID();
|
|
|
|
aGroup->EnsureSubscribed(aOriginProcess);
|
|
|
|
}
|
2018-11-05 15:43:10 +03:00
|
|
|
|
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
2019-03-14 21:51:09 +03:00
|
|
|
("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
|
|
|
|
aInit.mId, originId));
|
|
|
|
|
|
|
|
RefPtr<BrowsingContext> parent = aInit.GetParent();
|
2018-11-05 15:43:10 +03:00
|
|
|
|
|
|
|
RefPtr<BrowsingContext> context;
|
|
|
|
if (XRE_IsParentProcess()) {
|
2019-03-14 21:51:09 +03:00
|
|
|
context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
|
|
|
|
Type::Content);
|
2018-11-05 15:43:10 +03:00
|
|
|
} else {
|
2019-03-14 21:51:09 +03:00
|
|
|
context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content);
|
2018-11-05 15:43:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Register(context);
|
|
|
|
|
2019-03-14 21:51:11 +03:00
|
|
|
// Initialize all of our fields from IPC. We don't have to worry about
|
|
|
|
// mOpenerId, as we won't try to dereference it immediately.
|
2019-03-14 21:51:09 +03:00
|
|
|
#define MOZ_BC_FIELD(name, ...) context->m##name = aInit.m##name;
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
|
2019-03-14 21:50:38 +03:00
|
|
|
// Caller handles attaching us to the tree.
|
2018-11-05 15:43:10 +03:00
|
|
|
|
|
|
|
return context.forget();
|
2018-07-26 10:31:00 +03:00
|
|
|
}
|
|
|
|
|
2018-11-05 15:43:10 +03:00
|
|
|
BrowsingContext::BrowsingContext(BrowsingContext* aParent,
|
2019-03-14 21:51:09 +03:00
|
|
|
BrowsingContextGroup* aGroup,
|
2018-11-05 15:43:10 +03:00
|
|
|
uint64_t aBrowsingContextId, Type aType)
|
2019-03-14 21:51:09 +03:00
|
|
|
: mType(aType),
|
2018-11-05 15:43:10 +03:00
|
|
|
mBrowsingContextId(aBrowsingContextId),
|
2019-03-14 21:51:09 +03:00
|
|
|
mGroup(aGroup),
|
2019-03-06 00:11:48 +03:00
|
|
|
mParent(aParent),
|
2019-07-02 23:48:13 +03:00
|
|
|
mIsInProcess(false),
|
|
|
|
mIsDiscarded(false) {
|
2019-03-14 21:51:09 +03:00
|
|
|
MOZ_RELEASE_ASSERT(!mParent || mParent->Group() == mGroup);
|
|
|
|
MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
|
|
|
|
MOZ_RELEASE_ASSERT(mGroup);
|
2018-12-17 13:45:37 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-11-05 15:43:10 +03:00
|
|
|
void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
|
|
|
|
// XXX(nika): We should communicate that we are now an active BrowsingContext
|
|
|
|
// process to the parent & do other validation here.
|
|
|
|
MOZ_RELEASE_ASSERT(nsDocShell::Cast(aDocShell)->GetBrowsingContext() == this);
|
|
|
|
mDocShell = aDocShell;
|
Bug 1529684 - Part 6: Store a mIsInProcess flag to preserve WindowProxy behaviour, r=farre
Currently when we have an in-process WindowProxy object, we will attempt
to either use the cached mWindowProxy value, or fetch the
nsGlobalWindowOuter object from through the nsDocShell. Unfortunately,
when the BrowsingContext is detached, we will fail to get the
nsGlobalWindowOuter object. This happens to be OK for our test cases, as
the cached mWindowProxy value doesn't have the chance to go away, but
isn't acceptable long-term.
These patches exascerbated that issue by causing the nsDocShell pointer
itself to be cleared when it is destroyed, which caused the Remote
WindowProxy logic to be triggered. To deal with that case, this patch
adds a new mIsInProcess flag to continue to act like the old code-path.
In the future, we will need to also handle ensuring that the
nsGlobalWindowOuter lives for long enough, however that is not being
done in this patch in order to land it sooner rather than later.
Depends on D22763
Differential Revision: https://phabricator.services.mozilla.com/D22764
--HG--
extra : moz-landing-system : lando
2019-03-14 21:50:54 +03:00
|
|
|
mIsInProcess = true;
|
2018-11-05 15:43:10 +03:00
|
|
|
}
|
|
|
|
|
2019-04-17 03:51:36 +03:00
|
|
|
void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
|
|
|
|
// Notify the parent process of the embedding status. We don't need to do
|
|
|
|
// this when clearing our embedder, as we're being destroyed either way.
|
2019-04-17 03:53:24 +03:00
|
|
|
if (aEmbedder) {
|
|
|
|
nsCOMPtr<nsIDocShell> container =
|
|
|
|
do_QueryInterface(aEmbedder->OwnerDoc()->GetContainer());
|
|
|
|
|
|
|
|
// If our embedder element is being mutated to a different embedder, and we
|
|
|
|
// have a parent edge, bad things might be happening!
|
|
|
|
//
|
|
|
|
// XXX: This is a workaround to some parent edges not being immutable in the
|
|
|
|
// parent process. It can be fixed once bug 1539979 has been fixed.
|
|
|
|
if (mParent && mEmbedderElement && mEmbedderElement != aEmbedder) {
|
|
|
|
NS_WARNING("Non root content frameLoader swap! This will crash soon!");
|
|
|
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mType == Type::Chrome, "must be chrome");
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "must be in parent");
|
2019-07-02 23:48:13 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mGroup->IsContextCached(this),
|
2019-04-29 10:01:50 +03:00
|
|
|
"cannot be in bfcache");
|
2019-04-17 03:53:24 +03:00
|
|
|
|
|
|
|
RefPtr<BrowsingContext> kungFuDeathGrip(this);
|
|
|
|
RefPtr<BrowsingContext> newParent;
|
|
|
|
container->GetBrowsingContext(getter_AddRefs(newParent));
|
|
|
|
mParent->mChildren.RemoveElement(this);
|
|
|
|
if (newParent) {
|
|
|
|
newParent->mChildren.AppendElement(this);
|
|
|
|
}
|
|
|
|
mParent = newParent;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindowInner> inner =
|
|
|
|
do_QueryInterface(aEmbedder->GetOwnerGlobal());
|
|
|
|
if (inner) {
|
|
|
|
RefPtr<WindowGlobalChild> wgc = inner->GetWindowGlobalChild();
|
|
|
|
|
|
|
|
// If we're in-process, synchronously perform the update to ensure we
|
|
|
|
// don't get out of sync.
|
|
|
|
// XXX(nika): This is super gross, and I don't like it one bit.
|
|
|
|
if (RefPtr<WindowGlobalParent> wgp = wgc->GetParentActor()) {
|
|
|
|
Canonical()->SetEmbedderWindowGlobal(wgp);
|
|
|
|
} else {
|
|
|
|
wgc->SendDidEmbedBrowsingContext(this);
|
|
|
|
}
|
2019-04-17 03:51:36 +03:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 03:53:24 +03:00
|
|
|
|
|
|
|
mEmbedderElement = aEmbedder;
|
2019-04-17 03:51:36 +03:00
|
|
|
}
|
|
|
|
|
2019-03-14 21:50:38 +03:00
|
|
|
void BrowsingContext::Attach(bool aFromIPC) {
|
2018-07-26 10:31:00 +03:00
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
2019-04-29 14:38:45 +03:00
|
|
|
("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64,
|
|
|
|
XRE_IsParentProcess() ? "Parent" : "Child", Id(),
|
|
|
|
mParent ? mParent->Id() : 0));
|
2018-12-17 13:45:37 +03:00
|
|
|
|
2019-07-02 23:48:13 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mGroup);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mGroup->IsContextCached(this));
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
|
2018-07-26 10:31:00 +03:00
|
|
|
|
2019-01-30 19:07:21 +03:00
|
|
|
auto* children = mParent ? &mParent->mChildren : &mGroup->Toplevels();
|
2018-12-17 13:45:37 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!children->Contains(this));
|
|
|
|
|
|
|
|
children->AppendElement(this);
|
2018-07-26 10:31:00 +03:00
|
|
|
|
2019-04-04 19:17:47 +03:00
|
|
|
if (!aFromIPC) {
|
|
|
|
// Send attach to our parent if we need to.
|
|
|
|
if (XRE_IsContentProcess()) {
|
|
|
|
ContentChild::GetSingleton()->SendAttachBrowsingContext(
|
|
|
|
GetIPCInitializer());
|
|
|
|
} else if (IsContent()) {
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
|
2019-07-02 23:48:13 +03:00
|
|
|
mGroup->EachParent([&](ContentParent* aParent) {
|
2019-04-30 11:45:41 +03:00
|
|
|
Unused << aParent->SendAttachBrowsingContext(GetIPCInitializer());
|
|
|
|
});
|
2019-04-04 19:17:47 +03:00
|
|
|
}
|
2019-03-14 21:50:38 +03:00
|
|
|
}
|
2018-07-26 10:31:00 +03:00
|
|
|
}
|
|
|
|
|
2019-03-14 21:50:38 +03:00
|
|
|
void BrowsingContext::Detach(bool aFromIPC) {
|
2018-07-26 10:31:00 +03:00
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
|
|
("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
|
|
|
|
XRE_IsParentProcess() ? "Parent" : "Child", Id(),
|
|
|
|
mParent ? mParent->Id() : 0));
|
|
|
|
|
2019-07-02 23:48:13 +03:00
|
|
|
// Unlinking might remove our group before Detach gets called.
|
|
|
|
if (NS_WARN_IF(!mGroup)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-17 13:45:37 +03:00
|
|
|
RefPtr<BrowsingContext> kungFuDeathGrip(this);
|
|
|
|
|
2019-07-02 23:48:13 +03:00
|
|
|
if (!mGroup->EvictCachedContext(this)) {
|
2019-03-14 21:51:13 +03:00
|
|
|
Children* children = nullptr;
|
|
|
|
if (mParent) {
|
|
|
|
children = &mParent->mChildren;
|
2019-07-02 23:48:13 +03:00
|
|
|
} else {
|
2019-03-14 21:51:13 +03:00
|
|
|
children = &mGroup->Toplevels();
|
|
|
|
}
|
|
|
|
|
2019-07-02 23:48:13 +03:00
|
|
|
children->RemoveElement(this);
|
2019-03-14 21:51:13 +03:00
|
|
|
}
|
2019-03-14 21:50:45 +03:00
|
|
|
|
2019-06-25 09:36:36 +03:00
|
|
|
// As our nsDocShell is going away, this should implicitly mark us as closed.
|
|
|
|
// We directly set our member, rather than using a transaction as we're going
|
|
|
|
// to send a `Detach` message to other processes either way.
|
2019-07-02 23:48:13 +03:00
|
|
|
Unregister();
|
Bug 1529684 - Part 6: Store a mIsInProcess flag to preserve WindowProxy behaviour, r=farre
Currently when we have an in-process WindowProxy object, we will attempt
to either use the cached mWindowProxy value, or fetch the
nsGlobalWindowOuter object from through the nsDocShell. Unfortunately,
when the BrowsingContext is detached, we will fail to get the
nsGlobalWindowOuter object. This happens to be OK for our test cases, as
the cached mWindowProxy value doesn't have the chance to go away, but
isn't acceptable long-term.
These patches exascerbated that issue by causing the nsDocShell pointer
itself to be cleared when it is destroyed, which caused the Remote
WindowProxy logic to be triggered. To deal with that case, this patch
adds a new mIsInProcess flag to continue to act like the old code-path.
In the future, we will need to also handle ensuring that the
nsGlobalWindowOuter lives for long enough, however that is not being
done in this patch in order to land it sooner rather than later.
Depends on D22763
Differential Revision: https://phabricator.services.mozilla.com/D22764
--HG--
extra : moz-landing-system : lando
2019-03-14 21:50:54 +03:00
|
|
|
|
2019-03-14 21:50:38 +03:00
|
|
|
if (!aFromIPC && XRE_IsContentProcess()) {
|
|
|
|
auto cc = ContentChild::GetSingleton();
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
2019-06-25 09:36:36 +03:00
|
|
|
cc->SendDetachBrowsingContext(this);
|
2018-07-26 10:31:00 +03:00
|
|
|
}
|
2018-06-28 05:40:00 +03:00
|
|
|
}
|
|
|
|
|
2019-06-06 17:57:18 +03:00
|
|
|
void BrowsingContext::PrepareForProcessChange() {
|
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
|
|
("%s: Preparing 0x%08" PRIx64 " for a process change",
|
|
|
|
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
|
|
|
|
|
|
|
|
MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
|
2019-07-08 20:27:27 +03:00
|
|
|
MOZ_ASSERT(!mIsDiscarded, "We're already closed?");
|
2019-06-06 17:57:18 +03:00
|
|
|
|
|
|
|
mIsInProcess = false;
|
|
|
|
|
|
|
|
// XXX: We should transplant our WindowProxy into a Cross-Process WindowProxy
|
|
|
|
// if mWindowProxy is non-nullptr. (bug 1510760)
|
|
|
|
mWindowProxy = nullptr;
|
|
|
|
|
|
|
|
// NOTE: For now, clear our nsDocShell reference, as we're primarily in a
|
|
|
|
// different process now. This may need to change in the future with
|
|
|
|
// Cross-Process BFCache.
|
|
|
|
mDocShell = nullptr;
|
|
|
|
}
|
|
|
|
|
2019-03-14 21:50:38 +03:00
|
|
|
void BrowsingContext::CacheChildren(bool aFromIPC) {
|
2018-06-28 05:40:00 +03:00
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
|
|
("%s: Caching children of 0x%08" PRIx64 "",
|
|
|
|
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
|
|
|
|
|
2019-07-02 23:48:13 +03:00
|
|
|
mGroup->CacheContexts(mChildren);
|
2018-12-17 13:45:37 +03:00
|
|
|
mChildren.Clear();
|
2018-06-28 05:40:00 +03:00
|
|
|
|
2019-03-14 21:50:38 +03:00
|
|
|
if (!aFromIPC && XRE_IsContentProcess()) {
|
|
|
|
auto cc = ContentChild::GetSingleton();
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
2019-04-29 14:38:45 +03:00
|
|
|
cc->SendCacheBrowsingContextChildren(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::RestoreChildren(Children&& aChildren, bool aFromIPC) {
|
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
|
|
("%s: Restoring children of 0x%08" PRIx64 "",
|
|
|
|
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
|
|
|
|
|
2019-04-30 19:11:03 +03:00
|
|
|
for (BrowsingContext* child : aChildren) {
|
2019-04-29 14:38:45 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(child->GetParent() == this);
|
2019-07-02 23:48:13 +03:00
|
|
|
Unused << mGroup->EvictCachedContext(child);
|
2019-04-29 14:38:45 +03:00
|
|
|
}
|
|
|
|
|
2019-05-06 19:14:40 +03:00
|
|
|
mChildren.AppendElements(aChildren);
|
2019-04-29 14:38:45 +03:00
|
|
|
|
|
|
|
if (!aFromIPC && XRE_IsContentProcess()) {
|
|
|
|
auto cc = ContentChild::GetSingleton();
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
2019-05-28 10:40:00 +03:00
|
|
|
cc->SendRestoreBrowsingContextChildren(this, aChildren);
|
2018-06-28 05:40:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-02 23:48:13 +03:00
|
|
|
bool BrowsingContext::IsCached() { return mGroup->IsContextCached(this); }
|
2018-07-26 10:31:00 +03:00
|
|
|
|
2019-04-15 18:14:54 +03:00
|
|
|
bool BrowsingContext::HasOpener() const {
|
2019-05-24 23:15:53 +03:00
|
|
|
return sBrowsingContexts->Contains(mOpenerId);
|
2019-04-15 18:14:54 +03:00
|
|
|
}
|
|
|
|
|
2019-04-29 14:38:45 +03:00
|
|
|
void BrowsingContext::GetChildren(Children& aChildren) {
|
2018-12-17 13:45:37 +03:00
|
|
|
MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
|
2018-08-29 05:00:00 +03:00
|
|
|
}
|
|
|
|
|
2019-02-15 12:59:21 +03:00
|
|
|
// FindWithName follows the rules for choosing a browsing context,
|
|
|
|
// with the exception of sandboxing for iframes. The implementation
|
|
|
|
// for arbitrarily choosing between two browsing contexts with the
|
|
|
|
// same name is as follows:
|
|
|
|
//
|
|
|
|
// 1) The start browsing context, i.e. 'this'
|
|
|
|
// 2) Descendants in insertion order
|
|
|
|
// 3) The parent
|
|
|
|
// 4) Siblings and their children, both in insertion order
|
|
|
|
// 5) After this we iteratively follow the parent chain, repeating 3
|
|
|
|
// and 4 until
|
|
|
|
// 6) If there is no parent, consider all other top level browsing
|
|
|
|
// contexts and their children, both in insertion order
|
|
|
|
//
|
|
|
|
// See
|
|
|
|
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
|
|
|
|
BrowsingContext* BrowsingContext::FindWithName(const nsAString& aName) {
|
|
|
|
BrowsingContext* found = nullptr;
|
|
|
|
if (aName.IsEmpty()) {
|
|
|
|
// You can't find a browsing context with an empty name.
|
|
|
|
found = nullptr;
|
|
|
|
} else if (BrowsingContext* special = FindWithSpecialName(aName)) {
|
|
|
|
found = special;
|
|
|
|
} else if (aName.LowerCaseEqualsLiteral("_blank")) {
|
|
|
|
// Just return null. Caller must handle creating a new window with
|
|
|
|
// a blank name.
|
|
|
|
found = nullptr;
|
|
|
|
} else if (BrowsingContext* child = FindWithNameInSubtree(aName, this)) {
|
|
|
|
found = child;
|
|
|
|
} else {
|
|
|
|
BrowsingContext* current = this;
|
|
|
|
|
|
|
|
do {
|
|
|
|
Children* siblings;
|
|
|
|
BrowsingContext* parent = current->mParent;
|
|
|
|
|
|
|
|
if (!parent) {
|
|
|
|
// We've reached the root of the tree, consider browsing
|
|
|
|
// contexts in the same browsing context group.
|
|
|
|
siblings = &mGroup->Toplevels();
|
|
|
|
} else if (parent->NameEquals(aName) && CanAccess(parent) &&
|
|
|
|
parent->IsActive()) {
|
|
|
|
found = parent;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
siblings = &parent->mChildren;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (BrowsingContext* sibling : *siblings) {
|
|
|
|
if (sibling == current) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BrowsingContext* relative =
|
|
|
|
sibling->FindWithNameInSubtree(aName, this)) {
|
|
|
|
found = relative;
|
|
|
|
// Breaks the outer loop
|
|
|
|
parent = nullptr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
current = parent;
|
|
|
|
} while (current);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helpers should perform access control checks, which means that we
|
|
|
|
// only need to assert that we can access found.
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!found || CanAccess(found));
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
BrowsingContext* BrowsingContext::FindChildWithName(const nsAString& aName) {
|
|
|
|
if (aName.IsEmpty()) {
|
|
|
|
// You can't find a browsing context with the empty name.
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (BrowsingContext* child : mChildren) {
|
|
|
|
if (child->NameEquals(aName) && CanAccess(child) && child->IsActive()) {
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
BrowsingContext* BrowsingContext::FindWithSpecialName(const nsAString& aName) {
|
|
|
|
// TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
|
|
|
|
// browsing context pointed to by a special name is active. Should
|
|
|
|
// it be? See Bug 1527913.
|
|
|
|
if (aName.LowerCaseEqualsLiteral("_self")) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aName.LowerCaseEqualsLiteral("_parent")) {
|
|
|
|
return mParent && CanAccess(mParent.get()) ? mParent.get() : this;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aName.LowerCaseEqualsLiteral("_top")) {
|
2019-04-09 09:19:24 +03:00
|
|
|
BrowsingContext* top = Top();
|
2019-02-15 12:59:21 +03:00
|
|
|
|
|
|
|
return CanAccess(top) ? top : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
BrowsingContext* BrowsingContext::FindWithNameInSubtree(
|
|
|
|
const nsAString& aName, BrowsingContext* aRequestingContext) {
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
|
|
|
|
|
|
|
|
if (NameEquals(aName) && aRequestingContext->CanAccess(this) && IsActive()) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (BrowsingContext* child : mChildren) {
|
|
|
|
if (BrowsingContext* found =
|
|
|
|
child->FindWithNameInSubtree(aName, aRequestingContext)) {
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BrowsingContext::CanAccess(BrowsingContext* aContext) {
|
|
|
|
// TODO(farre): Bouncing this to nsDocShell::CanAccessItem is
|
|
|
|
// temporary, we should implement a replacement for this in
|
|
|
|
// BrowsingContext. See Bug 151590.
|
|
|
|
return aContext && nsDocShell::CanAccessItem(aContext->mDocShell, mDocShell);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BrowsingContext::IsActive() const {
|
|
|
|
// TODO(farre): Mimicking the bahaviour from
|
|
|
|
// ItemIsActive(nsIDocShellTreeItem* aItem) is temporary, we should
|
|
|
|
// implement a replacement for this using mClosed only. See Bug
|
|
|
|
// 1527321.
|
|
|
|
|
|
|
|
if (!mDocShell) {
|
|
|
|
return mClosed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
|
|
|
|
auto* win = nsGlobalWindowOuter::Cast(window);
|
|
|
|
if (!win->GetClosedOuter()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-26 10:31:00 +03:00
|
|
|
BrowsingContext::~BrowsingContext() {
|
2018-12-17 13:45:37 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
|
2019-01-30 19:07:21 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
|
2019-04-29 10:01:50 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->IsContextCached(this));
|
2018-07-26 10:31:00 +03:00
|
|
|
|
|
|
|
if (sBrowsingContexts) {
|
2019-05-24 23:15:53 +03:00
|
|
|
sBrowsingContexts->Remove(Id());
|
2018-07-26 10:31:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 05:00:00 +03:00
|
|
|
nsISupports* BrowsingContext::GetParentObject() const {
|
|
|
|
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject* BrowsingContext::WrapObject(JSContext* aCx,
|
|
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
|
|
return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
|
|
|
|
}
|
|
|
|
|
2019-07-02 01:24:09 +03:00
|
|
|
bool BrowsingContext::WriteStructuredClone(JSContext* aCx,
|
|
|
|
JSStructuredCloneWriter* aWriter,
|
|
|
|
StructuredCloneHolder* aHolder) {
|
|
|
|
return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) &&
|
|
|
|
JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
|
|
|
|
JSStructuredCloneReader* aReader,
|
|
|
|
StructuredCloneHolder* aHolder) {
|
|
|
|
uint32_t idLow = 0;
|
|
|
|
uint32_t idHigh = 0;
|
|
|
|
if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
uint64_t id = uint64_t(idHigh) << 32 | idLow;
|
|
|
|
|
|
|
|
// Note: Do this check after reading our ID data. Returning null will abort
|
|
|
|
// the decode operation anyway, but we should at least be as safe as possible.
|
|
|
|
if (NS_WARN_IF(!NS_IsMainThread())) {
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(false,
|
|
|
|
"We shouldn't be trying to decode a BrowsingContext "
|
|
|
|
"on a background thread.");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::RootedValue val(aCx, JS::NullValue());
|
|
|
|
// We'll get rooting hazard errors from the RefPtr destructor if it isn't
|
|
|
|
// destroyed before we try to return a raw JSObject*, so create it in its own
|
|
|
|
// scope.
|
|
|
|
if (RefPtr<BrowsingContext> context = Get(id)) {
|
|
|
|
if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val.toObjectOrNull();
|
|
|
|
}
|
|
|
|
|
2019-01-15 02:09:42 +03:00
|
|
|
void BrowsingContext::NotifyUserGestureActivation() {
|
|
|
|
// We would set the user gesture activation flag on the top level browsing
|
|
|
|
// context, which would automatically be sync to other top level browsing
|
|
|
|
// contexts which are in the different process.
|
2019-04-09 09:19:24 +03:00
|
|
|
RefPtr<BrowsingContext> topLevelBC = Top();
|
2019-01-14 19:08:06 +03:00
|
|
|
USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64,
|
|
|
|
topLevelBC->Id());
|
2019-03-14 21:50:55 +03:00
|
|
|
topLevelBC->SetIsActivatedByUserGesture(true);
|
2019-01-15 02:09:42 +03:00
|
|
|
}
|
|
|
|
|
2019-01-15 02:21:05 +03:00
|
|
|
void BrowsingContext::NotifyResetUserGestureActivation() {
|
|
|
|
// We would reset the user gesture activation flag on the top level browsing
|
|
|
|
// context, which would automatically be sync to other top level browsing
|
|
|
|
// contexts which are in the different process.
|
2019-04-09 09:19:24 +03:00
|
|
|
RefPtr<BrowsingContext> topLevelBC = Top();
|
2019-01-15 02:21:05 +03:00
|
|
|
USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64,
|
|
|
|
topLevelBC->Id());
|
2019-03-14 21:50:55 +03:00
|
|
|
topLevelBC->SetIsActivatedByUserGesture(false);
|
2019-01-15 02:09:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool BrowsingContext::GetUserGestureActivation() {
|
2019-04-09 09:19:24 +03:00
|
|
|
RefPtr<BrowsingContext> topLevelBC = Top();
|
2019-03-14 21:50:55 +03:00
|
|
|
return topLevelBC->GetIsActivatedByUserGesture();
|
2019-01-15 02:21:05 +03:00
|
|
|
}
|
|
|
|
|
2018-09-14 17:57:18 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
|
2019-05-24 23:15:53 +03:00
|
|
|
if (sBrowsingContexts) {
|
|
|
|
sBrowsingContexts->Remove(tmp->Id());
|
|
|
|
}
|
|
|
|
|
2019-04-17 03:51:36 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent, mGroup,
|
|
|
|
mEmbedderElement)
|
2018-09-14 17:57:18 +03:00
|
|
|
if (XRE_IsParentProcess()) {
|
2019-01-29 20:32:28 +03:00
|
|
|
CanonicalBrowsingContext::Cast(tmp)->Unlink();
|
2018-09-14 17:57:18 +03:00
|
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
|
2019-04-17 03:51:36 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell, mChildren, mParent, mGroup,
|
|
|
|
mEmbedderElement)
|
2018-09-14 17:57:18 +03:00
|
|
|
if (XRE_IsParentProcess()) {
|
2019-01-29 20:32:28 +03:00
|
|
|
CanonicalBrowsingContext::Cast(tmp)->Traverse(cb);
|
2018-09-14 17:57:18 +03:00
|
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BrowsingContext)
|
|
|
|
|
2018-07-26 10:31:00 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContext, Release)
|
|
|
|
|
2019-02-28 21:23:15 +03:00
|
|
|
class RemoteLocationProxy
|
|
|
|
: public RemoteObjectProxy<BrowsingContext::LocationProxy,
|
|
|
|
Location_Binding::sCrossOriginAttributes,
|
|
|
|
Location_Binding::sCrossOriginMethods> {
|
|
|
|
public:
|
|
|
|
typedef RemoteObjectProxy Base;
|
|
|
|
|
|
|
|
constexpr RemoteLocationProxy()
|
|
|
|
: RemoteObjectProxy(prototypes::id::Location) {}
|
2019-02-28 22:34:02 +03:00
|
|
|
|
|
|
|
void NoteChildren(JSObject* aProxy,
|
|
|
|
nsCycleCollectionTraversalCallback& aCb) const override {
|
|
|
|
auto location =
|
|
|
|
static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
|
|
|
|
CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
|
|
|
|
"js::GetObjectPrivate(obj)->GetBrowsingContext()");
|
|
|
|
}
|
2019-02-28 21:23:15 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static const RemoteLocationProxy sSingleton;
|
|
|
|
|
|
|
|
// Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
|
|
|
|
// so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
|
|
|
|
// malloc.
|
|
|
|
template <>
|
|
|
|
const js::Class RemoteLocationProxy::Base::sClass =
|
|
|
|
PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
|
|
|
|
|
2019-01-02 16:29:18 +03:00
|
|
|
void BrowsingContext::Location(JSContext* aCx,
|
|
|
|
JS::MutableHandle<JSObject*> aLocation,
|
2019-02-28 21:23:15 +03:00
|
|
|
ErrorResult& aError) {
|
|
|
|
aError.MightThrowJSException();
|
|
|
|
sSingleton.GetProxyObject(aCx, &mLocation, aLocation);
|
|
|
|
if (!aLocation) {
|
|
|
|
aError.StealExceptionFromJSContext(aCx);
|
|
|
|
}
|
|
|
|
}
|
2019-01-02 16:29:18 +03:00
|
|
|
|
|
|
|
void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
|
|
|
|
// FIXME We need to set mClosed, but only once we're sending the
|
|
|
|
// DOMWindowClose event (which happens in the process where the
|
|
|
|
// document for this browsing context is loaded).
|
|
|
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1516343.
|
2019-03-14 21:50:52 +03:00
|
|
|
if (ContentChild* cc = ContentChild::GetSingleton()) {
|
|
|
|
cc->SendWindowClose(this, aCallerType == CallerType::System);
|
|
|
|
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
|
|
|
|
Unused << cp->SendWindowClose(this, aCallerType == CallerType::System);
|
|
|
|
}
|
2019-01-02 16:29:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::Focus(ErrorResult& aError) {
|
2019-03-14 21:50:52 +03:00
|
|
|
if (ContentChild* cc = ContentChild::GetSingleton()) {
|
|
|
|
cc->SendWindowFocus(this);
|
|
|
|
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
|
|
|
|
Unused << cp->SendWindowFocus(this);
|
|
|
|
}
|
2019-01-02 16:29:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::Blur(ErrorResult& aError) {
|
2019-03-14 21:50:52 +03:00
|
|
|
if (ContentChild* cc = ContentChild::GetSingleton()) {
|
|
|
|
cc->SendWindowBlur(this);
|
|
|
|
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
|
|
|
|
Unused << cp->SendWindowBlur(this);
|
|
|
|
}
|
2019-01-02 16:29:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
|
2019-07-02 23:48:13 +03:00
|
|
|
if (mIsDiscarded) {
|
2019-04-17 03:53:09 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-01-02 16:29:18 +03:00
|
|
|
// We never return null or throw an error, but the implementation in
|
|
|
|
// nsGlobalWindow does and we need to use the same signature.
|
2019-04-09 09:19:24 +03:00
|
|
|
return WindowProxyHolder(Top());
|
2019-01-02 16:29:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::GetOpener(JSContext* aCx,
|
|
|
|
JS::MutableHandle<JS::Value> aOpener,
|
|
|
|
ErrorResult& aError) const {
|
2019-03-14 21:51:11 +03:00
|
|
|
RefPtr<BrowsingContext> opener = GetOpener();
|
2019-01-02 16:29:18 +03:00
|
|
|
if (!opener) {
|
|
|
|
aOpener.setNull();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
|
|
|
|
aError.NoteJSContextException(aCx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 03:53:05 +03:00
|
|
|
Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
|
2019-07-02 23:48:13 +03:00
|
|
|
if (mIsDiscarded) {
|
2019-04-17 03:53:05 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-01-02 16:29:18 +03:00
|
|
|
// We never throw an error, but the implementation in nsGlobalWindow does and
|
|
|
|
// we need to use the same signature.
|
|
|
|
if (!mParent) {
|
2019-04-17 03:53:05 +03:00
|
|
|
return WindowProxyHolder(this);
|
2019-01-02 16:29:18 +03:00
|
|
|
}
|
|
|
|
return WindowProxyHolder(mParent.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::PostMessageMoz(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aMessage,
|
|
|
|
const nsAString& aTargetOrigin,
|
|
|
|
const Sequence<JSObject*>& aTransfer,
|
|
|
|
nsIPrincipal& aSubjectPrincipal,
|
|
|
|
ErrorResult& aError) {
|
|
|
|
RefPtr<BrowsingContext> sourceBc;
|
|
|
|
PostMessageData data;
|
|
|
|
data.targetOrigin() = aTargetOrigin;
|
|
|
|
data.subjectPrincipal() = &aSubjectPrincipal;
|
|
|
|
RefPtr<nsGlobalWindowInner> callerInnerWindow;
|
|
|
|
if (!nsGlobalWindowOuter::GatherPostMessageData(
|
|
|
|
aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
|
|
|
|
getter_AddRefs(data.targetOriginURI()),
|
|
|
|
getter_AddRefs(data.callerPrincipal()),
|
|
|
|
getter_AddRefs(callerInnerWindow),
|
|
|
|
getter_AddRefs(data.callerDocumentURI()), aError)) {
|
|
|
|
return;
|
|
|
|
}
|
2019-02-14 00:02:55 +03:00
|
|
|
data.source() = sourceBc;
|
2019-01-02 16:29:18 +03:00
|
|
|
data.isFromPrivateWindow() =
|
|
|
|
callerInnerWindow &&
|
|
|
|
nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
|
|
|
|
|
|
|
|
JS::Rooted<JS::Value> transferArray(aCx);
|
|
|
|
aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
|
|
|
|
&transferArray);
|
|
|
|
if (NS_WARN_IF(aError.Failed())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipc::StructuredCloneData message;
|
|
|
|
message.Write(aCx, aMessage, transferArray, aError);
|
|
|
|
if (NS_WARN_IF(aError.Failed())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ClonedMessageData messageData;
|
2019-03-14 21:50:52 +03:00
|
|
|
if (ContentChild* cc = ContentChild::GetSingleton()) {
|
|
|
|
if (!message.BuildClonedMessageDataForChild(cc, messageData)) {
|
|
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cc->SendWindowPostMessage(this, messageData, data);
|
|
|
|
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
|
|
|
|
if (!message.BuildClonedMessageDataForParent(cp, messageData)) {
|
|
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
2019-01-02 16:29:18 +03:00
|
|
|
|
2019-03-14 21:50:52 +03:00
|
|
|
Unused << cp->SendWindowPostMessage(this, messageData, data);
|
|
|
|
}
|
2019-01-02 16:29:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::PostMessageMoz(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aMessage,
|
|
|
|
const WindowPostMessageOptions& aOptions,
|
|
|
|
nsIPrincipal& aSubjectPrincipal,
|
|
|
|
ErrorResult& aError) {
|
|
|
|
PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
|
|
|
|
aSubjectPrincipal, aError);
|
|
|
|
}
|
|
|
|
|
2019-02-21 23:14:28 +03:00
|
|
|
void BrowsingContext::Transaction::Commit(BrowsingContext* aBrowsingContext) {
|
|
|
|
if (XRE_IsContentProcess()) {
|
2019-03-27 12:19:29 +03:00
|
|
|
// Increment the field epoch for fields affected by this transaction. We
|
|
|
|
// only need to do this in content.
|
|
|
|
#define MOZ_BC_FIELD_RACY(name, ...) \
|
|
|
|
if (m##name) { \
|
|
|
|
aBrowsingContext->mFieldEpochs.m##name++; \
|
|
|
|
}
|
|
|
|
#define MOZ_BC_FIELD(...) /* nothing */
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
|
2019-02-21 23:14:28 +03:00
|
|
|
ContentChild* cc = ContentChild::GetSingleton();
|
2019-03-27 12:19:29 +03:00
|
|
|
cc->SendCommitBrowsingContextTransaction(aBrowsingContext, *this,
|
|
|
|
aBrowsingContext->mFieldEpochs);
|
2019-02-21 23:14:28 +03:00
|
|
|
} else {
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
|
2019-04-30 11:45:41 +03:00
|
|
|
|
|
|
|
aBrowsingContext->Group()->EachParent([&](ContentParent* aParent) {
|
2019-03-27 12:19:29 +03:00
|
|
|
const FieldEpochs& childEpochs =
|
2019-04-30 11:45:41 +03:00
|
|
|
aBrowsingContext->Canonical()->GetFieldEpochsForChild(aParent);
|
|
|
|
Unused << aParent->SendCommitBrowsingContextTransaction(
|
|
|
|
aBrowsingContext, *this, childEpochs);
|
|
|
|
});
|
2019-02-21 23:14:28 +03:00
|
|
|
}
|
|
|
|
|
2019-03-14 21:51:05 +03:00
|
|
|
Apply(aBrowsingContext, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::Transaction::Apply(BrowsingContext* aBrowsingContext,
|
2019-03-27 12:19:29 +03:00
|
|
|
ContentParent* aSource,
|
|
|
|
const FieldEpochs* aEpochs) {
|
|
|
|
// Filter out racy fields which have been updated in this process since this
|
|
|
|
// transaction was committed in the parent. This should only ever occur in the
|
|
|
|
// content process.
|
|
|
|
if (aEpochs) {
|
|
|
|
MOZ_ASSERT(XRE_IsContentProcess());
|
|
|
|
#define MOZ_BC_FIELD_RACY(name, ...) \
|
|
|
|
if (m##name) { \
|
|
|
|
if (aEpochs->m##name < aBrowsingContext->mFieldEpochs.m##name) { \
|
|
|
|
m##name.reset(); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
#define MOZ_BC_FIELD(...) /* nothing */
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
}
|
|
|
|
|
2019-03-14 21:51:05 +03:00
|
|
|
#define MOZ_BC_FIELD(name, ...) \
|
|
|
|
if (m##name) { \
|
2019-03-14 21:51:07 +03:00
|
|
|
aBrowsingContext->WillSet##name(*m##name, aSource); \
|
2019-03-14 21:51:05 +03:00
|
|
|
aBrowsingContext->m##name = std::move(*m##name); \
|
2019-03-14 21:51:07 +03:00
|
|
|
aBrowsingContext->DidSet##name(aSource); \
|
2019-03-14 21:51:05 +03:00
|
|
|
m##name.reset(); \
|
|
|
|
}
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
2019-02-21 23:14:28 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 22:24:10 +03:00
|
|
|
BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
|
2019-06-10 16:06:46 +03:00
|
|
|
// FIXME: We should assert that we're loaded in-content here. (bug 1553804)
|
2019-05-09 22:24:10 +03:00
|
|
|
|
|
|
|
IPCInitializer init;
|
|
|
|
init.mId = Id();
|
|
|
|
init.mParentId = mParent ? mParent->Id() : 0;
|
|
|
|
init.mCached = IsCached();
|
|
|
|
|
|
|
|
#define MOZ_BC_FIELD(name, type) init.m##name = m##name;
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
return init;
|
|
|
|
}
|
|
|
|
|
2019-03-14 21:51:09 +03:00
|
|
|
already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetParent() {
|
|
|
|
RefPtr<BrowsingContext> parent;
|
|
|
|
if (mParentId != 0) {
|
|
|
|
parent = BrowsingContext::Get(mParentId);
|
|
|
|
MOZ_RELEASE_ASSERT(parent);
|
|
|
|
}
|
|
|
|
return parent.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
|
|
|
|
RefPtr<BrowsingContext> opener;
|
|
|
|
if (mOpenerId != 0) {
|
|
|
|
opener = BrowsingContext::Get(mOpenerId);
|
|
|
|
MOZ_RELEASE_ASSERT(opener);
|
|
|
|
}
|
|
|
|
return opener.forget();
|
|
|
|
}
|
|
|
|
|
2019-02-28 21:23:15 +03:00
|
|
|
void BrowsingContext::LocationProxy::SetHref(const nsAString& aHref,
|
|
|
|
nsIPrincipal& aSubjectPrincipal,
|
|
|
|
ErrorResult& aError) {
|
|
|
|
nsPIDOMWindowOuter* win = GetBrowsingContext()->GetDOMWindow();
|
|
|
|
if (!win || !win->GetLocation()) {
|
|
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
win->GetLocation()->SetHref(aHref, aSubjectPrincipal, aError);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::LocationProxy::Replace(const nsAString& aUrl,
|
|
|
|
nsIPrincipal& aSubjectPrincipal,
|
|
|
|
ErrorResult& aError) {
|
|
|
|
nsPIDOMWindowOuter* win = GetBrowsingContext()->GetDOMWindow();
|
|
|
|
if (!win || !win->GetLocation()) {
|
|
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
win->GetLocation()->Replace(aUrl, aSubjectPrincipal, aError);
|
|
|
|
}
|
|
|
|
|
2019-03-20 06:15:36 +03:00
|
|
|
void BrowsingContext::StartDelayedAutoplayMediaComponents() {
|
|
|
|
if (!mDocShell) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64,
|
|
|
|
XRE_IsParentProcess() ? "Parent" : "Child", Id());
|
|
|
|
mDocShell->StartDelayedAutoplayMediaComponents();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BrowsingContext::DidSetIsActivatedByUserGesture(ContentParent* aSource) {
|
|
|
|
MOZ_ASSERT(!mParent, "Set user activation flag on non top-level context!");
|
|
|
|
USER_ACTIVATION_LOG(
|
|
|
|
"Set user gesture activation %d for %s browsing context 0x%08" PRIx64,
|
|
|
|
mIsActivatedByUserGesture, XRE_IsParentProcess() ? "Parent" : "Child",
|
|
|
|
Id());
|
|
|
|
}
|
|
|
|
|
2018-07-26 10:31:00 +03:00
|
|
|
} // namespace dom
|
2019-02-14 00:02:53 +03:00
|
|
|
|
|
|
|
namespace ipc {
|
|
|
|
|
2019-05-21 20:04:39 +03:00
|
|
|
void IPDLParamTraits<dom::BrowsingContext*>::Write(
|
2019-02-14 00:02:53 +03:00
|
|
|
IPC::Message* aMsg, IProtocol* aActor, dom::BrowsingContext* aParam) {
|
|
|
|
uint64_t id = aParam ? aParam->Id() : 0;
|
|
|
|
WriteIPDLParam(aMsg, aActor, id);
|
|
|
|
|
|
|
|
// If his is an in-process send. We want to make sure that our BrowsingContext
|
|
|
|
// object lives long enough to make it to the other side, so we take an extra
|
|
|
|
// reference. This reference is freed in ::Read().
|
|
|
|
if (!aActor->GetIPCChannel()->IsCrossProcess()) {
|
|
|
|
NS_IF_ADDREF(aParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 20:04:39 +03:00
|
|
|
bool IPDLParamTraits<dom::BrowsingContext*>::Read(
|
2019-02-14 00:02:53 +03:00
|
|
|
const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
|
|
|
|
RefPtr<dom::BrowsingContext>* aResult) {
|
|
|
|
uint64_t id = 0;
|
|
|
|
if (!ReadIPDLParam(aMsg, aIter, aActor, &id)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id == 0) {
|
2019-04-15 17:52:11 +03:00
|
|
|
*aResult = nullptr;
|
2019-02-14 00:02:53 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = dom::BrowsingContext::Get(id);
|
2019-04-15 17:52:11 +03:00
|
|
|
MOZ_ASSERT(*aResult, "Deserialized absent BrowsingContext!");
|
2019-02-14 00:02:53 +03:00
|
|
|
|
|
|
|
// If this is an in-process actor, free the reference taken in ::Write().
|
|
|
|
if (!aActor->GetIPCChannel()->IsCrossProcess()) {
|
|
|
|
dom::BrowsingContext* bc = *aResult;
|
|
|
|
NS_IF_RELEASE(bc);
|
|
|
|
}
|
|
|
|
|
2019-04-15 17:52:11 +03:00
|
|
|
return *aResult != nullptr;
|
2019-02-14 00:02:53 +03:00
|
|
|
}
|
|
|
|
|
2019-02-21 23:14:28 +03:00
|
|
|
void IPDLParamTraits<dom::BrowsingContext::Transaction>::Write(
|
|
|
|
IPC::Message* aMessage, IProtocol* aActor,
|
|
|
|
const dom::BrowsingContext::Transaction& aTransaction) {
|
2019-03-14 21:51:05 +03:00
|
|
|
#define MOZ_BC_FIELD(name, ...) \
|
|
|
|
WriteIPDLParam(aMessage, aActor, aTransaction.m##name);
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
2019-02-21 23:14:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IPDLParamTraits<dom::BrowsingContext::Transaction>::Read(
|
|
|
|
const IPC::Message* aMessage, PickleIterator* aIterator, IProtocol* aActor,
|
|
|
|
dom::BrowsingContext::Transaction* aTransaction) {
|
2019-03-14 21:51:05 +03:00
|
|
|
#define MOZ_BC_FIELD(name, ...) \
|
|
|
|
if (!ReadIPDLParam(aMessage, aIterator, aActor, &aTransaction->m##name)) { \
|
|
|
|
return false; \
|
|
|
|
}
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
|
|
|
|
return true;
|
2019-02-21 23:14:28 +03:00
|
|
|
}
|
|
|
|
|
2019-03-27 12:19:29 +03:00
|
|
|
void IPDLParamTraits<dom::BrowsingContext::FieldEpochs>::Write(
|
|
|
|
IPC::Message* aMessage, IProtocol* aActor,
|
|
|
|
const dom::BrowsingContext::FieldEpochs& aEpochs) {
|
|
|
|
#define MOZ_BC_FIELD_RACY(name, ...) \
|
|
|
|
WriteIPDLParam(aMessage, aActor, aEpochs.m##name);
|
|
|
|
#define MOZ_BC_FIELD(...) /* nothing */
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IPDLParamTraits<dom::BrowsingContext::FieldEpochs>::Read(
|
|
|
|
const IPC::Message* aMessage, PickleIterator* aIterator, IProtocol* aActor,
|
|
|
|
dom::BrowsingContext::FieldEpochs* aEpochs) {
|
|
|
|
#define MOZ_BC_FIELD_RACY(name, ...) \
|
|
|
|
if (!ReadIPDLParam(aMessage, aIterator, aActor, &aEpochs->m##name)) { \
|
|
|
|
return false; \
|
|
|
|
}
|
|
|
|
#define MOZ_BC_FIELD(...) /* nothing */
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-14 21:51:09 +03:00
|
|
|
void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
|
|
|
|
IPC::Message* aMessage, IProtocol* aActor,
|
|
|
|
const dom::BrowsingContext::IPCInitializer& aInit) {
|
|
|
|
// Write actor ID parameters.
|
|
|
|
WriteIPDLParam(aMessage, aActor, aInit.mId);
|
|
|
|
WriteIPDLParam(aMessage, aActor, aInit.mParentId);
|
|
|
|
|
2019-04-29 10:01:50 +03:00
|
|
|
WriteIPDLParam(aMessage, aActor, aInit.mCached);
|
|
|
|
|
2019-03-14 21:51:09 +03:00
|
|
|
// Write other synchronized fields.
|
|
|
|
#define MOZ_BC_FIELD(name, ...) WriteIPDLParam(aMessage, aActor, aInit.m##name);
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
|
|
|
|
const IPC::Message* aMessage, PickleIterator* aIterator, IProtocol* aActor,
|
|
|
|
dom::BrowsingContext::IPCInitializer* aInit) {
|
|
|
|
// Read actor ID parameters.
|
|
|
|
if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mId) ||
|
2019-03-14 21:51:11 +03:00
|
|
|
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mParentId)) {
|
2019-03-14 21:51:09 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-29 10:01:50 +03:00
|
|
|
if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mCached)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-14 21:51:09 +03:00
|
|
|
// Read other synchronized fields.
|
|
|
|
#define MOZ_BC_FIELD(name, ...) \
|
|
|
|
if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->m##name)) { \
|
|
|
|
return false; \
|
|
|
|
}
|
|
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-02-14 00:02:53 +03:00
|
|
|
} // namespace ipc
|
2018-07-26 10:31:00 +03:00
|
|
|
} // namespace mozilla
|