WindowGlobalChild.cpp 22.8 KB
Newer Older
1
2
3
4
5
6
7
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/dom/WindowGlobalChild.h"
8

9
#include "mozilla/AntiTrackingUtils.h"
10
#include "mozilla/ClearOnShutdown.h"
11
#include "mozilla/dom/WindowGlobalParent.h"
12
#include "mozilla/dom/BrowsingContext.h"
13
#include "mozilla/dom/BrowsingContextGroup.h"
14
#include "mozilla/dom/ContentChild.h"
15
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
16
#include "mozilla/dom/BrowserChild.h"
17
#include "mozilla/dom/BrowserBridgeChild.h"
18
#include "mozilla/dom/ContentParent.h"
19
#include "mozilla/dom/SecurityPolicyViolationEvent.h"
20
#include "mozilla/dom/WindowGlobalActorsBinding.h"
21
#include "mozilla/dom/WindowGlobalParent.h"
22
#include "mozilla/dom/WindowContext.h"
23
#include "mozilla/ipc/InProcessChild.h"
24
#include "mozilla/ipc/InProcessParent.h"
25
#include "nsContentUtils.h"
26
#include "nsDocShell.h"
27
#include "nsFocusManager.h"
28
#include "nsFrameLoaderOwner.h"
29
#include "nsGlobalWindowInner.h"
30
31
#include "nsFrameLoaderOwner.h"
#include "nsQueryObject.h"
32
#include "nsSerializationHelper.h"
33
#include "nsFrameLoader.h"
34

35
36
#include "mozilla/dom/JSWindowActorBinding.h"
#include "mozilla/dom/JSWindowActorChild.h"
37
#include "mozilla/dom/JSActorService.h"
38
#include "nsIHttpChannelInternal.h"
39
#include "nsIURIMutator.h"
40

41
42
#include "mozilla/dom/nsMixedContentBlocker.h"

43
44
45
using namespace mozilla::ipc;
using namespace mozilla::dom::ipc;

46
47
48
namespace mozilla {
namespace dom {

49
50
51
typedef nsRefPtrHashtable<nsUint64HashKey, WindowGlobalChild> WGCByIdMap;
static StaticAutoPtr<WGCByIdMap> gWindowGlobalChildById;

52
53
54
55
56
57
58
WindowGlobalChild::WindowGlobalChild(dom::WindowContext* aWindowContext,
                                     nsIPrincipal* aPrincipal,
                                     nsIURI* aDocumentURI)
    : mWindowContext(aWindowContext),
      mDocumentPrincipal(aPrincipal),
      mDocumentURI(aDocumentURI) {
  MOZ_DIAGNOSTIC_ASSERT(mWindowContext);
59
60
  MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal);

61
62
63
  if (!mDocumentURI) {
    NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank");
  }
64
65
66
67
68
69
70
71
72
73
74
75
76
77

#ifdef MOZ_GECKO_PROFILER
  // Registers a DOM Window with the profiler. It re-registers the same Inner
  // Window ID with different URIs because when a Browsing context is first
  // loaded, the first url loaded in it will be about:blank. This call keeps the
  // first non-about:blank registration of window and discards the previous one.
  uint64_t embedderInnerWindowID = 0;
  if (BrowsingContext()->GetParent()) {
    embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
  }
  profiler_register_page(BrowsingContext()->Id(), InnerWindowId(),
                         aDocumentURI->GetSpecOrDefault(),
                         embedderInnerWindowID);
#endif
78
}
79
80
81

already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create(
    nsGlobalWindowInner* aWindow) {
82
83
84
85
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  // Opener policy is set when we start to load a document. Here, we ensure we
  // have set the correct Opener policy so that it will be available in the
  // parent process through window global child.
86
87
88
  nsCOMPtr<nsIChannel> chan = aWindow->GetDocument()->GetChannel();
  nsCOMPtr<nsILoadInfo> loadInfo = chan ? chan->LoadInfo() : nullptr;
  nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(chan);
89
  nsILoadInfo::CrossOriginOpenerPolicy policy;
90
91
92
  if (httpChan &&
      loadInfo->GetExternalContentPolicyType() ==
          nsIContentPolicy::TYPE_DOCUMENT &&
93
      NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy))) {
94
95
    MOZ_DIAGNOSTIC_ASSERT(policy ==
                          aWindow->GetBrowsingContext()->GetOpenerPolicy());
96
  }
