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/CanonicalBrowsingContext.h"
8
9
#include "mozilla/dom/BrowserParent.h"
10
#include "mozilla/dom/BrowsingContextGroup.h"
11
#include "mozilla/dom/WindowGlobalParent.h"
12
#include "mozilla/dom/ContentProcessManager.h"
13
#include "mozilla/dom/MediaController.h"
14
#include "mozilla/dom/MediaControlService.h"
15
#include "mozilla/dom/PlaybackController.h"
16
#include "mozilla/ipc/ProtocolUtils.h"
17
#include "mozilla/NullPrincipal.h"
18
19
#include "nsGlobalWindowOuter.h"
20
21
using namespace mozilla::ipc;
22
23
extern mozilla::LazyLogModule gAutoplayPermissionLog;
24
25
#define AUTOPLAY_LOG(msg, ...) \
26
MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
27
28
namespace mozilla {
29
namespace dom {
30
31
extern mozilla::LazyLogModule gUserInteractionPRLog;
32
33
#define USER_ACTIVATION_LOG(msg, ...) \
34
MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
35
36
CanonicalBrowsingContext::CanonicalBrowsingContext(BrowsingContext* aParent,
37
BrowsingContextGroup* aGroup,
38
uint64_t aBrowsingContextId,
39
uint64_t aOwnerProcessId,
40
uint64_t aEmbedderProcessId,
41
BrowsingContext::Type aType,
42
FieldTuple&& aFields)
43
: BrowsingContext(aParent, aGroup, aBrowsingContextId, aType,
44
std::move(aFields)),
45
mProcessId(aOwnerProcessId),
46
mEmbedderProcessId(aEmbedderProcessId) {
47
// You are only ever allowed to create CanonicalBrowsingContexts in the
48
// parent process.
49
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
50
}
51
52
/* static */
53
already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Get(
54
uint64_t aId) {
55
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
56
return BrowsingContext::Get(aId).downcast<CanonicalBrowsingContext>();
57
}
58
59
/* static */
60
CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
61
BrowsingContext* aContext) {
62
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
63
return static_cast<CanonicalBrowsingContext*>(aContext);
64
}
65
66
/* static */
67
const CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
68
const BrowsingContext* aContext) {
69
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
70
return static_cast<const CanonicalBrowsingContext*>(aContext);
71
}
72
73
ContentParent* CanonicalBrowsingContext::GetContentParent() const {
74
if (mProcessId == 0) {
75
return nullptr;
76
}
77
78
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
79
return cpm->GetContentProcessById(ContentParentId(mProcessId));
80
}
81
82
void CanonicalBrowsingContext::GetCurrentRemoteType(nsAString& aRemoteType,
83
ErrorResult& aRv) const {
84
// If we're in the parent process, dump out the void string.
85
if (mProcessId == 0) {
86
aRemoteType.Assign(VoidString());
87
return;
88
}
89
90
ContentParent* cp = GetContentParent();
91
if (!cp) {
92
aRv.Throw(NS_ERROR_UNEXPECTED);
93
return;
94
}
95
96
aRemoteType.Assign(cp->GetRemoteType());
97
}
98
99
void CanonicalBrowsingContext::SetOwnerProcessId(uint64_t aProcessId) {
100
MOZ_LOG(GetLog(), LogLevel::Debug,
101
("SetOwnerProcessId for 0x%08" PRIx64 " (0x%08" PRIx64
102
" -> 0x%08" PRIx64 ")",
103
Id(), mProcessId, aProcessId));
104
105
mProcessId = aProcessId;
106
}
107
108
void CanonicalBrowsingContext::SetInFlightProcessId(uint64_t aProcessId) {
109
// We can't handle more than one in-flight process change at a time.
110
MOZ_ASSERT_IF(aProcessId, mInFlightProcessId == 0);
111
112
mInFlightProcessId = aProcessId;
113
}
114
115
void CanonicalBrowsingContext::GetWindowGlobals(
116
nsTArray<RefPtr<WindowGlobalParent>>& aWindows) {
117
aWindows.SetCapacity(GetWindowContexts().Length());
118
for (auto& window : GetWindowContexts()) {
119
aWindows.AppendElement(static_cast<WindowGlobalParent*>(window.get()));
120
}
121
}
122
123
WindowGlobalParent* CanonicalBrowsingContext::GetCurrentWindowGlobal() const {
124
return static_cast<WindowGlobalParent*>(GetCurrentWindowContext());
125
}
126
127
already_AddRefed<nsIWidget>
128
CanonicalBrowsingContext::GetParentProcessWidgetContaining() {
129
// If our document is loaded in-process, such as chrome documents, get the
130
// widget directly from our outer window. Otherwise, try to get the widget
131
// from the toplevel content's browser's element.
132
nsCOMPtr<nsIWidget> widget;
133
if (nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetDOMWindow())) {
134
widget = window->GetNearestWidget();
135
} else if (Element* topEmbedder = Top()->GetEmbedderElement()) {
136
widget = nsContentUtils::WidgetForContent(topEmbedder);
137
if (!widget) {
138
widget = nsContentUtils::WidgetForDocument(topEmbedder->OwnerDoc());
139
}
140
}
141
142
if (widget) {
143
widget = widget->GetTopLevelWidget();
144
}
145
146
return widget.forget();
147
}
148
149
already_AddRefed<WindowGlobalParent>
150
CanonicalBrowsingContext::GetEmbedderWindowGlobal() const {
151
uint64_t windowId = GetEmbedderInnerWindowId();
152
if (windowId == 0) {
153
return nullptr;
154
}
155
156
return WindowGlobalParent::GetByInnerWindowId(windowId);
157
}
158
159
nsISHistory* CanonicalBrowsingContext::GetSessionHistory() {
160
if (mSessionHistory) {
161
return mSessionHistory;
162
}
163
164
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(GetDocShell());
165
if (webNav) {
166
RefPtr<ChildSHistory> shistory = webNav->GetSessionHistory();
167
if (shistory) {
168
return shistory->LegacySHistory();
169
}
170
}
171
172
return nullptr;
173
}
174
175
JSObject* CanonicalBrowsingContext::WrapObject(
176
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
177
return CanonicalBrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
178
}
179
180
void CanonicalBrowsingContext::CanonicalDiscard() {
181
if (mTabMediaController) {
182
mTabMediaController->Shutdown();
183
mTabMediaController = nullptr;
184
}
185
}
186
187
void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() {
188
if (!GetCurrentWindowGlobal()) {
189
return;
190
}
191
192
// As this function would only be called when user click the play icon on the
193
// tab bar. That's clear user intent to play, so gesture activate the browsing
194
// context so that the block-autoplay logic allows the media to autoplay.
195
NotifyUserGestureActivation();
196
AUTOPLAY_LOG("NotifyStartDelayedAutoplayMedia for chrome bc 0x%08" PRIx64,
197
Id());
198
StartDelayedAutoplayMediaComponents();
199
// Notfiy all content browsing contexts which are related with the canonical
200
// browsing content tree to start delayed autoplay media.
201
202
Group()->EachParent([&](ContentParent* aParent) {
203
Unused << aParent->SendStartDelayedAutoplayMediaComponents(this);
204
});
205
}
206
207
void CanonicalBrowsingContext::NotifyMediaMutedChanged(bool aMuted) {
208
MOZ_ASSERT(!GetParent(),
209
"Notify media mute change on non top-level context!");
210
SetMuted(aMuted);
211
}
212
213
uint32_t CanonicalBrowsingContext::CountSiteOrigins(
214
GlobalObject& aGlobal,
215
const Sequence<OwningNonNull<BrowsingContext>>& aRoots) {
216
nsTHashtable<nsCStringHashKey> uniqueSiteOrigins;
217
218
for (const auto& root : aRoots) {
219
root->PreOrderWalk([&](BrowsingContext* aContext) {
220
WindowGlobalParent* windowGlobalParent =
221
aContext->Canonical()->GetCurrentWindowGlobal();
222
if (windowGlobalParent) {
223
nsIPrincipal* documentPrincipal =
224
windowGlobalParent->DocumentPrincipal();
225
226
bool isContentPrincipal = documentPrincipal->GetIsContentPrincipal();
227
if (isContentPrincipal) {
228
nsCString siteOrigin;
229
documentPrincipal->GetSiteOrigin(siteOrigin);
230
uniqueSiteOrigins.PutEntry(siteOrigin);
231
}
232
}
233
});
234
}
235
236
return uniqueSiteOrigins.Count();
237
}
238
239
void CanonicalBrowsingContext::UpdateMediaControlKeysEvent(
240
MediaControlKeysEvent aEvent) {
241
MediaActionHandler::HandleMediaControlKeysEvent(this, aEvent);
242
Group()->EachParent([&](ContentParent* aParent) {
243
Unused << aParent->SendUpdateMediaControlKeysEvent(this, aEvent);
244
});
245
}
246
247
void CanonicalBrowsingContext::LoadURI(const nsAString& aURI,
248
const LoadURIOptions& aOptions,
249
ErrorResult& aError) {
250
nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
251
252
nsCOMPtr<nsISupports> consumer = GetDocShell();
253
if (!consumer) {
254
consumer = GetEmbedderElement();
255
}
256
if (!consumer) {
257
aError.Throw(NS_ERROR_UNEXPECTED);
258
return;
259
}
260
261
RefPtr<nsDocShellLoadState> loadState;
262
nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
263
consumer, uriFixup, aURI, aOptions, getter_AddRefs(loadState));
264
265
if (rv == NS_ERROR_MALFORMED_URI) {
266
DisplayLoadError(aURI);
267
return;
268
}
269
270
if (NS_FAILED(rv)) {
271
aError.Throw(rv);
272
return;
273
}
274
275
// NOTE: It's safe to call `LoadURI` without an accessor from the parent
276
// process. The load will be performed with ambient "chrome" authority.
277
LoadURI(nullptr, loadState, true);
278
}
279
280
void CanonicalBrowsingContext::PendingRemotenessChange::Complete(
281
ContentParent* aContentParent) {
282
if (!mPromise) {
283
return;
284
}
285
286
RefPtr<CanonicalBrowsingContext> target(mTarget);
287
if (target->IsDiscarded()) {
288
Cancel(NS_ERROR_FAILURE);
289
return;
290
}
291
292
RefPtr<WindowGlobalParent> embedderWindow = target->GetEmbedderWindowGlobal();
293
if (NS_WARN_IF(!embedderWindow) || NS_WARN_IF(!embedderWindow->CanSend())) {
294
Cancel(NS_ERROR_FAILURE);
295
return;
296
}
297
298
RefPtr<BrowserParent> embedderBrowser = embedderWindow->GetBrowserParent();
299
if (NS_WARN_IF(!embedderBrowser)) {
300
Cancel(NS_ERROR_FAILURE);
301
return;
302
}
303
304
// Pull load flags from our embedder browser.
305
nsCOMPtr<nsILoadContext> loadContext = embedderBrowser->GetLoadContext();
306
MOZ_DIAGNOSTIC_ASSERT(
307
loadContext->UseRemoteTabs() && loadContext->UseRemoteSubframes(),
308
"Not supported without fission");
309
310
// NOTE: These are the only flags we actually care about
311
uint32_t chromeFlags = nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
312
nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
313
if (loadContext->UsePrivateBrowsing()) {
314
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
315
}
316
317
TabId tabId(nsContentUtils::GenerateTabId());
318
RefPtr<BrowserBridgeParent> bridge = new BrowserBridgeParent();
319
ManagedEndpoint<PBrowserBridgeChild> endpoint =
320
embedderBrowser->OpenPBrowserBridgeEndpoint(bridge);
321
if (NS_WARN_IF(!endpoint.IsValid())) {
322
Cancel(NS_ERROR_UNEXPECTED);
323
return;
324
}
325
326
RefPtr<WindowGlobalParent> oldWindow = target->GetCurrentWindowGlobal();
327
RefPtr<BrowserParent> oldBrowser =
328
oldWindow ? oldWindow->GetBrowserParent() : nullptr;
329
bool wasRemote = oldWindow && oldWindow->IsProcessRoot();
330
331
// Update which process is considered the current owner
332
uint64_t inFlightProcessId = target->OwnerProcessId();
333
target->SetInFlightProcessId(inFlightProcessId);
334
target->SetOwnerProcessId(aContentParent->ChildID());
335
336
auto resetInFlightId = [target, inFlightProcessId] {
337
if (target->GetInFlightProcessId() == inFlightProcessId) {
338
target->SetInFlightProcessId(0);
339
} else {
340
MOZ_DIAGNOSTIC_ASSERT(false, "Unexpected InFlightProcessId");
341
}
342
};
343
344
// If we were in a remote frame, trigger unloading of the remote window. When
345
// the original remote window acknowledges, we can clear the in-flight ID.
346
if (wasRemote) {
347
MOZ_DIAGNOSTIC_ASSERT(oldBrowser);
348
MOZ_DIAGNOSTIC_ASSERT(oldBrowser != embedderBrowser);
349
MOZ_DIAGNOSTIC_ASSERT(oldBrowser->GetBrowserBridgeParent());
350
351
auto callback = [resetInFlightId](auto) { resetInFlightId(); };
352
oldBrowser->SendWillChangeProcess(callback, callback);
353
oldBrowser->Destroy();
354
}
355
356
// Tell the embedder process a remoteness change is in-process. When this is
357
// acknowledged, reset the in-flight ID if it used to be an in-process load.
358
{
359
auto callback = [wasRemote, resetInFlightId](auto) {
360
if (!wasRemote) {
361
resetInFlightId();
362
}
363
};
364
embedderWindow->SendMakeFrameRemote(target, std::move(endpoint), tabId,
365
callback, callback);
366
}
367
368
// FIXME: We should get the correct principal for the to-be-created window so
369
// we can avoid creating unnecessary extra windows in the new process.
370
OriginAttributes attrs = embedderBrowser->OriginAttributesRef();
371
RefPtr<nsIPrincipal> principal = embedderBrowser->GetContentPrincipal();
372
if (principal) {
373
attrs.SetFirstPartyDomain(
374
true, principal->OriginAttributesRef().mFirstPartyDomain);
375
}
376
377
nsCOMPtr<nsIPrincipal> initialPrincipal =
378
NullPrincipal::CreateWithInheritedAttributes(attrs,
379
/* isFirstParty */ false);
380
WindowGlobalInit windowInit =
381
WindowGlobalActor::AboutBlankInitializer(target, initialPrincipal);
382
383
// Actually create the new BrowserParent actor and finish initialization of
384
// our new BrowserBridgeParent.
385
nsresult rv = bridge->InitWithProcess(aContentParent, EmptyString(),
386
windowInit, chromeFlags, tabId);
387
if (NS_WARN_IF(NS_FAILED(rv))) {
388
Cancel(rv);
389
return;
390
}
391
392
RefPtr<BrowserParent> newBrowser = bridge->GetBrowserParent();
393
newBrowser->ResumeLoad(mPendingSwitchId);
394
395
// We did it! The process switch is complete.
396
mPromise->Resolve(newBrowser, __func__);
397
Clear();
398
}
399
400
void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv) {
401
if (!mPromise) {
402
return;
403
}
404
405
mPromise->Reject(aRv, __func__);
406
Clear();
407
}
408
409
void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
410
// Make sure we don't die while we're doing cleanup.
411
RefPtr<PendingRemotenessChange> kungFuDeathGrip(this);
412
if (mTarget) {
413
MOZ_DIAGNOSTIC_ASSERT(mTarget->mPendingRemotenessChange == this);
414
mTarget->mPendingRemotenessChange = nullptr;
415
}
416
417
mPromise = nullptr;
418
mTarget = nullptr;
419
}
420
421
CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
422
MOZ_ASSERT(!mPromise && !mTarget,
423
"should've already been Cancel() or Complete()-ed");
424
}
425
426
RefPtr<CanonicalBrowsingContext::RemotenessPromise>
427
CanonicalBrowsingContext::ChangeFrameRemoteness(const nsAString& aRemoteType,
428
uint64_t aPendingSwitchId) {
429
// Ensure our embedder hasn't been destroyed already.
430
RefPtr<WindowGlobalParent> embedderWindowGlobal = GetEmbedderWindowGlobal();
431
if (!embedderWindowGlobal) {
432
NS_WARNING("Non-embedded BrowsingContext");
433
return RemotenessPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__);
434
}
435
436
if (!embedderWindowGlobal->CanSend()) {
437
NS_WARNING("Embedder already been destroyed.");
438
return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
439
}
440
441
RefPtr<ContentParent> oldContent = GetContentParent();
442
if (!oldContent || aRemoteType.IsEmpty()) {
443
NS_WARNING("Cannot switch to or from non-remote frame");
444
return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED,
445
__func__);
446
}
447
448
if (aRemoteType.Equals(oldContent->GetRemoteType())) {
449
NS_WARNING("Already in the correct process");
450
return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
451
}
452
453
// Cancel ongoing remoteness changes.
454
if (mPendingRemotenessChange) {
455
mPendingRemotenessChange->Cancel(NS_ERROR_ABORT);
456
MOZ_ASSERT(!mPendingRemotenessChange, "Should have cleared");
457
}
458
459
RefPtr<BrowserParent> embedderBrowser =
460
embedderWindowGlobal->GetBrowserParent();
461
MOZ_ASSERT(embedderBrowser);
462
463
// Switching to local. No new process, so perform switch sync.
464
if (aRemoteType.Equals(embedderBrowser->Manager()->GetRemoteType())) {
465
if (GetCurrentWindowGlobal()) {
466
MOZ_DIAGNOSTIC_ASSERT(GetCurrentWindowGlobal()->IsProcessRoot());
467
RefPtr<BrowserParent> oldBrowser =
468
GetCurrentWindowGlobal()->GetBrowserParent();
469
470
RefPtr<CanonicalBrowsingContext> target(this);
471
SetInFlightProcessId(OwnerProcessId());
472
oldBrowser->SendWillChangeProcess(
473
[target](bool aSuccess) { target->SetInFlightProcessId(0); },
474
[target](mozilla::ipc::ResponseRejectReason aReason) {
475
target->SetInFlightProcessId(0);
476
});
477
oldBrowser->Destroy();
478
}
479
480
SetOwnerProcessId(embedderBrowser->Manager()->ChildID());
481
Unused << embedderWindowGlobal->SendMakeFrameLocal(this, aPendingSwitchId);
482
return RemotenessPromise::CreateAndResolve(embedderBrowser, __func__);
483
}
484
485
// Switching to remote. Wait for new process to launch before switch.
486
auto promise = MakeRefPtr<RemotenessPromise::Private>(__func__);
487
RefPtr<PendingRemotenessChange> change =
488
new PendingRemotenessChange(this, promise, aPendingSwitchId);
489
mPendingRemotenessChange = change;
490
491
ContentParent::GetNewOrUsedBrowserProcessAsync(
492
/* aFrameElement = */ nullptr,
493
/* aRemoteType = */ aRemoteType,
494
/* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND,
495
/* aOpener = */ nullptr,
496
/* aPreferUsed = */ false)
497
->Then(
498
GetMainThreadSerialEventTarget(), __func__,
499
[change](ContentParent* aContentParent) {
500
change->Complete(aContentParent);
501
},
502
[change](LaunchError aError) { change->Cancel(NS_ERROR_FAILURE); });
503
return promise.forget();
504
}
505
506
already_AddRefed<Promise> CanonicalBrowsingContext::ChangeFrameRemoteness(
507
const nsAString& aRemoteType, uint64_t aPendingSwitchId, ErrorResult& aRv) {
508
nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
509
510
RefPtr<Promise> promise = Promise::Create(global, aRv);
511
if (aRv.Failed()) {
512
return nullptr;
513
}
514
515
ChangeFrameRemoteness(aRemoteType, aPendingSwitchId)
516
->Then(
517
GetMainThreadSerialEventTarget(), __func__,
518
[promise](BrowserParent* aBrowserParent) {
519
promise->MaybeResolve(aBrowserParent->Manager()->ChildID());
520
},
521
[promise](nsresult aRv) { promise->MaybeReject(aRv); });
522
return promise.forget();
523
}
524
525
MediaController* CanonicalBrowsingContext::GetMediaController() {
526
// We would only create one media controller per tab, so accessing the
527
// controller via the top-level browsing context.
528
if (GetParent()) {
529
return Cast(Top())->GetMediaController();
530
}
531
532
MOZ_ASSERT(!GetParent(),
533
"Must access the controller from the top-level browsing context!");
534
// Only content browsing context can create media controller, we won't create
535
// controller for chrome document, such as the browser UI.
536
if (!mTabMediaController && !IsDiscarded() && IsContent()) {
537
mTabMediaController = new MediaController(Id());
538
}
539
return mTabMediaController;
540
}
541
542
NS_IMPL_CYCLE_COLLECTION_INHERITED(CanonicalBrowsingContext, BrowsingContext,
543
mSessionHistory)
544
545
NS_IMPL_ADDREF_INHERITED(CanonicalBrowsingContext, BrowsingContext)
546
NS_IMPL_RELEASE_INHERITED(CanonicalBrowsingContext, BrowsingContext)
547
548
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanonicalBrowsingContext)
549
NS_INTERFACE_MAP_END_INHERITING(BrowsingContext)
550
551
} // namespace dom
552
} // namespace mozilla