Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/dom/BrowsingContext.h"
8
9
#include "ipc/IPCMessageUtils.h"
10
11
#include "mozilla/dom/CanonicalBrowsingContext.h"
12
#include "mozilla/dom/BrowsingContextGroup.h"
13
#include "mozilla/dom/BrowsingContextBinding.h"
14
#include "mozilla/dom/ContentChild.h"
15
#include "mozilla/dom/ContentParent.h"
16
#include "mozilla/dom/Document.h"
17
#include "mozilla/dom/Element.h"
18
#include "mozilla/dom/HTMLIFrameElement.h"
19
#include "mozilla/dom/Location.h"
20
#include "mozilla/dom/LocationBinding.h"
21
#include "mozilla/dom/PopupBlocker.h"
22
#include "mozilla/dom/ScriptSettings.h"
23
#include "mozilla/dom/SessionStorageManager.h"
24
#include "mozilla/dom/StructuredCloneTags.h"
25
#include "mozilla/dom/UserActivationIPCUtils.h"
26
#include "mozilla/dom/WindowBinding.h"
27
#include "mozilla/dom/WindowGlobalChild.h"
28
#include "mozilla/dom/WindowGlobalParent.h"
29
#include "mozilla/dom/WindowProxyHolder.h"
30
#include "mozilla/dom/SyncedContextInlines.h"
31
#include "mozilla/Assertions.h"
32
#include "mozilla/ClearOnShutdown.h"
33
#include "mozilla/Components.h"
34
#include "mozilla/HashTable.h"
35
#include "mozilla/Logging.h"
36
#include "mozilla/Services.h"
37
#include "mozilla/StaticPrefs_page_load.h"
38
#include "mozilla/StaticPtr.h"
39
#include "nsIURIFixup.h"
40
41
#include "nsDocShell.h"
42
#include "nsFocusManager.h"
43
#include "nsGlobalWindowOuter.h"
44
#include "nsIObserverService.h"
45
#include "nsContentUtils.h"
46
#include "nsScriptError.h"
47
#include "nsThreadUtils.h"
48
#include "xpcprivate.h"
49
50
#include "AutoplayPolicy.h"
51
#include "GVAutoplayRequestStatusIPC.h"
52
53
extern mozilla::LazyLogModule gAutoplayPermissionLog;
54
extern mozilla::LazyLogModule gTimeoutDeferralLog;
55
56
#define AUTOPLAY_LOG(msg, ...) \
57
MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
58
59
namespace IPC {
60
// Allow serialization and deserialization of OrientationType over IPC
61
template <>
62
struct ParamTraits<mozilla::dom::OrientationType>
63
: public ContiguousEnumSerializerInclusive<
64
mozilla::dom::OrientationType,
65
mozilla::dom::OrientationType::Portrait_primary,
66
mozilla::dom::OrientationType::Landscape_secondary> {};
67
68
} // namespace IPC
69
70
namespace mozilla {
71
namespace dom {
72
73
// Explicit specialization of the `Transaction` type. Required by the `extern
74
// template class` declaration in the header.
75
template class syncedcontext::Transaction<BrowsingContext>;
76
77
extern mozilla::LazyLogModule gUserInteractionPRLog;
78
79
#define USER_ACTIVATION_LOG(msg, ...) \
80
MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
81
82
static LazyLogModule gBrowsingContextLog("BrowsingContext");
83
84
typedef nsDataHashtable<nsUint64HashKey, BrowsingContext*> BrowsingContextMap;
85
86
static StaticAutoPtr<BrowsingContextMap> sBrowsingContexts;
87
88
static void Register(BrowsingContext* aBrowsingContext) {
89
sBrowsingContexts->Put(aBrowsingContext->Id(), aBrowsingContext);
90
91
aBrowsingContext->Group()->Register(aBrowsingContext);
92
}
93
94
BrowsingContext* BrowsingContext::Top() {
95
BrowsingContext* bc = this;
96
while (bc->mParent) {
97
bc = bc->mParent;
98
}
99
return bc;
100
}
101
102
/* static */
103
void BrowsingContext::Init() {
104
if (!sBrowsingContexts) {
105
sBrowsingContexts = new BrowsingContextMap();
106
ClearOnShutdown(&sBrowsingContexts);
107
}
108
}
109
110
/* static */
111
LogModule* BrowsingContext::GetLog() { return gBrowsingContextLog; }
112
113
/* static */
114
already_AddRefed<BrowsingContext> BrowsingContext::Get(uint64_t aId) {
115
return do_AddRef(sBrowsingContexts->Get(aId));
116
}
117
118
/* static */
119
already_AddRefed<BrowsingContext> BrowsingContext::GetFromWindow(
120
WindowProxyHolder& aProxy) {
121
return do_AddRef(aProxy.get());
122
}
123
124
CanonicalBrowsingContext* BrowsingContext::Canonical() {
125
return CanonicalBrowsingContext::Cast(this);
126
}
127
128
bool BrowsingContext::SameOriginWithTop() {
129
MOZ_ASSERT(IsInProcess());
130
// If the top BrowsingContext is not same-process to us, it is cross-origin
131
if (!Top()->IsInProcess()) {
132
return false;
133
}
134
135
nsIDocShell* docShell = GetDocShell();
136
if (!docShell) {
137
return false;
138
}
139
Document* doc = docShell->GetDocument();
140
if (!doc) {
141
return false;
142
}
143
nsIPrincipal* principal = doc->NodePrincipal();
144
145
nsIDocShell* topDocShell = Top()->GetDocShell();
146
if (!topDocShell) {
147
return false;
148
}
149
Document* topDoc = topDocShell->GetDocument();
150
if (!topDoc) {
151
return false;
152
}
153
nsIPrincipal* topPrincipal = topDoc->NodePrincipal();
154
155
return principal->Equals(topPrincipal);
156
}
157
158
/* static */
159
already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
160
BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
161
Type aType) {
162
MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->mType == aType);
163
164
MOZ_DIAGNOSTIC_ASSERT(aType != Type::Chrome || XRE_IsParentProcess());
165
166
uint64_t id = nsContentUtils::GenerateBrowsingContextId();
167
168
MOZ_LOG(GetLog(), LogLevel::Debug,
169
("Creating 0x%08" PRIx64 " in %s", id,
170
XRE_IsParentProcess() ? "Parent" : "Child"));
171
172
// Determine which BrowsingContextGroup this context should be created in.
173
RefPtr<BrowsingContextGroup> group =
174
(aType == Type::Chrome)
175
? do_AddRef(BrowsingContextGroup::GetChromeGroup())
176
: BrowsingContextGroup::Select(aParent, aOpener);
177
178
RefPtr<BrowsingContext> context;
179
if (XRE_IsParentProcess()) {
180
context =
181
new CanonicalBrowsingContext(aParent, group, id,
182
/* aOwnerProcessId */ 0,
183
/* aEmbedderProcessId */ 0, aType, {});
184
} else {
185
context = new BrowsingContext(aParent, group, id, aType, {});
186
}
187
188
// The name and opener fields need to be explicitly initialized. Don't bother
189
// using transactions to set them, as we haven't been attached yet.
190
context->mFields.SetWithoutSyncing<IDX_Name>(aName);
191
if (aOpener) {
192
MOZ_DIAGNOSTIC_ASSERT(aOpener->Group() == context->Group());
193
MOZ_DIAGNOSTIC_ASSERT(aOpener->mType == context->mType);
194
context->mFields.SetWithoutSyncing<IDX_OpenerId>(aOpener->Id());
195
context->mFields.SetWithoutSyncing<IDX_HadOriginalOpener>(true);
196
}
197
context->mFields.SetWithoutSyncing<IDX_EmbedderPolicy>(
198
nsILoadInfo::EMBEDDER_POLICY_NULL);
199
context->mFields.SetWithoutSyncing<IDX_OpenerPolicy>(
200
nsILoadInfo::OPENER_POLICY_UNSAFE_NONE);
201
202
if (aOpener && aOpener->SameOriginWithTop()) {
203
// We inherit the opener policy if there is a creator and if the creator's
204
// origin is same origin with the creator's top-level origin.
205
// If it is cross origin we should not inherit the CrossOriginOpenerPolicy
206
context->mFields.SetWithoutSyncing<IDX_OpenerPolicy>(
207
aOpener->Top()->GetOpenerPolicy());
208
} else if (aOpener) {
209
// They are not same origin
210
auto topPolicy = aOpener->Top()->GetOpenerPolicy();
211
MOZ_RELEASE_ASSERT(topPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE ||
212
topPolicy ==
213
nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS);
214
}
215
216
BrowsingContext* inherit = aParent ? aParent : aOpener;
217
if (inherit) {
219
context->mFields.SetWithoutSyncing<IDX_EmbedderPolicy>(
220
inherit->GetEmbedderPolicy());
221
// if our parent has a parent that's loading, we need it too
222
bool ancestorLoading = aParent ? aParent->GetAncestorLoading() : false;
223
if (!ancestorLoading && aParent) {
224
// XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug
225
// 1608448) Check if the parent was itself loading already
226
nsPIDOMWindowOuter* outer = aParent->GetDOMWindow();
227
if (outer) {
228
Document* document = nsGlobalWindowOuter::Cast(outer)->GetDocument();
229
auto readystate = document->GetReadyStateEnum();
230
if (readystate == Document::ReadyState::READYSTATE_LOADING ||
231
readystate == Document::ReadyState::READYSTATE_INTERACTIVE) {
232
ancestorLoading = true;
233
}
234
}
235
}
236
context->mFields.SetWithoutSyncing<IDX_AncestorLoading>(ancestorLoading);
237
}
238
239
nsContentUtils::GenerateUUIDInPlace(
240
context->mFields.GetNonSyncingReference<IDX_HistoryID>());
241
242
context->mFields.SetWithoutSyncing<IDX_IsActive>(true);
243
244
const bool allowContentRetargeting =
245
inherit ? inherit->GetAllowContentRetargetingOnChildren() : true;
246
context->mFields.SetWithoutSyncing<IDX_AllowContentRetargeting>(
247
allowContentRetargeting);
248
context->mFields.SetWithoutSyncing<IDX_AllowContentRetargetingOnChildren>(
249
allowContentRetargeting);
250
251
const bool allowPlugins = inherit ? inherit->GetAllowPlugins() : true;
252
context->mFields.SetWithoutSyncing<IDX_AllowPlugins>(allowPlugins);
253
254
return context.forget();
255
}
256
257
already_AddRefed<BrowsingContext> BrowsingContext::Create(
258
BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
259
Type aType) {
260
RefPtr<BrowsingContext> bc(CreateDetached(aParent, aOpener, aName, aType));
261
bc->EnsureAttached();
262
return bc.forget();
263
}
264
265
already_AddRefed<BrowsingContext> BrowsingContext::CreateWindowless(
266
BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
267
Type aType) {
268
RefPtr<BrowsingContext> bc(CreateDetached(aParent, aOpener, aName, aType));
269
bc->mWindowless = true;
270
bc->EnsureAttached();
271
return bc.forget();
272
}
273
274
void BrowsingContext::SetWindowless() {
275
MOZ_DIAGNOSTIC_ASSERT(!mEverAttached);
276
mWindowless = true;
277
}
278
279
void BrowsingContext::EnsureAttached() {
280
if (!mEverAttached) {
281
Register(this);
282
283
// Attach the browsing context to the tree.
284
Attach();
285
}
286
}
287
288
/* static */
289
already_AddRefed<BrowsingContext> BrowsingContext::CreateFromIPC(
290
BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup,
291
ContentParent* aOriginProcess) {
292
MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
293
MOZ_DIAGNOSTIC_ASSERT(aGroup);
294
295
uint64_t originId = 0;
296
if (aOriginProcess) {
297
originId = aOriginProcess->ChildID();
298
aGroup->EnsureSubscribed(aOriginProcess);
299
}
300
301
MOZ_LOG(GetLog(), LogLevel::Debug,
302
("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
303
aInit.mId, originId));
304
305
RefPtr<BrowsingContext> parent = aInit.GetParent();
306
307
RefPtr<BrowsingContext> context;
308
if (XRE_IsParentProcess()) {
309
// If the new BrowsingContext has a parent, it is a sub-frame embedded in
310
// whatever process sent the message. If it doesn't, and is not windowless,
311
// it is a new window or tab, and will be embedded in the parent process.
312
uint64_t embedderProcessId = (aInit.mWindowless || parent) ? originId : 0;
313
context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
314
embedderProcessId, Type::Content,
315
std::move(aInit.mFields));
316
} else {
317
context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content,
318
std::move(aInit.mFields));
319
}
320
321
context->mWindowless = aInit.mWindowless;
322
323
Register(context);
324
325
// Caller handles attaching us to the tree.
326
327
if (aInit.mCached) {
328
context->mEverAttached = true;
329
}
330
331
return context.forget();
332
}
333
334
BrowsingContext::BrowsingContext(BrowsingContext* aParent,
335
BrowsingContextGroup* aGroup,
336
uint64_t aBrowsingContextId, Type aType,
337
FieldTuple&& aFields)
338
: mFields(std::move(aFields)),
339
mType(aType),
340
mBrowsingContextId(aBrowsingContextId),
341
mGroup(aGroup),
342
mParent(aParent),
343
mEverAttached(false),
344
mIsInProcess(false),
345
mIsDiscarded(false),
346
mWindowless(false),
347
mDanglingRemoteOuterProxies(false),
348
mPendingInitialization(false),
349
mEmbeddedByThisProcess(false) {
350
MOZ_RELEASE_ASSERT(!mParent || mParent->Group() == mGroup);
351
MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
352
MOZ_RELEASE_ASSERT(mGroup);
353
}
354
355
void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
356
// XXX(nika): We should communicate that we are now an active BrowsingContext
357
// process to the parent & do other validation here.
358
MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
359
MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this);
360
mDocShell = aDocShell;
361
mDanglingRemoteOuterProxies = !mIsInProcess;
362
mIsInProcess = true;
363
}
364
365
// This class implements a callback that will return the remote window proxy for
366
// mBrowsingContext in that compartment, if it has one. It also removes the
367
// proxy from the map, because the object will be transplanted into another kind
368
// of object.
369
class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback
370
: public js::CompartmentTransplantCallback {
371
public:
372
explicit CompartmentRemoteProxyTransplantCallback(
373
BrowsingContext* aBrowsingContext)
374
: mBrowsingContext(aBrowsingContext) {}
375
376
virtual JSObject* getObjectToTransplant(
377
JS::Compartment* compartment) override {
378
auto* priv = xpc::CompartmentPrivate::Get(compartment);
379
if (!priv) {
380
return nullptr;
381
}
382
383
auto& map = priv->GetRemoteProxyMap();
384
auto result = map.lookup(mBrowsingContext);
385
if (!result) {
386
return nullptr;
387
}
388
JSObject* resultObject = result->value();
389
map.remove(result);
390
391
return resultObject;
392
}
393
394
private:
395
BrowsingContext* mBrowsingContext;
396
};
397
398
void BrowsingContext::CleanUpDanglingRemoteOuterWindowProxies(
399
JSContext* aCx, JS::MutableHandle<JSObject*> aOuter) {
400
if (!mDanglingRemoteOuterProxies) {
401
return;
402
}
403
mDanglingRemoteOuterProxies = false;
404
405
CompartmentRemoteProxyTransplantCallback cb(this);
406
js::RemapRemoteWindowProxies(aCx, &cb, aOuter);
407
}
408
409
void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
410
mEmbeddedByThisProcess = true;
411
412
// Update embedder-element-specific fields in a shared transaction.
413
// this when clearing our embedder, as we're being destroyed either way.
414
if (aEmbedder) {
415
Transaction txn;
416
txn.SetEmbedderElementType(Some(aEmbedder->LocalName()));
417
if (nsCOMPtr<nsPIDOMWindowInner> inner =
418
do_QueryInterface(aEmbedder->GetOwnerGlobal())) {
419
txn.SetEmbedderInnerWindowId(inner->WindowID());
420
}
421
if (XRE_IsParentProcess() && IsTopContent()) {
422
nsAutoString messageManagerGroup;
423
if (aEmbedder->IsXULElement()) {
424
aEmbedder->GetAttr(kNameSpaceID_None, nsGkAtoms::messagemanagergroup,
425
messageManagerGroup);
426
}
427
txn.SetMessageManagerGroup(messageManagerGroup);
428
}
429
txn.Commit(this);
430
}
431
432
mEmbedderElement = aEmbedder;
433
}
434
435
void BrowsingContext::Embed() {
436
if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) {
437
frame->BindToBrowsingContext(this);
438
}
439
}
440
441
void BrowsingContext::Attach(bool aFromIPC) {
442
MOZ_DIAGNOSTIC_ASSERT(!mEverAttached);
443
mEverAttached = true;
444
445
MOZ_LOG(GetLog(), LogLevel::Debug,
446
("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64,
447
XRE_IsParentProcess() ? "Parent" : "Child", Id(),
448
mParent ? mParent->Id() : 0));
449
450
MOZ_DIAGNOSTIC_ASSERT(mGroup);
451
MOZ_DIAGNOSTIC_ASSERT(!mGroup->IsContextCached(this));
452
MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
453
454
Children* children = nullptr;
455
if (mParent) {
456
children = &mParent->mChildren;
457
BrowsingContext_Binding::ClearCachedChildrenValue(mParent);
458
} else {
459
children = &mGroup->Toplevels();
460
}
461
MOZ_DIAGNOSTIC_ASSERT(!children->Contains(this));
462
463
children->AppendElement(this);
464
465
if (GetIsPopupSpam()) {
466
PopupBlocker::RegisterOpenPopupSpam();
467
}
468
469
if (!aFromIPC) {
470
// Send attach to our parent if we need to.
471
if (XRE_IsContentProcess()) {
472
ContentChild::GetSingleton()->SendAttachBrowsingContext(
473
GetIPCInitializer());
474
} else if (IsContent()) {
475
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
476
mGroup->EachParent([&](ContentParent* aParent) {
477
Unused << aParent->SendAttachBrowsingContext(GetIPCInitializer());
478
});
479
}
480
}
481
}
482
483
void BrowsingContext::Detach(bool aFromIPC) {
484
MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
485
486
MOZ_LOG(GetLog(), LogLevel::Debug,
487
("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
488
XRE_IsParentProcess() ? "Parent" : "Child", Id(),
489
mParent ? mParent->Id() : 0));
490
491
// Unlinking might remove our group before Detach gets called.
492
if (NS_WARN_IF(!mGroup)) {
493
return;
494
}
495
496
if (!mGroup->EvictCachedContext(this)) {
497
Children* children = nullptr;
498
if (mParent) {
499
children = &mParent->mChildren;
500
BrowsingContext_Binding::ClearCachedChildrenValue(mParent);
501
} else {
502
children = &mGroup->Toplevels();
503
}
504
505
children->RemoveElement(this);
506
}
507
508
if (!mChildren.IsEmpty()) {
509
mGroup->CacheContexts(mChildren);
510
mChildren.Clear();
511
BrowsingContext_Binding::ClearCachedChildrenValue(this);
512
}
513
514
{
515
// Hold a strong reference to ourself until the responses comes back to
516
// ensure we don't die while messages relating to this context are
517
// in-flight.
518
RefPtr<BrowsingContext> self(this);
519
auto callback = [self](auto) {};
520
if (XRE_IsParentProcess()) {
521
Group()->EachParent([&](ContentParent* aParent) {
522
// Only the embedder process is allowed to initiate a BrowsingContext
523
// detach, so if we've gotten here, the host process already knows we've
524
// been detached, and there's no need to tell it again.
525
// If the owner process is not the same as the embedder process, its
526
// BrowsingContext will be detached when its nsWebBrowser instance is
527
// destroyed.
528
if (!Canonical()->IsEmbeddedInProcess(aParent->ChildID()) &&
529
!Canonical()->IsOwnedByProcess(aParent->ChildID())) {
530
aParent->SendDetachBrowsingContext(Id(), callback, callback);
531
}
532
});
533
} else if (!aFromIPC) {
534
ContentChild::GetSingleton()->SendDetachBrowsingContext(Id(), callback,
535
callback);
536
}
537
}
538
539
mGroup->Unregister(this);
540
mIsDiscarded = true;
541
542
if (XRE_IsParentProcess()) {
543
nsFocusManager* fm = nsFocusManager::GetFocusManager();
544
if (fm) {
545
fm->BrowsingContextDetached(this);
546
}
547
}
548
549
if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
550
obs->NotifyObservers(ToSupports(this), "browsing-context-discarded",
551
nullptr);
552
}
553
554
// NOTE: Doesn't use SetClosed, as it will be set in all processes
555
// automatically by calls to Detach()
556
mFields.SetWithoutSyncing<IDX_Closed>(true);
557
558
if (GetIsPopupSpam()) {
559
PopupBlocker::UnregisterOpenPopupSpam();
560
// NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
561
// automatically.
562
mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
563
}
564
565
if (XRE_IsParentProcess()) {
566
Canonical()->CanonicalDiscard();
567
}
568
}
569
570
void BrowsingContext::PrepareForProcessChange() {
571
MOZ_LOG(GetLog(), LogLevel::Debug,
572
("%s: Preparing 0x%08" PRIx64 " for a process change",
573
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
574
575
MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
576
MOZ_ASSERT(!mIsDiscarded, "We're already closed?");
577
578
mIsInProcess = false;
579
mUserGestureStart = TimeStamp();
580
581
// NOTE: For now, clear our nsDocShell reference, as we're primarily in a
582
// different process now. This may need to change in the future with
583
// Cross-Process BFCache.
584
mDocShell = nullptr;
585
586
if (!mWindowProxy) {
587
return;
588
}
589
590
// We have to go through mWindowProxy rather than calling GetDOMWindow() on
591
// mDocShell because the mDocshell reference gets cleared immediately after
592
// the window is closed.
593
nsGlobalWindowOuter::PrepareForProcessChange(mWindowProxy);
594
MOZ_ASSERT(!mWindowProxy);
595
}
596
597
void BrowsingContext::CacheChildren(bool aFromIPC) {
598
MOZ_LOG(GetLog(), LogLevel::Debug,
599
("%s: Caching children of 0x%08" PRIx64 "",
600
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
601
602
mGroup->CacheContexts(mChildren);
603
mChildren.Clear();
604
BrowsingContext_Binding::ClearCachedChildrenValue(this);
605
606
if (!aFromIPC && XRE_IsContentProcess()) {
607
auto cc = ContentChild::GetSingleton();
608
MOZ_DIAGNOSTIC_ASSERT(cc);
609
cc->SendCacheBrowsingContextChildren(this);
610
}
611
}
612
613
void BrowsingContext::RestoreChildren(Children&& aChildren, bool aFromIPC) {
614
MOZ_LOG(GetLog(), LogLevel::Debug,
615
("%s: Restoring children of 0x%08" PRIx64 "",
616
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
617
618
nsTArray<MaybeDiscarded<BrowsingContext>> ipcChildren(aChildren.Length());
619
for (BrowsingContext* child : aChildren) {
620
MOZ_DIAGNOSTIC_ASSERT(child->GetParent() == this);
621
Unused << mGroup->EvictCachedContext(child);
622
ipcChildren.AppendElement(child);
623
}
624
625
mChildren.AppendElements(aChildren);
626
BrowsingContext_Binding::ClearCachedChildrenValue(this);
627
628
if (!aFromIPC && XRE_IsContentProcess()) {
629
auto cc = ContentChild::GetSingleton();
630
MOZ_DIAGNOSTIC_ASSERT(cc);
631
cc->SendRestoreBrowsingContextChildren(this, ipcChildren);
632
}
633
}
634
635
bool BrowsingContext::IsCached() { return mGroup->IsContextCached(this); }
636
637
bool BrowsingContext::IsTargetable() {
638
return !GetClosed() && !mIsDiscarded && !IsCached();
639
}
640
641
bool BrowsingContext::HasOpener() const {
642
return sBrowsingContexts->Contains(GetOpenerId());
643
}
644
645
void BrowsingContext::GetChildren(Children& aChildren) {
646
MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
647
}
648
649
void BrowsingContext::GetWindowContexts(
650
nsTArray<RefPtr<WindowContext>>& aWindows) {
651
aWindows.AppendElements(mWindowContexts);
652
}
653
654
void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) {
655
MOZ_ASSERT(!mWindowContexts.Contains(aWindow),
656
"WindowContext already registered!");
657
mWindowContexts.AppendElement(aWindow);
658
if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) {
659
MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
660
mCurrentWindowContext = aWindow;
661
}
662
}
663
664
void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
665
MOZ_ASSERT(mWindowContexts.Contains(aWindow),
666
"WindowContext not registered!");
667
mWindowContexts.RemoveElement(aWindow);
668
669
// Our current window global should be in our mWindowGlobals set. If it's not
670
// anymore, clear that reference.
671
// FIXME: There are probably situations where this is wrong. We should
672
// double-check.
673
if (aWindow == mCurrentWindowContext) {
674
mCurrentWindowContext = nullptr;
675
if (XRE_IsParentProcess()) {
676
BrowserParent::UpdateFocusFromBrowsingContext();
677
}
678
}
679
}
680
681
// FindWithName follows the rules for choosing a browsing context,
682
// with the exception of sandboxing for iframes. The implementation
683
// for arbitrarily choosing between two browsing contexts with the
684
// same name is as follows:
685
//
686
// 1) The start browsing context, i.e. 'this'
687
// 2) Descendants in insertion order
688
// 3) The parent
689
// 4) Siblings and their children, both in insertion order
690
// 5) After this we iteratively follow the parent chain, repeating 3
691
// and 4 until
692
// 6) If there is no parent, consider all other top level browsing
693
// contexts and their children, both in insertion order
694
//
695
// See
697
BrowsingContext* BrowsingContext::FindWithName(
698
const nsAString& aName, bool aUseEntryGlobalForAccessCheck) {
699
RefPtr<BrowsingContext> requestingContext = this;
700
if (aUseEntryGlobalForAccessCheck) {
701
if (nsCOMPtr<nsIDocShell> caller = do_GetInterface(GetEntryGlobal())) {
702
if (caller->GetBrowsingContext()) {
703
requestingContext = caller->GetBrowsingContext();
704
}
705
}
706
}
707
708
BrowsingContext* found = nullptr;
709
if (aName.IsEmpty()) {
710
// You can't find a browsing context with an empty name.
711
found = nullptr;
712
} else if (aName.LowerCaseEqualsLiteral("_blank")) {
713
// Just return null. Caller must handle creating a new window with
714
// a blank name.
715
found = nullptr;
716
} else if (nsContentUtils::IsSpecialName(aName)) {
717
found = FindWithSpecialName(aName, *requestingContext);
718
} else if (BrowsingContext* child =
719
FindWithNameInSubtree(aName, *requestingContext)) {
720
found = child;
721
} else {
722
BrowsingContext* current = this;
723
724
do {
725
Children* siblings;
726
BrowsingContext* parent = current->mParent;
727
728
if (!parent) {
729
// We've reached the root of the tree, consider browsing
730
// contexts in the same browsing context group.
731
siblings = &mGroup->Toplevels();
732
} else if (parent->NameEquals(aName) &&
733
requestingContext->CanAccess(parent) &&
734
parent->IsTargetable()) {
735
found = parent;
736
break;
737
} else {
738
siblings = &parent->mChildren;
739
}
740
741
for (BrowsingContext* sibling : *siblings) {
742
if (sibling == current) {
743
continue;
744
}
745
746
if (BrowsingContext* relative =
747
sibling->FindWithNameInSubtree(aName, *requestingContext)) {
748
found = relative;
749
// Breaks the outer loop
750
parent = nullptr;
751
break;
752
}
753
}
754
755
current = parent;
756
} while (current);
757
}
758
759
// Helpers should perform access control checks, which means that we
760
// only need to assert that we can access found.
761
MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanAccess(found));
762
763
return found;
764
}
765
766
BrowsingContext* BrowsingContext::FindChildWithName(
767
const nsAString& aName, BrowsingContext& aRequestingContext) {
768
if (aName.IsEmpty()) {
769
// You can't find a browsing context with the empty name.
770
return nullptr;
771
}
772
773
for (BrowsingContext* child : mChildren) {
774
if (child->NameEquals(aName) && aRequestingContext.CanAccess(child) &&
775
child->IsTargetable()) {
776
return child;
777
}
778
}
779
780
return nullptr;
781
}
782
783
BrowsingContext* BrowsingContext::FindWithSpecialName(
784
const nsAString& aName, BrowsingContext& aRequestingContext) {
785
// TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
786
// browsing context pointed to by a special name is active. Should
787
// it be? See Bug 1527913.
788
if (aName.LowerCaseEqualsLiteral("_self")) {
789
return this;
790
}
791
792
if (aName.LowerCaseEqualsLiteral("_parent")) {
793
if (mParent) {
794
return aRequestingContext.CanAccess(mParent) ? mParent.get() : nullptr;
795
}
796
return this;
797
}
798
799
if (aName.LowerCaseEqualsLiteral("_top")) {
800
BrowsingContext* top = Top();
801
802
return aRequestingContext.CanAccess(top) ? top : nullptr;
803
}
804
805
return nullptr;
806
}
807
808
BrowsingContext* BrowsingContext::FindWithNameInSubtree(
809
const nsAString& aName, BrowsingContext& aRequestingContext) {
810
MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
811
812
if (NameEquals(aName) && aRequestingContext.CanAccess(this) &&
813
IsTargetable()) {
814
return this;
815
}
816
817
for (BrowsingContext* child : mChildren) {
818
if (BrowsingContext* found =
819
child->FindWithNameInSubtree(aName, aRequestingContext)) {
820
return found;
821
}
822
}
823
824
return nullptr;
825
}
826
827
// For historical context, see:
828
//
829
// Bug 13871: Prevent frameset spoofing
830
// Bug 103638: Targets with same name in different windows open in wrong
831
// window with javascript
832
// Bug 408052: Adopt "ancestor" frame navigation policy
833
// Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
834
// origin attribute isolation.
835
bool BrowsingContext::CanAccess(BrowsingContext* aTarget,
836
bool aConsiderOpener) {
837
MOZ_ASSERT(
838
mDocShell,
839
"CanAccess() may only be called in the process of the accessing window");
840
MOZ_ASSERT(aTarget, "Must have a target");
841
842
MOZ_DIAGNOSTIC_ASSERT(
843
Group() == aTarget->Group(),
844
"A BrowsingContext should never see a context from a different group");
845
846
// A frame can navigate itself and its own root.
847
if (aTarget == this || aTarget == Top()) {
848
return true;
849
}
850
851
// A frame can navigate any frame with a same-origin ancestor.
852
for (BrowsingContext* bc = aTarget; bc; bc = bc->GetParent()) {
853
if (bc->mDocShell && nsDocShell::ValidateOrigin(this, bc)) {
854
return true;
855
}
856
}
857
858
// If the target is a top-level document, a frame can navigate it if it can
859
// navigate its opener.
860
if (aConsiderOpener && !aTarget->GetParent()) {
861
if (RefPtr<BrowsingContext> opener = aTarget->GetOpener()) {
862
return CanAccess(opener, false);
863
}
864
}
865
866
return false;
867
}
868
869
RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() {
870
RefPtr<SessionStorageManager>& manager = Top()->mSessionStorageManager;
871
if (!manager) {
872
manager = new SessionStorageManager(this);
873
}
874
return manager;
875
}
876
877
BrowsingContext::~BrowsingContext() {
878
MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
879
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
880
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->IsContextCached(this));
881
882
mDeprioritizedLoadRunner.clear();
883
884
if (sBrowsingContexts) {
885
sBrowsingContexts->Remove(Id());
886
}
887
}
888
889
nsISupports* BrowsingContext::GetParentObject() const {
890
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
891
}
892
893
JSObject* BrowsingContext::WrapObject(JSContext* aCx,
894
JS::Handle<JSObject*> aGivenProto) {
895
return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
896
}
897
898
bool BrowsingContext::WriteStructuredClone(JSContext* aCx,
899
JSStructuredCloneWriter* aWriter,
900
StructuredCloneHolder* aHolder) {
901
MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
902
return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) &&
903
JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32)));
904
}
905
906
/* static */
907
JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
908
JSStructuredCloneReader* aReader,
909
StructuredCloneHolder* aHolder) {
910
uint32_t idLow = 0;
911
uint32_t idHigh = 0;
912
if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) {
913
return nullptr;
914
}
915
uint64_t id = uint64_t(idHigh) << 32 | idLow;
916
917
// Note: Do this check after reading our ID data. Returning null will abort
918
// the decode operation anyway, but we should at least be as safe as possible.
919
if (NS_WARN_IF(!NS_IsMainThread())) {
920
MOZ_DIAGNOSTIC_ASSERT(false,
921
"We shouldn't be trying to decode a BrowsingContext "
922
"on a background thread.");
923
return nullptr;
924
}
925
926
JS::RootedValue val(aCx, JS::NullValue());
927
// We'll get rooting hazard errors from the RefPtr destructor if it isn't
928
// destroyed before we try to return a raw JSObject*, so create it in its own
929
// scope.
930
if (RefPtr<BrowsingContext> context = Get(id)) {
931
if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
932
return nullptr;
933
}
934
}
935
return val.toObjectOrNull();
936
}
937
938
void BrowsingContext::NotifyUserGestureActivation() {
939
SetUserActivationState(UserActivation::State::FullActivated);
940
}
941
942
void BrowsingContext::NotifyResetUserGestureActivation() {
943
SetUserActivationState(UserActivation::State::None);
944
}
945
946
bool BrowsingContext::HasBeenUserGestureActivated() {
947
return GetUserActivationState() != UserActivation::State::None;
948
}
949
950
bool BrowsingContext::HasValidTransientUserGestureActivation() {
951
MOZ_ASSERT(mIsInProcess);
952
953
if (GetUserActivationState() != UserActivation::State::FullActivated) {
954
MOZ_ASSERT(mUserGestureStart.IsNull(),
955
"mUserGestureStart should be null if the document hasn't ever "
956
"been activated by user gesture");
957
return false;
958
}
959
960
MOZ_ASSERT(!mUserGestureStart.IsNull(),
961
"mUserGestureStart shouldn't be null if the document has ever "
962
"been activated by user gesture");
963
TimeDuration timeout = TimeDuration::FromMilliseconds(
964
StaticPrefs::dom_user_activation_transient_timeout());
965
966
return timeout <= TimeDuration() ||
967
(TimeStamp::Now() - mUserGestureStart) <= timeout;
968
}
969
970
bool BrowsingContext::ConsumeTransientUserGestureActivation() {
971
MOZ_ASSERT(mIsInProcess);
972
973
if (!HasValidTransientUserGestureActivation()) {
974
return false;
975
}
976
977
BrowsingContext* top = Top();
978
top->PreOrderWalk([&](BrowsingContext* aContext) {
979
if (aContext->GetUserActivationState() ==
980
UserActivation::State::FullActivated) {
981
aContext->SetUserActivationState(UserActivation::State::HasBeenActivated);
982
}
983
});
984
985
return true;
986
}
987
988
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowsingContext)
989
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
990
NS_INTERFACE_MAP_ENTRY(nsISupports)
991
NS_INTERFACE_MAP_END
992
993
NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
994
995
NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowsingContext)
996
NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowsingContext)
997
998
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowsingContext)
999
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1000
NS_IMPL_CYCLE_COLLECTION_TRACE_END
1001
1002
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
1003
if (sBrowsingContexts) {
1004
sBrowsingContexts->Remove(tmp->Id());
1005
}
1006
1007
if (tmp->GetIsPopupSpam()) {
1008
PopupBlocker::UnregisterOpenPopupSpam();
1009
// NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1010
// automatically.
1011
tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1012
}
1013
1014
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent, mGroup,
1015
mEmbedderElement, mWindowContexts,
1016
mCurrentWindowContext, mSessionStorageManager)
1017
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1018
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1019
1020
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
1021
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
1022
mDocShell, mChildren, mParent, mGroup, mEmbedderElement, mWindowContexts,
1023
mCurrentWindowContext, mSessionStorageManager)
1024
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1025
1026
class RemoteLocationProxy
1027
: public RemoteObjectProxy<BrowsingContext::LocationProxy,
1028
Location_Binding::sCrossOriginAttributes,
1029
Location_Binding::sCrossOriginMethods> {
1030
public:
1031
typedef RemoteObjectProxy Base;
1032
1033
constexpr RemoteLocationProxy()
1034
: RemoteObjectProxy(prototypes::id::Location) {}
1035
1036
void NoteChildren(JSObject* aProxy,
1037
nsCycleCollectionTraversalCallback& aCb) const override {
1038
auto location =
1039
static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
1040
CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
1041
"js::GetObjectPrivate(obj)->GetBrowsingContext()");
1042
}
1043
};
1044
1045
static const RemoteLocationProxy sSingleton;
1046
1047
// Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
1048
// so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
1049
// malloc.
1050
template <>
1051
const JSClass RemoteLocationProxy::Base::sClass =
1052
PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
1053
1054
void BrowsingContext::Location(JSContext* aCx,
1055
JS::MutableHandle<JSObject*> aLocation,
1056
ErrorResult& aError) {
1057
aError.MightThrowJSException();
1058
sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr,
1059
aLocation);
1060
if (!aLocation) {
1061
aError.StealExceptionFromJSContext(aCx);
1062
}
1063
}
1064
1065
nsresult BrowsingContext::LoadURI(BrowsingContext* aAccessor,
1066
nsDocShellLoadState* aLoadState,
1067
bool aSetNavigating) {
1068
// Per spec, most load attempts are silently ignored when a BrowsingContext is
1069
// null (which in our code corresponds to discarded), so we simply fail
1070
// silently in those cases. Regardless, we cannot trigger loads in/from
1071
// discarded BrowsingContexts via IPC, so we need to abort in any case.
1072
if (IsDiscarded() || (aAccessor && aAccessor->IsDiscarded())) {
1073
return NS_OK;
1074
}
1075
1076
if (mDocShell) {
1077
return mDocShell->LoadURI(aLoadState, aSetNavigating);
1078
}
1079
1080
if (!aAccessor && XRE_IsParentProcess()) {
1081
if (ContentParent* cp = Canonical()->GetContentParent()) {
1082
Unused << cp->SendLoadURI(this, aLoadState, aSetNavigating);
1083
}
1084
} else {
1085
MOZ_DIAGNOSTIC_ASSERT(aAccessor);
1086
MOZ_DIAGNOSTIC_ASSERT(aAccessor->Group() == Group());
1087
if (!aAccessor) {
1088
return NS_ERROR_UNEXPECTED;
1089
}
1090
1091
if (!aAccessor->CanAccess(this)) {
1092
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1093
}
1094
1095
nsCOMPtr<nsPIDOMWindowOuter> win(aAccessor->GetDOMWindow());
1096
MOZ_DIAGNOSTIC_ASSERT(win);
1097
if (WindowGlobalChild* wgc =
1098
win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
1099
wgc->SendLoadURI(this, aLoadState, aSetNavigating);
1100
}
1101
}
1102
return NS_OK;
1103
}
1104
1105
nsresult BrowsingContext::InternalLoad(BrowsingContext* aAccessor,
1106
nsDocShellLoadState* aLoadState,
1107
nsIDocShell** aDocShell,
1108
nsIRequest** aRequest) {
1109
if (IsDiscarded() || (aAccessor && aAccessor->IsDiscarded())) {
1110
return NS_OK;
1111
}
1112
1113
bool isActive =
1114
aAccessor && aAccessor->GetIsActive() && !GetIsActive() &&
1115
!Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
1116
if (mDocShell) {
1117
nsresult rv = nsDocShell::Cast(mDocShell)->InternalLoad(
1118
aLoadState, aDocShell, aRequest);
1119
NS_ENSURE_SUCCESS(rv, rv);
1120
1121
// Switch to target tab if we're currently focused window.
1122
// Take loadDivertedInBackground into account so the behavior would be
1123
// the same as how the tab first opened.
1124
nsCOMPtr<nsPIDOMWindowOuter> domWin = GetDOMWindow();
1125
if (isActive && domWin) {
1126
nsFocusManager::FocusWindow(domWin, CallerType::System);
1127
}
1128
1129
// Else we ran out of memory, or were a popup and got blocked,
1130
// or something.
1131
1132
return rv;
1133
}
1134
1135
if (XRE_IsParentProcess()) {
1136
if (ContentParent* cp = Canonical()->GetContentParent()) {
1137
Unused << cp->SendInternalLoad(this, aLoadState, isActive);
1138
}
1139
} else {
1140
MOZ_DIAGNOSTIC_ASSERT(aAccessor);
1141
MOZ_DIAGNOSTIC_ASSERT(aAccessor->Group() == Group());
1142
1143
if (!aAccessor->CanAccess(this)) {
1144
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1145
}
1146
1147
nsCOMPtr<nsPIDOMWindowOuter> win(aAccessor->GetDOMWindow());
1148
MOZ_DIAGNOSTIC_ASSERT(win);
1149
if (WindowGlobalChild* wgc =
1150
win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
1151
wgc->SendInternalLoad(this, aLoadState);
1152
}
1153
}
1154
return NS_OK;
1155
}
1156
1157
void BrowsingContext::DisplayLoadError(const nsAString& aURI) {
1158
MOZ_LOG(GetLog(), LogLevel::Debug, ("DisplayLoadError"));
1159
MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
1160
MOZ_DIAGNOSTIC_ASSERT(mDocShell || XRE_IsParentProcess());
1161
1162
if (mDocShell) {
1163
bool didDisplayLoadError = false;
1164
mDocShell->DisplayLoadError(NS_ERROR_MALFORMED_URI, nullptr,
1165
PromiseFlatString(aURI).get(), nullptr,
1166
&didDisplayLoadError);
1167
} else {
1168
if (ContentParent* cp = Canonical()->GetContentParent()) {
1169
Unused << cp->SendDisplayLoadError(this, PromiseFlatString(aURI));
1170
}
1171
}
1172
}
1173
1174
WindowProxyHolder BrowsingContext::Window() {
1175
return WindowProxyHolder(Self());
1176
}
1177
1178
WindowProxyHolder BrowsingContext::GetFrames(ErrorResult& aError) {
1179
return Window();
1180
}
1181
1182
void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
1183
if (mIsDiscarded) {
1184
return;
1185
}
1186
// FIXME We need to set the Closed field, but only once we're sending the
1187
// DOMWindowClose event (which happens in the process where the
1188
// document for this browsing context is loaded).
1190
if (GetDOMWindow()) {
1191
nsGlobalWindowOuter::Cast(GetDOMWindow())
1192
->CloseOuter(aCallerType == CallerType::System);
1193
} else if (ContentChild* cc = ContentChild::GetSingleton()) {
1194
cc->SendWindowClose(this, aCallerType == CallerType::System);
1195
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
1196
Unused << cp->SendWindowClose(this, aCallerType == CallerType::System);
1197
}
1198
}
1199
1200
void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) {
1201
if (ContentChild* cc = ContentChild::GetSingleton()) {
1202
cc->SendWindowFocus(this, aCallerType);
1203
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
1204
Unused << cp->SendWindowFocus(this, aCallerType);
1205
}
1206
}
1207
1208
void BrowsingContext::Blur(ErrorResult& aError) {
1209
if (ContentChild* cc = ContentChild::GetSingleton()) {
1210
cc->SendWindowBlur(this);
1211
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
1212
Unused << cp->SendWindowBlur(this);
1213
}
1214
}
1215
1216
Nullable<WindowProxyHolder> BrowsingContext::GetWindow() {
1217
if (XRE_IsParentProcess() && !IsInProcess()) {
1218
return nullptr;
1219
}
1220
return WindowProxyHolder(this);
1221
}
1222
1223
Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
1224
if (mIsDiscarded) {
1225
return nullptr;
1226
}
1227
1228
// We never return null or throw an error, but the implementation in
1229
// nsGlobalWindow does and we need to use the same signature.
1230
return WindowProxyHolder(Top());
1231
}
1232
1233
void BrowsingContext::GetOpener(JSContext* aCx,
1234
JS::MutableHandle<JS::Value> aOpener,
1235
ErrorResult& aError) const {
1236
RefPtr<BrowsingContext> opener = GetOpener();
1237
if (!opener) {
1238
aOpener.setNull();
1239
return;
1240
}
1241
1242
if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
1243
aError.NoteJSContextException(aCx);
1244
}
1245
}
1246
1247
Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
1248
if (mIsDiscarded) {
1249
return nullptr;
1250
}
1251
1252
// We never throw an error, but the implementation in nsGlobalWindow does and
1253
// we need to use the same signature.
1254
if (!mParent) {
1255
return WindowProxyHolder(this);
1256
}
1257
return WindowProxyHolder(mParent.get());
1258
}
1259
1260
void BrowsingContext::PostMessageMoz(JSContext* aCx,
1261
JS::Handle<JS::Value> aMessage,
1262
const nsAString& aTargetOrigin,
1263
const Sequence<JSObject*>& aTransfer,
1264
nsIPrincipal& aSubjectPrincipal,
1265
ErrorResult& aError) {
1266
if (mIsDiscarded) {
1267
return;
1268
}
1269
1270
RefPtr<BrowsingContext> sourceBc;
1271
PostMessageData data;
1272
data.targetOrigin() = aTargetOrigin;
1273
data.subjectPrincipal() = &aSubjectPrincipal;
1274
RefPtr<nsGlobalWindowInner> callerInnerWindow;
1275
nsAutoCString scriptLocation;
1276
// We don't need to get the caller's agentClusterId since that is used for
1277
// checking whether it's okay to sharing memory (and it's not allowed to share
1278
// memory cross processes)
1279
if (!nsGlobalWindowOuter::GatherPostMessageData(
1280
aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
1281
getter_AddRefs(data.targetOriginURI()),
1282
getter_AddRefs(data.callerPrincipal()),
1283
getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerURI()),
1284
/* aCallerAgentClusterId */ nullptr, &scriptLocation, aError)) {
1285
return;
1286
}
1287
if (sourceBc && sourceBc->IsDiscarded()) {
1288
return;
1289
}
1290
data.source() = sourceBc;
1291
data.isFromPrivateWindow() =
1292
callerInnerWindow &&
1293
nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
1294
data.innerWindowId() = callerInnerWindow ? callerInnerWindow->WindowID() : 0;
1295
data.scriptLocation() = scriptLocation;
1296
JS::Rooted<JS::Value> transferArray(aCx);
1297
aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
1298
&transferArray);
1299
if (NS_WARN_IF(aError.Failed())) {
1300
return;
1301
}
1302
1303
ipc::StructuredCloneData message;
1304
message.Write(aCx, aMessage, transferArray, JS::CloneDataPolicy(), aError);
1305
if (NS_WARN_IF(aError.Failed())) {
1306
return;
1307
}
1308
1309
ClonedMessageData messageData;
1310
if (ContentChild* cc = ContentChild::GetSingleton()) {
1311
if (!message.BuildClonedMessageDataForChild(cc, messageData)) {
1312
aError.Throw(NS_ERROR_FAILURE);
1313
return;
1314
}
1315
1316
cc->SendWindowPostMessage(this, messageData, data);
1317
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
1318
if (!message.BuildClonedMessageDataForParent(cp, messageData)) {
1319
aError.Throw(NS_ERROR_FAILURE);
1320
return;
1321
}
1322
1323
Unused << cp->SendWindowPostMessage(this, messageData, data);
1324
}
1325
}
1326
1327
void BrowsingContext::PostMessageMoz(JSContext* aCx,
1328
JS::Handle<JS::Value> aMessage,
1329
const WindowPostMessageOptions& aOptions,
1330
nsIPrincipal& aSubjectPrincipal,
1331
ErrorResult& aError) {
1332
PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
1333
aSubjectPrincipal, aError);
1334
}
1335
1336
void BrowsingContext::SendCommitTransaction(ContentParent* aParent,
1337
const BaseTransaction& aTxn,
1338
uint64_t aEpoch) {
1339
Unused << aParent->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
1340
}
1341
1342
void BrowsingContext::SendCommitTransaction(ContentChild* aChild,
1343
const BaseTransaction& aTxn,
1344
uint64_t aEpoch) {
1345
aChild->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
1346
}
1347
1348
BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
1349
MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
1350
MOZ_DIAGNOSTIC_ASSERT(mType == Type::Content);
1351
1352
IPCInitializer init;
1353
init.mId = Id();
1354
init.mParentId = mParent ? mParent->Id() : 0;
1355
init.mCached = IsCached();
1356
init.mWindowless = mWindowless;
1357
init.mFields = mFields.Fields();
1358
return init;
1359
}
1360
1361
already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetParent() {
1362
RefPtr<BrowsingContext> parent;
1363
if (mParentId != 0) {
1364
parent = BrowsingContext::Get(mParentId);
1365
MOZ_RELEASE_ASSERT(parent);
1366
}
1367
return parent.forget();
1368
}
1369
1370
already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
1371
RefPtr<BrowsingContext> opener;
1372
if (GetOpenerId() != 0) {
1373
opener = BrowsingContext::Get(GetOpenerId());
1374
MOZ_RELEASE_ASSERT(opener);
1375
}
1376
return opener.forget();
1377
}
1378
1379
void BrowsingContext::StartDelayedAutoplayMediaComponents() {
1380
if (!mDocShell) {
1381
return;
1382
}
1383
AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64,
1384
XRE_IsParentProcess() ? "Parent" : "Child", Id());
1385
mDocShell->StartDelayedAutoplayMediaComponents();
1386
}
1387
1388
void BrowsingContext::ResetGVAutoplayRequestStatus() {
1389
MOZ_ASSERT(IsTop(),
1390
"Should only set GVAudibleAutoplayRequestStatus in the top-level "
1391
"browsing context");
1392
SetGVAudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
1393
SetGVInaudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
1394
}
1395
1396
void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) {
1397
MOZ_ASSERT(IsTop(),
1398
"Should only set GVAudibleAutoplayRequestStatus in the top-level "
1399
"browsing context");
1400
}
1401
1402
void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
1403
MOZ_ASSERT(IsTop(),
1404
"Should only set GVAudibleAutoplayRequestStatus in the top-level "
1405
"browsing context");
1406
}
1407
1408
void BrowsingContext::DidSet(FieldIndex<IDX_UserActivationState>) {
1409
MOZ_ASSERT_IF(!mIsInProcess, mUserGestureStart.IsNull());
1410
USER_ACTIVATION_LOG("Set user gesture activation %" PRIu8
1411
" for %s browsing context 0x%08" PRIx64,
1412
static_cast<uint8_t>(GetUserActivationState()),
1413
XRE_IsParentProcess() ? "Parent" : "Child", Id());
1414
if (mIsInProcess) {
1415
USER_ACTIVATION_LOG(
1416
"Set user gesture start time for %s browsing context 0x%08" PRIx64,
1417
XRE_IsParentProcess() ? "Parent" : "Child", Id());
1418
mUserGestureStart =
1419
(GetUserActivationState() == UserActivation::State::FullActivated)
1420
? TimeStamp::Now()
1421
: TimeStamp();
1422
}
1423
}
1424
1425
void BrowsingContext::DidSet(FieldIndex<IDX_Muted>) {
1426
MOZ_ASSERT(!mParent, "Set muted flag on non top-level context!");
1427
USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64,
1428
GetMuted(), XRE_IsParentProcess() ? "Parent" : "Child",
1429
Id());
1430
PreOrderWalk([&](BrowsingContext* aContext) {
1431
nsPIDOMWindowOuter* win = aContext->GetDOMWindow();
1432
if (win) {
1433
win->RefreshMediaElementsVolume();
1434
}
1435
});
1436
}
1437
1438
void BrowsingContext::SetAllowContentRetargeting(
1439
bool aAllowContentRetargeting) {
1440
Transaction txn;
1441
txn.SetAllowContentRetargeting(aAllowContentRetargeting);
1442
txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
1443
txn.Commit(this);
1444
}
1445
1446
void BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent) {
1447
Top()->SetUserAgentOverride(aUserAgent);
1448
}
1449
1450
void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) {
1451
MOZ_ASSERT(IsTop());
1452
1453
PreOrderWalk([&](BrowsingContext* aContext) {
1454
nsIDocShell* shell = aContext->GetDocShell();
1455
if (shell) {
1456
shell->ClearCachedUserAgent();
1457
}
1458
});
1459
}
1460
1461
bool BrowsingContext::CheckOnlyOwningProcessCanSet(ContentParent* aSource) {
1462
if (aSource) {
1463
MOZ_ASSERT(XRE_IsParentProcess());
1464
1465
// Double-check ownership if we aren't the setter.
1466
if (!Canonical()->IsOwnedByProcess(aSource->ChildID()) &&
1467
aSource->ChildID() != Canonical()->GetInFlightProcessId()) {
1468
return false;
1469
}
1470
} else if (!IsInProcess() && !XRE_IsParentProcess()) {
1471
// Don't allow this to be set from content processes that
1472
// don't own the BrowsingContext.
1473
return false;
1474
}
1475
1476
return true;
1477
}
1478
1479
bool BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>,
1480
const bool& aAllowContentRetargeting,
1481
ContentParent* aSource) {
1482
return CheckOnlyOwningProcessCanSet(aSource);
1483
}
1484
1485
bool BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>,
1486
const bool& aAllowContentRetargetingOnChildren,
1487
ContentParent* aSource) {
1488
return CheckOnlyOwningProcessCanSet(aSource);
1489
}
1490
1491
bool BrowsingContext::CanSet(FieldIndex<IDX_AllowPlugins>,
1492
const bool& aAllowPlugins,
1493
ContentParent* aSource) {
1494
return CheckOnlyOwningProcessCanSet(aSource);
1495
}
1496
1497
bool BrowsingContext::CanSet(FieldIndex<IDX_UserAgentOverride>,
1498
const nsString& aUserAgent,
1499
ContentParent* aSource) {
1500
if (!IsTop()) {
1501
return false;
1502
}
1503
1504
return CheckOnlyOwningProcessCanSet(aSource);
1505
}
1506
1507
bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) {
1508
if (aSource) {
1509
// Set by a content process, verify that it's this BC's embedder.
1510
MOZ_ASSERT(XRE_IsParentProcess());
1511
return Canonical()->IsEmbeddedInProcess(aSource->ChildID());
1512
}
1513
1514
// In-process case, verify that we've been embedded in this process.
1515
return mEmbeddedByThisProcess;
1516
}
1517
1518
bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>,
1519
const uint64_t& aValue, ContentParent* aSource) {
1520
// Generally allow clearing this. We may want to be more precise about this
1521
// check in the future.
1522
if (aValue == 0) {
1523
return true;
1524
}
1525
1526
// If we don't have a specified source, we're the setting process. The window
1527
// which we're setting this to must be in-process.
1528
RefPtr<BrowsingContext> impliedParent;
1529
if (!aSource) {
1530
nsGlobalWindowInner* innerWindow =
1531
nsGlobalWindowInner::GetInnerWindowWithId(aValue);
1532
if (NS_WARN_IF(!innerWindow)) {
1533
return false;
1534
}
1535
1536
impliedParent = innerWindow->GetBrowsingContext();
1537
}
1538
1539
// If in the parent process, double-check ownership and WindowGlobalParent as
1540
// well.
1541
if (XRE_IsParentProcess()) {
1542
RefPtr<WindowGlobalParent> wgp =
1543
WindowGlobalParent::GetByInnerWindowId(aValue);
1544
if (NS_WARN_IF(!wgp)) {
1545
return false;
1546
}
1547
1548
// Deduce the implied parent from the WindowGlobalParent actor.
1549
if (impliedParent) {
1550
MOZ_ASSERT(impliedParent == wgp->BrowsingContext());
1551
}
1552
impliedParent = wgp->BrowsingContext();
1553
1554
// Double-check ownership if we aren't the setter.
1555
if (aSource &&
1556
!impliedParent->Canonical()->IsOwnedByProcess(aSource->ChildID()) &&
1557
aSource->ChildID() !=
1558
impliedParent->Canonical()->GetInFlightProcessId()) {
1559
return false;
1560
}
1561
}
1562
1563
// If we would have an invalid implied parent, something has gone wrong.
1564
MOZ_ASSERT(impliedParent);
1565
if (NS_WARN_IF(mParent && mParent != impliedParent)) {
1566
return false;
1567
}
1568
1569
return true;
1570
}
1571
1572
bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>,
1573
const Maybe<nsString>&, ContentParent* aSource) {
1574
return CheckOnlyEmbedderCanSet(aSource);
1575
}
1576
1577
bool BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
1578
const uint64_t& aValue, ContentParent* aSource) {
1579
// Generally allow clearing this. We may want to be more precise about this
1580
// check in the future.
1581
if (aValue == 0) {
1582
return true;
1583
}
1584
1585
if (aSource) {
1586
MOZ_ASSERT(XRE_IsParentProcess());
1587
1588
// If in the parent process, double-check ownership and WindowGlobalParent
1589
// as well.
1590
RefPtr<WindowGlobalParent> wgp =
1591
WindowGlobalParent::GetByInnerWindowId(aValue);
1592
if (NS_WARN_IF(!wgp) || NS_WARN_IF(wgp->BrowsingContext() != this)) {
1593
return false;
1594
}
1595
1596
// Double-check ownership if we aren't the setter.
1597
if (!Canonical()->IsOwnedByProcess(aSource->ChildID()) &&
1598
aSource->ChildID() != Canonical()->GetInFlightProcessId()) {
1599
return false;
1600
}
1601
}
1602
1603
// We must have access to the specified context.
1604
RefPtr<WindowContext> window = WindowContext::GetById(aValue);
1605
return window && window->GetBrowsingContext() == this;
1606
}
1607
1608
void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) {
1609
mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId());
1610
if (XRE_IsParentProcess()) {
1611
BrowserParent::UpdateFocusFromBrowsingContext();
1612
}
1613
}
1614
1615
bool BrowsingContext::CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue,
1616
ContentParent* aSource) {
1617
// Ensure that we only mark a browsing context as popup spam once and never
1618
// unmark it.
1619
return aValue && !GetIsPopupSpam();
1620
}
1621
1622
void BrowsingContext::DidSet(FieldIndex<IDX_IsPopupSpam>) {
1623
if (GetIsPopupSpam()) {
1624
PopupBlocker::RegisterOpenPopupSpam();
1625
}
1626
}
1627
1628
bool BrowsingContext::CanSet(FieldIndex<IDX_MessageManagerGroup>,
1629
const nsString& aMessageManagerGroup,
1630
ContentParent* aSource) {
1631
// Should only be set in the parent process on toplevel.
1632
return XRE_IsParentProcess() && !aSource && IsTopContent();
1633
}
1634
1635
bool BrowsingContext::IsLoading() {
1636
if (GetLoading()) {
1637
return true;
1638
}
1639
1640
// If we're in the same process as the page, we're possibly just
1641
// updating the flag.
1642
nsIDocShell* shell = GetDocShell();
1643
if (shell) {
1644
Document* doc = shell->GetDocument();
1645
return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE;
1646
}
1647
1648
return false;
1649
}
1650
1651
void BrowsingContext::DidSet(FieldIndex<IDX_Loading>) {
1652
if (mFields.Get<IDX_Loading>()) {
1653
return;
1654
}
1655
1656
while (!mDeprioritizedLoadRunner.isEmpty()) {
1657
nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
1658
NS_DispatchToCurrentThread(runner.forget());
1659
}
1660
1661
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
1662
Top() == this) {
1663
Group()->FlushPostMessageEvents();
1664
}
1665
}
1666
1667
// Inform the Document for this context of the (potential) change in
1668
// loading state
1669
void BrowsingContext::DidSet(FieldIndex<IDX_AncestorLoading>) {
1670
nsPIDOMWindowOuter* outer = GetDOMWindow();
1671
if (!outer) {
1672
MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
1673
("DidSetAncestorLoading BC: %p -- No outer window", (void*)this));
1674
return;
1675
}
1676
Document* document = nsGlobalWindowOuter::Cast(outer)->GetExtantDoc();
1677
if (document) {
1678
MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
1679
("DidSetAncestorLoading BC: %p -- NotifyLoading(%d, %d, %d)",
1680
(void*)this, GetAncestorLoading(), document->GetReadyStateEnum(),
1681
document->GetReadyStateEnum()));
1682
document->NotifyLoading(GetAncestorLoading(), document->GetReadyStateEnum(),
1683
document->GetReadyStateEnum());
1684
}
1685
}
1686
1687
void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
1688
MOZ_ASSERT(IsLoading());
1689
MOZ_ASSERT(Top() == this);
1690
1691
RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
1692
mDeprioritizedLoadRunner.insertBack(runner);
1693
NS_DispatchToCurrentThreadQueue(
1694
runner.forget(), StaticPrefs::page_load_deprioritization_period(),
1695
EventQueuePriority::Idle);
1696
}
1697
1698
} // namespace dom
1699
1700
namespace ipc {
1701
1702
void IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Write(
1703
IPC::Message* aMsg, IProtocol* aActor,
1704
const dom::MaybeDiscarded<dom::BrowsingContext>& aParam) {
1705
MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() ||
1706
aParam.GetMaybeDiscarded()->EverAttached());
1707
uint64_t id = aParam.ContextId();
1708
WriteIPDLParam(aMsg, aActor, id);
1709
}
1710
1711
bool IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Read(
1712
const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
1713
dom::MaybeDiscarded<dom::BrowsingContext>* aResult) {
1714
uint64_t id = 0;
1715
if (!ReadIPDLParam(aMsg, aIter, aActor, &id)) {
1716
return false;
1717
}
1718
1719
if (id == 0) {
1720
*aResult = nullptr;
1721
} else if (RefPtr<dom::BrowsingContext> bc = dom::BrowsingContext::Get(id)) {
1722
*aResult = std::move(bc);
1723
} else {
1724
aResult->SetDiscarded(id);
1725
}
1726
return true;
1727
}
1728
1729
void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
1730
IPC::Message* aMessage, IProtocol* aActor,
1731
const dom::BrowsingContext::IPCInitializer& aInit) {
1732
// Write actor ID parameters.
1733
WriteIPDLParam(aMessage, aActor, aInit.mId);
1734
WriteIPDLParam(aMessage, aActor, aInit.mParentId);
1735
WriteIPDLParam(aMessage, aActor, aInit.mCached);
1736
WriteIPDLParam(aMessage, aActor, aInit.mWindowless);
1737
WriteIPDLParam(aMessage, aActor, aInit.mFields);
1738
}
1739
1740
bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
1741
const IPC::Message* aMessage, PickleIterator* aIterator, IProtocol* aActor,
1742
dom::BrowsingContext::IPCInitializer* aInit) {
1743
// Read actor ID parameters.
1744
if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mId) ||
1745
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mParentId) ||
1746
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mCached) ||
1747
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mWindowless) ||
1748
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mFields)) {
1749
return false;
1750
}
1751
return true;
1752
}
1753
1754
template struct IPDLParamTraits<dom::BrowsingContext::BaseTransaction>;
1755
1756
} // namespace ipc
1757
} // namespace mozilla