97
#endif
98

99
100
  WindowGlobalInit init = WindowGlobalActor::WindowInitializer(aWindow);
  RefPtr<WindowGlobalChild> wgc = CreateDisconnected(init);
101

102
  // Send the link constructor over PBrowser, or link over PInProcess.
103
  if (XRE_IsParentProcess()) {
104
105
106
    InProcessChild* ipChild = InProcessChild::Singleton();
    InProcessParent* ipParent = InProcessParent::Singleton();
    if (!ipChild || !ipParent) {
107
108
109
      return nullptr;
    }

110
    ManagedEndpoint<PWindowGlobalParent> endpoint =
111
        ipChild->OpenPWindowGlobalEndpoint(wgc);
112
113
    ipParent->BindPWindowGlobalEndpoint(std::move(endpoint),
                                        wgc->WindowContext()->Canonical());
114
  } else {
115
116
117
118
    RefPtr<BrowserChild> browserChild =
        BrowserChild::GetFrom(static_cast<mozIDOMWindow*>(aWindow));
    MOZ_ASSERT(browserChild);

119
    ManagedEndpoint<PWindowGlobalParent> endpoint =
120
        browserChild->OpenPWindowGlobalEndpoint(wgc);
121
    browserChild->SendNewWindowGlobal(std::move(endpoint), init);
122
123
  }

124
  wgc->Init();
125
  wgc->InitWindowGlobal(aWindow);
126
127
128
  return wgc.forget();
}

129
130
131
132
already_AddRefed<WindowGlobalChild> WindowGlobalChild::CreateDisconnected(
    const WindowGlobalInit& aInit) {
  RefPtr<dom::BrowsingContext> browsingContext =
      dom::BrowsingContext::Get(aInit.context().mBrowsingContextId);
133

134
135
136
137
138
  RefPtr<dom::WindowContext> windowContext =
      dom::WindowContext::GetById(aInit.context().mInnerWindowId);
  MOZ_RELEASE_ASSERT(!windowContext, "Creating duplicate WindowContext");

  // Create our new WindowContext
139
  if (XRE_IsParentProcess()) {
140
141
    windowContext =
        WindowGlobalParent::CreateDisconnected(aInit, /* aInProcess */ true);
142
  } else {
143
144
145
146
147
    dom::WindowContext::FieldTuple fields = aInit.context().mFields;
    windowContext =
        new dom::WindowContext(browsingContext, aInit.context().mInnerWindowId,
                               aInit.context().mOuterWindowId,
                               /* aInProcess */ true, std::move(fields));
148
149
  }

150
151
152
153
154
155
156
157
  RefPtr<WindowGlobalChild> windowChild = new WindowGlobalChild(
      windowContext, aInit.principal(), aInit.documentURI());
  return windowChild.forget();
}

void WindowGlobalChild::Init() {
  mWindowContext->Init();

158
159
160
161
162
  // Register this WindowGlobal in the gWindowGlobalParentsById map.
  if (!gWindowGlobalChildById) {
    gWindowGlobalChildById = new WGCByIdMap();
    ClearOnShutdown(&gWindowGlobalChildById);
  }
163
  auto entry = gWindowGlobalChildById->LookupForAdd(InnerWindowId());
164
  MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalChild entry for ID!");
165
  entry.OrInsert([&] { return this; });
166
167
}

168
169
170
171
void WindowGlobalChild::InitWindowGlobal(nsGlobalWindowInner* aWindow) {
  mWindowGlobal = aWindow;
}

172
173
174
175
176
void WindowGlobalChild::OnNewDocument(Document* aDocument) {
  MOZ_RELEASE_ASSERT(mWindowGlobal);
  MOZ_RELEASE_ASSERT(aDocument);

  // Send a series of messages to update document-specific state on
177
178
179
180
  // WindowGlobalParent, when we change documents on an existing WindowGlobal.
  // This data is also all sent when we construct a WindowGlobal, so anything
  // added here should also be added to WindowGlobalActor::WindowInitializer.

181
182
183
  // FIXME: Perhaps these should be combined into a smaller number of messages?
  SetDocumentURI(aDocument->GetDocumentURI());
  SetDocumentPrincipal(aDocument->NodePrincipal());
184
185
186
187
188
189
190
191
192

  nsCOMPtr<nsITransportSecurityInfo> securityInfo;
  if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
    nsCOMPtr<nsISupports> securityInfoSupports;
    channel->GetSecurityInfo(getter_AddRefs(securityInfoSupports));
    securityInfo = do_QueryInterface(securityInfoSupports);
  }
  SendUpdateDocumentSecurityInfo(securityInfo);

193
194
195
196
197
198
199
200
201
202
203
204
205
  SendUpdateDocumentCspSettings(aDocument->GetBlockAllMixedContent(false),
                                aDocument->GetUpgradeInsecureRequests(false));
  SendUpdateSandboxFlags(aDocument->GetSandboxFlags());

  net::CookieJarSettingsArgs csArgs;
  net::CookieJarSettings::Cast(aDocument->CookieJarSettings())
      ->Serialize(csArgs);
  if (!SendUpdateCookieJarSettings(csArgs)) {
    NS_WARNING(
        "Failed to update document's cookie jar settings on the "
        "WindowGlobalParent");
  }

206
207
  SendUpdateHttpsOnlyStatus(aDocument->HttpsOnlyStatus());

208
209
210
211
212
213
214
215
216
217
  // Update window context fields for the newly loaded Document.
  WindowContext::Transaction txn;
  txn.SetCookieBehavior(
      Some(aDocument->CookieJarSettings()->GetCookieBehavior()));
  txn.SetIsOnContentBlockingAllowList(
      aDocument->CookieJarSettings()->GetIsOnContentBlockingAllowList());
  txn.SetIsThirdPartyWindow(nsContentUtils::IsThirdPartyWindowOrChannel(
      mWindowGlobal, nullptr, nullptr));
  txn.SetIsThirdPartyTrackingResourceWindow(
      nsContentUtils::IsThirdPartyTrackingResourceWindow(mWindowGlobal));
218
  txn.SetIsSecureContext(mWindowGlobal->IsSecureContext());
219
220
221
222
  auto policy = aDocument->GetEmbedderPolicyFromHTTP();
  if (policy.isSome()) {
    txn.SetEmbedderPolicy(policy.ref());
  }
223
224

  // Init Mixed Content Fields
225
226
227
  nsCOMPtr<nsIURI> innerDocURI =
      NS_GetInnermostURI(aDocument->GetDocumentURI());
  if (innerDocURI) {
228
229
230
    txn.SetIsSecure(
        innerDocURI->SchemeIs("https") ||
        nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI));
231
  }
232
233
234
235
236
237
238
239
240
241
242
243
  nsCOMPtr<nsIChannel> mixedChannel;
  mWindowGlobal->GetDocShell()->GetMixedContentChannel(
      getter_AddRefs(mixedChannel));
  // A non null mixedContent channel on the docshell indicates,
  // that the user has overriden mixed content to allow mixed
  // content loads to happen.
  if (mixedChannel && (mixedChannel == aDocument->GetChannel())) {
    txn.SetAllowMixedContent(true);
  }
  txn.Commit(mWindowContext);
}

244
245
246
/* static */
already_AddRefed<WindowGlobalChild> WindowGlobalChild::GetByInnerWindowId(
    uint64_t aInnerWindowId) {
247
248
249
250
251
252
  if (!gWindowGlobalChildById) {
    return nullptr;
  }
  return gWindowGlobalChildById->Get(aInnerWindowId);
}

253
254
255
256
257
258
259
260
261
262
263
264
dom::BrowsingContext* WindowGlobalChild::BrowsingContext() {
  return mWindowContext->GetBrowsingContext();
}

uint64_t WindowGlobalChild::InnerWindowId() {
  return mWindowContext->InnerWindowId();
}

uint64_t WindowGlobalChild::OuterWindowId() {
  return mWindowContext->OuterWindowId();
}

265
bool WindowGlobalChild::IsCurrentGlobal() {
266
  return CanSend() && mWindowGlobal->IsCurrentInnerWindow();
267
268
}

269
already_AddRefed<WindowGlobalParent> WindowGlobalChild::GetParentActor() {
270
  if (!CanSend()) {
271
272
273
274
275
276
    return nullptr;
  }
  IProtocol* otherSide = InProcessChild::ParentActorFor(this);
  return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
}

277
already_AddRefed<BrowserChild> WindowGlobalChild::GetBrowserChild() {
278
279
280
281
  if (IsInProcess() || !CanSend()) {
    return nullptr;
  }
  return do_AddRef(static_cast<BrowserChild*>(Manager()));
282
283
}

284
285
286
287
288
289
290
uint64_t WindowGlobalChild::ContentParentId() {
  if (XRE_IsParentProcess()) {
    return 0;
  }
  return ContentChild::GetSingleton()->GetID();
}

291
292
293
294
295
296
297
298
299
300
// A WindowGlobalChild is the root in its process if it has no parent, or its
// embedder is in a different process.
bool WindowGlobalChild::IsProcessRoot() {
  if (!BrowsingContext()->GetParent()) {
    return true;
  }

  return !BrowsingContext()->GetEmbedderElement();
}

301
302
void WindowGlobalChild::BeforeUnloadAdded() {
  // Don't bother notifying the parent if we don't have an IPC link open.
303
  if (mBeforeUnloadListeners == 0 && CanSend()) {
304
305
306
307
308
309
310
311
312
313
314
315
    SendSetHasBeforeUnload(true);
  }

  mBeforeUnloadListeners++;
  MOZ_ASSERT(mBeforeUnloadListeners > 0);
}

void WindowGlobalChild::BeforeUnloadRemoved() {
  mBeforeUnloadListeners--;
  MOZ_ASSERT(mBeforeUnloadListeners >= 0);

  // Don't bother notifying the parent if we don't have an IPC link open.
316
  if (mBeforeUnloadListeners == 0 && CanSend()) {
317
318
319
320
    SendSetHasBeforeUnload(false);
  }
}

321
void WindowGlobalChild::Destroy() {
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
  // Destroying a WindowGlobalChild requires running script, so hold off on
  // doing it until we can safely run JS callbacks.
  nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
      "WindowGlobalChild::Destroy", [self = RefPtr<WindowGlobalChild>(this)]() {
        // Make a copy so that we can avoid potential iterator invalidation when
        // calling the user-provided Destroy() methods.
        nsTArray<RefPtr<JSWindowActorChild>> windowActors(
            self->mWindowActors.Count());
        for (auto iter = self->mWindowActors.Iter(); !iter.Done();
             iter.Next()) {
          windowActors.AppendElement(iter.UserData());
        }

        for (auto& windowActor : windowActors) {
          windowActor->StartDestroy();
        }

339
340
341
342
343
344
345
        // Perform async IPC shutdown unless we're not in-process, and our
        // BrowserChild is in the process of being destroyed, which will destroy
        // us as well.
        RefPtr<BrowserChild> browserChild = self->GetBrowserChild();
        if (!browserChild || !browserChild->IsDestroyed()) {
          self->SendDestroy();
        }
346
      }));
347
348
}

349
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
350
351
    const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
    uint64_t aPendingSwitchId) {
352
353
  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());

354
355
  MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
          ("RecvMakeFrameLocal ID=%" PRIx64, aFrameContext.ContextId()));
356

357
358
359
360
361
362
  if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
    return IPC_OK();
  }
  dom::BrowsingContext* frameContext = aFrameContext.get();

  RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
363
  if (NS_WARN_IF(!embedderElt)) {
364
    return IPC_OK();
365
366
  }

367
  if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
368
    return IPC_OK();
369
370
371
  }

  RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
372
  MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
373

374
  // Trigger a process switch into the current process.
375
  RemotenessOptions options;
376
  options.mRemoteType.Assign(VoidString());
377
  options.mPendingSwitchID.Construct(aPendingSwitchId);
378
379
380
  flo->ChangeRemoteness(options, IgnoreErrors());
  return IPC_OK();
}
381

382
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote(
383
    const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
384
    ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
385
    const LayersId& aLayersId, MakeFrameRemoteResolver&& aResolve) {
386
  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
387

388
389
  MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
          ("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext.ContextId()));
390

391
392
  // Immediately resolve the promise, acknowledging the request.
  aResolve(true);
393

394
395
396
397
  if (!aLayersId.IsValid()) {
    return IPC_FAIL(this, "Received an invalid LayersId");
  }

398
399
400
401
402
403
404
405
  // Get a BrowsingContext if we're not null or discarded. We don't want to
  // early-return before we connect the BrowserBridgeChild, as otherwise we'll
  // never break the channel in the parent.
  RefPtr<dom::BrowsingContext> frameContext;
  if (!aFrameContext.IsDiscarded()) {
    frameContext = aFrameContext.get();
  }

406
407
408
  // Immediately construct the BrowserBridgeChild so we can destroy it cleanly
  // if the process switch fails.
  RefPtr<BrowserBridgeChild> bridge =
409
      new BrowserBridgeChild(frameContext, aTabId, aLayersId);
410
411
412
413
414
415
  RefPtr<BrowserChild> manager = GetBrowserChild();
  if (NS_WARN_IF(
          !manager->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge))) {
    return IPC_OK();
  }

416
417
418
419
420
421
422
  // Immediately tear down the actor if we don't have a valid FrameContext.
  if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
    BrowserBridgeChild::Send__delete__(bridge);
    return IPC_OK();
  }

  RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
423
  if (NS_WARN_IF(!embedderElt)) {
424
425
    BrowserBridgeChild::Send__delete__(bridge);
    return IPC_OK();
426
  }
427

428
  if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
429
430
    BrowserBridgeChild::Send__delete__(bridge);
    return IPC_OK();
431
  }
432

433
434
  RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
  MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
435

436
  // Trgger a process switch into the specified process.
437
438
439
440
441
442
443
  IgnoredErrorResult rv;
  flo->ChangeRemotenessWithBridge(bridge, rv);
  if (NS_WARN_IF(rv.Failed())) {
    BrowserBridgeChild::Send__delete__(bridge);
    return IPC_OK();
  }

444
445
446
  return IPC_OK();
}

447
448
mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot(
    const Maybe<IntRect>& aRect, const float& aScale,
449
450
    const nscolor& aBackgroundColor, const uint32_t& aFlags,
    DrawSnapshotResolver&& aResolve) {
451
452
453
454
455
456
  nsCOMPtr<nsIDocShell> docShell = BrowsingContext()->GetDocShell();
  if (!docShell) {
    aResolve(gfx::PaintFragment{});
    return IPC_OK();
  }

457
458
  aResolve(gfx::PaintFragment::Record(docShell, aRect, aScale, aBackgroundColor,
                                      (gfx::CrossProcessPaintFlags)aFlags));
459
460
461
  return IPC_OK();
}

462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
mozilla::ipc::IPCResult WindowGlobalChild::RecvGetSecurityInfo(
    GetSecurityInfoResolver&& aResolve) {
  Maybe<nsCString> result;

  if (nsCOMPtr<Document> doc = mWindowGlobal->GetDoc()) {
    nsCOMPtr<nsISupports> secInfo;
    nsresult rv = NS_OK;

    // First check if there's a failed channel, in case of a certificate
    // error.
    if (nsIChannel* failedChannel = doc->GetFailedChannel()) {
      rv = failedChannel->GetSecurityInfo(getter_AddRefs(secInfo));
    } else {
      // When there's no failed channel we should have a regular
      // security info on the document. In some cases there's no
      // security info at all, i.e. on HTTP sites.
      secInfo = doc->GetSecurityInfo();
    }

    if (NS_SUCCEEDED(rv) && secInfo) {
      nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfo);
      result.emplace();
      NS_SerializeToString(secInfoSer, result.ref());
    }
  }

  aResolve(result);
  return IPC_OK();
}

492
493
494
495
mozilla::ipc::IPCResult WindowGlobalChild::RecvSaveStorageAccessGranted() {
  nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal();
  if (inner) {
    inner->SaveStorageAccessGranted();
496
497
  }

498
499
500
501
502
503
  nsCOMPtr<nsPIDOMWindowOuter> outer =
      nsPIDOMWindowOuter::GetFromCurrentInner(inner);
  if (outer) {
    nsGlobalWindowOuter::Cast(outer)->SetHasStorageAccess(true);
  }

504
505
506
  return IPC_OK();
}

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
mozilla::ipc::IPCResult WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
    const nsString& aViolationEventJSON) {
  nsGlobalWindowInner* window = GetWindowGlobal();
  if (!window) {
    return IPC_OK();
  }

  Document* doc = window->GetDocument();
  if (!doc) {
    return IPC_OK();
  }

  SecurityPolicyViolationEventInit violationEvent;
  if (!violationEvent.Init(aViolationEventJSON)) {
    return IPC_OK();
  }

  RefPtr<Event> event = SecurityPolicyViolationEvent::Constructor(
      doc, NS_LITERAL_STRING("securitypolicyviolation"), violationEvent);
  event->SetTrusted(true);
  doc->DispatchEvent(*event, IgnoreErrors());
  return IPC_OK();
}

531
532
533
IPCResult WindowGlobalChild::RecvRawMessage(const JSActorMessageMeta& aMeta,
                                            const ClonedMessageData& aData,
                                            const ClonedMessageData& aStack) {
534
535
  StructuredCloneData data;
  data.BorrowFromClonedMessageDataForChild(aData);
536
537
538
  StructuredCloneData stack;
  stack.BorrowFromClonedMessageDataForChild(aStack);
  ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
539
540
541
  return IPC_OK();
}

542
void WindowGlobalChild::ReceiveRawMessage(const JSActorMessageMeta& aMeta,
543
544
                                          StructuredCloneData&& aData,
                                          StructuredCloneData&& aStack) {
545
546
547
  RefPtr<JSWindowActorChild> actor =
      GetActor(aMeta.actorName(), IgnoreErrors());
  if (actor) {
548
    actor->ReceiveRawMessage(aMeta, std::move(aData), std::move(aStack));
549
550
551
  }
}

552
void WindowGlobalChild::SetDocumentURI(nsIURI* aDocumentURI) {
553
554
555
556
557
#ifdef MOZ_GECKO_PROFILER
  // Registers a DOM Window with the profiler. It re-registers the same Inner
  // Window ID with different URIs because when a Browsing context is first
  // loaded, the first url loaded in it will be about:blank. This call keeps the
  // first non-about:blank registration of window and discards the previous one.
558
  uint64_t embedderInnerWindowID = 0;
559
560
  if (BrowsingContext()->GetParent()) {
    embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
561
  }
562
  profiler_register_page(BrowsingContext()->Id(), InnerWindowId(),
563
                         aDocumentURI->GetSpecOrDefault(),
564
                         embedderInnerWindowID);
565
#endif
566
567
  mDocumentURI = aDocumentURI;
  SendUpdateDocumentURI(aDocumentURI);
568
569
}

570
571
572
573
574
575
576
void WindowGlobalChild::SetDocumentPrincipal(
    nsIPrincipal* aNewDocumentPrincipal) {
  MOZ_ASSERT(mDocumentPrincipal->Equals(aNewDocumentPrincipal));
  mDocumentPrincipal = aNewDocumentPrincipal;
  SendUpdateDocumentPrincipal(aNewDocumentPrincipal);
}

577
578
579
580
581
582
583
584
const nsAString& WindowGlobalChild::GetRemoteType() {
  if (XRE_IsContentProcess()) {
    return ContentChild::GetSingleton()->GetRemoteType();
  }

  return VoidString();
}

585
already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor(
586
    const nsACString& aName, ErrorResult& aRv) {
587
  if (!CanSend()) {
588
589
590
591
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return nullptr;
  }

592
593
594
595
596
  // Check if this actor has already been created, and return it if it has.
  if (mWindowActors.Contains(aName)) {
    return do_AddRef(mWindowActors.GetWeak(aName));
  }

597
  // Otherwise, we want to create a new instance of this actor.
598
  JS::RootedObject obj(RootingCx());
599
  ConstructActor(aName, &obj, aRv);
600
601
602
603
604
605
606
607
608
609
  if (aRv.Failed()) {
    return nullptr;
  }

  // Unwrap our actor to a JSWindowActorChild object.
  RefPtr<JSWindowActorChild> actor;
  if (NS_FAILED(UNWRAP_OBJECT(JSWindowActorChild, &obj, actor))) {
    return nullptr;
  }

610
  MOZ_RELEASE_ASSERT(!actor->GetManager(),
611
                     "mManager was already initialized once!");
612
  actor->Init(aName, this);
613
  mWindowActors.Put(aName, RefPtr{actor});
614
615
616
  return actor.forget();
}

617
void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
618
619
620
  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
             "Destroying WindowGlobalChild can run script");

621
  gWindowGlobalChildById->Remove(InnerWindowId());
622

623
#ifdef MOZ_GECKO_PROFILER
624
  profiler_unregister_page(InnerWindowId());
625
626
#endif

627
  // Destroy our JSActors, and reject any pending queries.
628
  nsRefPtrHashtable<nsCStringHashKey, JSWindowActorChild> windowActors;
629
630
631
  mWindowActors.SwapElements(windowActors);
  for (auto iter = windowActors.Iter(); !iter.Done(); iter.Next()) {
    iter.Data()->RejectPendingQueries();
632
    iter.Data()->AfterDestroy();
633
  }
634
  windowActors.Clear();
635
636
}

637
WindowGlobalChild::~WindowGlobalChild() {
638
  MOZ_ASSERT(!gWindowGlobalChildById ||
639
             !gWindowGlobalChildById->Contains(InnerWindowId()));
640
  MOZ_ASSERT(!mWindowActors.Count());
641
642
}

643
644
JSObject* WindowGlobalChild::WrapObject(JSContext* aCx,
                                        JS::Handle<JSObject*> aGivenProto) {
645
646
647
  return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto);
}

648
nsISupports* WindowGlobalChild::GetParentObject() {
649
650
651
  return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}

652
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalChild, mWindowGlobal,
653
                                      mWindowActors)
654
655

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
656
657
658
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
659

660
661
NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild)
662

663
}  // namespace dom
664
}  // namespace mozilla