Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/base/DocManager.cpp
Line
Count
Source (jump to first uncovered line)
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 "DocManager.h"
8
9
#include "ApplicationAccessible.h"
10
#include "ARIAMap.h"
11
#include "DocAccessible-inl.h"
12
#include "DocAccessibleChild.h"
13
#include "DocAccessibleParent.h"
14
#include "nsAccessibilityService.h"
15
#include "Platform.h"
16
#include "RootAccessibleWrap.h"
17
#include "xpcAccessibleDocument.h"
18
19
#ifdef A11Y_LOG
20
#include "Logging.h"
21
#endif
22
23
#include "mozilla/EventListenerManager.h"
24
#include "mozilla/dom/Event.h" // for Event
25
#include "nsContentUtils.h"
26
#include "nsCURILoader.h"
27
#include "nsDocShellLoadTypes.h"
28
#include "nsIChannel.h"
29
#include "nsIInterfaceRequestorUtils.h"
30
#include "nsIWebNavigation.h"
31
#include "nsServiceManagerUtils.h"
32
#include "nsIWebProgress.h"
33
#include "nsCoreUtils.h"
34
#include "nsXULAppAPI.h"
35
#include "mozilla/dom/TabChild.h"
36
37
using namespace mozilla;
38
using namespace mozilla::a11y;
39
using namespace mozilla::dom;
40
41
StaticAutoPtr<nsTArray<DocAccessibleParent*>> DocManager::sRemoteDocuments;
42
nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>*
43
DocManager::sRemoteXPCDocumentCache = nullptr;
44
45
////////////////////////////////////////////////////////////////////////////////
46
// DocManager
47
////////////////////////////////////////////////////////////////////////////////
48
49
DocManager::DocManager()
50
  : mDocAccessibleCache(2), mXPCDocumentCache(0)
51
0
{
52
0
}
53
54
////////////////////////////////////////////////////////////////////////////////
55
// DocManager public
56
57
DocAccessible*
58
DocManager::GetDocAccessible(nsIDocument* aDocument)
59
0
{
60
0
  if (!aDocument)
61
0
    return nullptr;
62
0
63
0
  DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
64
0
  if (docAcc)
65
0
    return docAcc;
66
0
67
0
  return CreateDocOrRootAccessible(aDocument);
68
0
}
69
70
Accessible*
71
DocManager::FindAccessibleInCache(nsINode* aNode) const
72
0
{
73
0
  for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) {
74
0
    DocAccessible* docAccessible = iter.UserData();
75
0
    NS_ASSERTION(docAccessible,
76
0
                 "No doc accessible for the object in doc accessible cache!");
77
0
78
0
    if (docAccessible) {
79
0
      Accessible* accessible = docAccessible->GetAccessible(aNode);
80
0
      if (accessible) {
81
0
        return accessible;
82
0
      }
83
0
    }
84
0
  }
85
0
  return nullptr;
86
0
}
87
88
void
89
DocManager::RemoveFromXPCDocumentCache(DocAccessible* aDocument)
90
0
{
91
0
  xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
92
0
  if (xpcDoc) {
93
0
    xpcDoc->Shutdown();
94
0
    mXPCDocumentCache.Remove(aDocument);
95
0
96
0
    if (!HasXPCDocuments()) {
97
0
      MaybeShutdownAccService(nsAccessibilityService::eXPCOM);
98
0
    }
99
0
  }
100
0
}
101
102
void
103
DocManager::NotifyOfDocumentShutdown(DocAccessible* aDocument,
104
                                     nsIDocument* aDOMDocument)
105
0
{
106
0
  // We need to remove listeners in both cases, when document is being shutdown
107
0
  // or when accessibility service is being shut down as well.
108
0
  RemoveListeners(aDOMDocument);
109
0
110
0
  // Document will already be removed when accessibility service is shutting
111
0
  // down so we do not need to remove it twice.
112
0
  if (nsAccessibilityService::IsShutdown()) {
113
0
    return;
114
0
  }
115
0
116
0
  RemoveFromXPCDocumentCache(aDocument);
117
0
  mDocAccessibleCache.Remove(aDOMDocument);
118
0
}
119
120
void
121
DocManager::RemoveFromRemoteXPCDocumentCache(DocAccessibleParent* aDoc)
122
0
{
123
0
  xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
124
0
  if (doc) {
125
0
    doc->Shutdown();
126
0
    sRemoteXPCDocumentCache->Remove(aDoc);
127
0
  }
128
0
129
0
  if (sRemoteXPCDocumentCache && sRemoteXPCDocumentCache->Count() == 0) {
130
0
    MaybeShutdownAccService(nsAccessibilityService::eXPCOM);
131
0
  }
132
0
}
133
134
void
135
DocManager::NotifyOfRemoteDocShutdown(DocAccessibleParent* aDoc)
136
0
{
137
0
  RemoveFromRemoteXPCDocumentCache(aDoc);
138
0
}
139
140
xpcAccessibleDocument*
141
DocManager::GetXPCDocument(DocAccessible* aDocument)
142
0
{
143
0
  if (!aDocument)
144
0
    return nullptr;
145
0
146
0
  xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
147
0
  if (!xpcDoc) {
148
0
    xpcDoc = new xpcAccessibleDocument(aDocument);
149
0
    mXPCDocumentCache.Put(aDocument, xpcDoc);
150
0
  }
151
0
  return xpcDoc;
152
0
}
153
154
xpcAccessibleDocument*
155
DocManager::GetXPCDocument(DocAccessibleParent* aDoc)
156
0
{
157
0
  xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
158
0
  if (doc) {
159
0
    return doc;
160
0
  }
161
0
162
0
  if (!sRemoteXPCDocumentCache) {
163
0
    sRemoteXPCDocumentCache =
164
0
      new nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>;
165
0
  }
166
0
167
0
  doc =
168
0
    new xpcAccessibleDocument(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
169
0
  sRemoteXPCDocumentCache->Put(aDoc, doc);
170
0
171
0
  return doc;
172
0
}
173
174
#ifdef DEBUG
175
bool
176
DocManager::IsProcessingRefreshDriverNotification() const
177
{
178
  for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) {
179
    DocAccessible* docAccessible = iter.UserData();
180
    NS_ASSERTION(docAccessible,
181
                 "No doc accessible for the object in doc accessible cache!");
182
183
    if (docAccessible && docAccessible->mNotificationController &&
184
        docAccessible->mNotificationController->IsUpdating()) {
185
      return true;
186
    }
187
  }
188
  return false;
189
}
190
#endif
191
192
193
////////////////////////////////////////////////////////////////////////////////
194
// DocManager protected
195
196
bool
197
DocManager::Init()
198
0
{
199
0
  nsCOMPtr<nsIWebProgress> progress =
200
0
    do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
201
0
202
0
  if (!progress)
203
0
    return false;
204
0
205
0
  progress->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
206
0
                                nsIWebProgress::NOTIFY_STATE_DOCUMENT);
207
0
208
0
  return true;
209
0
}
210
211
void
212
DocManager::Shutdown()
213
0
{
214
0
  nsCOMPtr<nsIWebProgress> progress =
215
0
    do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
216
0
217
0
  if (progress)
218
0
    progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this));
219
0
220
0
  ClearDocCache();
221
0
}
222
223
////////////////////////////////////////////////////////////////////////////////
224
// nsISupports
225
226
NS_IMPL_ISUPPORTS(DocManager,
227
                  nsIWebProgressListener,
228
                  nsIDOMEventListener,
229
                  nsISupportsWeakReference)
230
231
////////////////////////////////////////////////////////////////////////////////
232
// nsIWebProgressListener
233
234
NS_IMETHODIMP
235
DocManager::OnStateChange(nsIWebProgress* aWebProgress,
236
                          nsIRequest* aRequest, uint32_t aStateFlags,
237
                          nsresult aStatus)
238
0
{
239
0
  NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded");
240
0
241
0
  if (nsAccessibilityService::IsShutdown() || !aWebProgress ||
242
0
      (aStateFlags & (STATE_START | STATE_STOP)) == 0)
243
0
    return NS_OK;
244
0
245
0
  nsCOMPtr<mozIDOMWindowProxy> DOMWindow;
246
0
  aWebProgress->GetDOMWindow(getter_AddRefs(DOMWindow));
247
0
  NS_ENSURE_STATE(DOMWindow);
248
0
249
0
  nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(DOMWindow);
250
0
  MOZ_ASSERT(piWindow);
251
0
252
0
  nsCOMPtr<nsIDocument> document = piWindow->GetDoc();
253
0
  NS_ENSURE_STATE(document);
254
0
255
0
  // Document was loaded.
256
0
  if (aStateFlags & STATE_STOP) {
257
0
#ifdef A11Y_LOG
258
0
    if (logging::IsEnabled(logging::eDocLoad))
259
0
      logging::DocLoad("document loaded", aWebProgress, aRequest, aStateFlags);
260
0
#endif
261
0
262
0
    // Figure out an event type to notify the document has been loaded.
263
0
    uint32_t eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED;
264
0
265
0
    // Some XUL documents get start state and then stop state with failure
266
0
    // status when everything is ok. Fire document load complete event in this
267
0
    // case.
268
0
    if (NS_SUCCEEDED(aStatus) || !nsCoreUtils::IsContentDocument(document))
269
0
      eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE;
270
0
271
0
    // If end consumer has been retargeted for loaded content then do not fire
272
0
    // any event because it means no new document has been loaded, for example,
273
0
    // it happens when user clicks on file link.
274
0
    if (aRequest) {
275
0
      uint32_t loadFlags = 0;
276
0
      aRequest->GetLoadFlags(&loadFlags);
277
0
      if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
278
0
        eventType = 0;
279
0
    }
280
0
281
0
    HandleDOMDocumentLoad(document, eventType);
282
0
    return NS_OK;
283
0
  }
284
0
285
0
  // Document loading was started.
286
0
#ifdef A11Y_LOG
287
0
  if (logging::IsEnabled(logging::eDocLoad))
288
0
    logging::DocLoad("start document loading", aWebProgress, aRequest, aStateFlags);
289
0
#endif
290
0
291
0
  DocAccessible* docAcc = GetExistingDocAccessible(document);
292
0
  if (!docAcc)
293
0
    return NS_OK;
294
0
295
0
  nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
296
0
  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
297
0
  NS_ENSURE_STATE(docShell);
298
0
299
0
  bool isReloading = false;
300
0
  uint32_t loadType;
301
0
  docShell->GetLoadType(&loadType);
302
0
  if (loadType == LOAD_RELOAD_NORMAL ||
303
0
      loadType == LOAD_RELOAD_BYPASS_CACHE ||
304
0
      loadType == LOAD_RELOAD_BYPASS_PROXY ||
305
0
      loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
306
0
      loadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
307
0
    isReloading = true;
308
0
  }
309
0
310
0
  docAcc->NotifyOfLoading(isReloading);
311
0
  return NS_OK;
312
0
}
313
314
NS_IMETHODIMP
315
DocManager::OnProgressChange(nsIWebProgress* aWebProgress,
316
                             nsIRequest* aRequest,
317
                             int32_t aCurSelfProgress,
318
                             int32_t aMaxSelfProgress,
319
                             int32_t aCurTotalProgress,
320
                             int32_t aMaxTotalProgress)
321
0
{
322
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
323
0
  return NS_OK;
324
0
}
325
326
NS_IMETHODIMP
327
DocManager::OnLocationChange(nsIWebProgress* aWebProgress,
328
                             nsIRequest* aRequest, nsIURI* aLocation,
329
                             uint32_t aFlags)
330
0
{
331
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
332
0
  return NS_OK;
333
0
}
334
335
NS_IMETHODIMP
336
DocManager::OnStatusChange(nsIWebProgress* aWebProgress,
337
                           nsIRequest* aRequest, nsresult aStatus,
338
                           const char16_t* aMessage)
339
0
{
340
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
341
0
  return NS_OK;
342
0
}
343
344
NS_IMETHODIMP
345
DocManager::OnSecurityChange(nsIWebProgress* aWebProgress,
346
                             nsIRequest* aRequest,
347
                             uint32_t aState)
348
0
{
349
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
350
0
  return NS_OK;
351
0
}
352
353
////////////////////////////////////////////////////////////////////////////////
354
// nsIDOMEventListener
355
356
NS_IMETHODIMP
357
DocManager::HandleEvent(Event* aEvent)
358
0
{
359
0
  nsAutoString type;
360
0
  aEvent->GetType(type);
361
0
362
0
  nsCOMPtr<nsIDocument> document = do_QueryInterface(aEvent->GetTarget());
363
0
  NS_ASSERTION(document, "pagehide or DOMContentLoaded for non document!");
364
0
  if (!document)
365
0
    return NS_OK;
366
0
367
0
  if (type.EqualsLiteral("pagehide")) {
368
0
    // 'pagehide' event is registered on every DOM document we create an
369
0
    // accessible for, process the event for the target. This document
370
0
    // accessible and all its sub document accessible are shutdown as result of
371
0
    // processing.
372
0
373
0
#ifdef A11Y_LOG
374
0
    if (logging::IsEnabled(logging::eDocDestroy))
375
0
      logging::DocDestroy("received 'pagehide' event", document);
376
0
#endif
377
0
378
0
    // Shutdown this one and sub document accessibles.
379
0
380
0
    // We're allowed to not remove listeners when accessible document is
381
0
    // shutdown since we don't keep strong reference on chrome event target and
382
0
    // listeners are removed automatically when chrome event target goes away.
383
0
    DocAccessible* docAccessible = GetExistingDocAccessible(document);
384
0
    if (docAccessible)
385
0
      docAccessible->Shutdown();
386
0
387
0
    return NS_OK;
388
0
  }
389
0
390
0
  // XXX: handle error pages loading separately since they get neither
391
0
  // webprogress notifications nor 'pageshow' event.
392
0
  if (type.EqualsLiteral("DOMContentLoaded") &&
393
0
      nsCoreUtils::IsErrorPage(document)) {
394
0
#ifdef A11Y_LOG
395
0
    if (logging::IsEnabled(logging::eDocLoad))
396
0
      logging::DocLoad("handled 'DOMContentLoaded' event", document);
397
0
#endif
398
0
399
0
    HandleDOMDocumentLoad(document,
400
0
                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
401
0
  }
402
0
403
0
  return NS_OK;
404
0
}
405
406
////////////////////////////////////////////////////////////////////////////////
407
// DocManager private
408
409
void
410
DocManager::HandleDOMDocumentLoad(nsIDocument* aDocument,
411
                                  uint32_t aLoadEventType)
412
0
{
413
0
  // Document accessible can be created before we were notified the DOM document
414
0
  // was loaded completely. However if it's not created yet then create it.
415
0
  DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
416
0
  if (!docAcc) {
417
0
    docAcc = CreateDocOrRootAccessible(aDocument);
418
0
    if (!docAcc)
419
0
      return;
420
0
  }
421
0
422
0
  docAcc->NotifyOfLoad(aLoadEventType);
423
0
}
424
425
void
426
DocManager::AddListeners(nsIDocument* aDocument,
427
                         bool aAddDOMContentLoadedListener)
428
0
{
429
0
  nsPIDOMWindowOuter* window = aDocument->GetWindow();
430
0
  EventTarget* target = window->GetChromeEventHandler();
431
0
  EventListenerManager* elm = target->GetOrCreateListenerManager();
432
0
  elm->AddEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
433
0
                              TrustedEventsAtCapture());
434
0
435
0
#ifdef A11Y_LOG
436
0
  if (logging::IsEnabled(logging::eDocCreate))
437
0
    logging::Text("added 'pagehide' listener");
438
0
#endif
439
0
440
0
  if (aAddDOMContentLoadedListener) {
441
0
    elm->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
442
0
                                TrustedEventsAtCapture());
443
0
#ifdef A11Y_LOG
444
0
    if (logging::IsEnabled(logging::eDocCreate))
445
0
      logging::Text("added 'DOMContentLoaded' listener");
446
0
#endif
447
0
  }
448
0
}
449
450
void
451
DocManager::RemoveListeners(nsIDocument* aDocument)
452
0
{
453
0
  nsPIDOMWindowOuter* window = aDocument->GetWindow();
454
0
  if (!window)
455
0
    return;
456
0
457
0
  EventTarget* target = window->GetChromeEventHandler();
458
0
  if (!target)
459
0
    return;
460
0
461
0
  EventListenerManager* elm = target->GetOrCreateListenerManager();
462
0
  elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
463
0
                                 TrustedEventsAtCapture());
464
0
465
0
  elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
466
0
                                 TrustedEventsAtCapture());
467
0
}
468
469
DocAccessible*
470
DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
471
0
{
472
0
  // Ignore hidden documents, resource documents, static clone
473
0
  // (printing) documents and documents without a docshell.
474
0
  if (!aDocument->IsVisibleConsideringAncestors() ||
475
0
      aDocument->IsResourceDoc() || aDocument->IsStaticDocument() ||
476
0
      !aDocument->IsActive()) {
477
0
    return nullptr;
478
0
  }
479
0
480
0
  nsIDocShell* docShell = aDocument->GetDocShell();
481
0
  if (!docShell || docShell->IsInvisible()) {
482
0
    return nullptr;
483
0
  }
484
0
485
0
  nsIWidget* widget = nsContentUtils::WidgetForDocument(aDocument);
486
0
  if (!widget || widget->WindowType() == eWindowType_invisible) {
487
0
    return nullptr;
488
0
  }
489
0
490
0
  // Ignore documents without presshell and not having root frame.
491
0
  nsIPresShell* presShell = aDocument->GetShell();
492
0
  if (!presShell || presShell->IsDestroying())
493
0
    return nullptr;
494
0
495
0
  bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument);
496
0
497
0
  DocAccessible* parentDocAcc = nullptr;
498
0
  if (!isRootDoc) {
499
0
    // XXXaaronl: ideally we would traverse the presshell chain. Since there's
500
0
    // no easy way to do that, we cheat and use the document hierarchy.
501
0
    parentDocAcc = GetDocAccessible(aDocument->GetParentDocument());
502
0
    NS_ASSERTION(parentDocAcc,
503
0
                 "Can't create an accessible for the document!");
504
0
    if (!parentDocAcc)
505
0
      return nullptr;
506
0
  }
507
0
508
0
  // We only create root accessibles for the true root, otherwise create a
509
0
  // doc accessible.
510
0
  RefPtr<DocAccessible> docAcc = isRootDoc ?
511
0
    new RootAccessibleWrap(aDocument, presShell) :
512
0
    new DocAccessibleWrap(aDocument, presShell);
513
0
514
0
  // Cache the document accessible into document cache.
515
0
  mDocAccessibleCache.Put(aDocument, docAcc);
516
0
517
0
  // Initialize the document accessible.
518
0
  docAcc->Init();
519
0
520
0
  // Bind the document to the tree.
521
0
  if (isRootDoc) {
522
0
    if (!ApplicationAcc()->AppendChild(docAcc)) {
523
0
      docAcc->Shutdown();
524
0
      return nullptr;
525
0
    }
526
0
527
0
    // Fire reorder event to notify new accessible document has been attached to
528
0
    // the tree. The reorder event is delivered after the document tree is
529
0
    // constructed because event processing and tree construction are done by
530
0
    // the same document.
531
0
    // Note: don't use AccReorderEvent to avoid coalsecense and special reorder
532
0
    // events processing.
533
0
    docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
534
0
                             ApplicationAcc());
535
0
536
0
  } else {
537
0
    parentDocAcc->BindChildDocument(docAcc);
538
0
  }
539
0
540
0
#ifdef A11Y_LOG
541
0
  if (logging::IsEnabled(logging::eDocCreate)) {
542
0
    logging::DocCreate("document creation finished", aDocument);
543
0
    logging::Stack();
544
0
  }
545
0
#endif
546
0
547
0
  AddListeners(aDocument, isRootDoc);
548
0
  return docAcc;
549
0
}
550
551
////////////////////////////////////////////////////////////////////////////////
552
// DocManager static
553
554
void
555
DocManager::ClearDocCache()
556
0
{
557
0
  while (mDocAccessibleCache.Count() > 0) {
558
0
    auto iter = mDocAccessibleCache.Iter();
559
0
    MOZ_ASSERT(!iter.Done());
560
0
    DocAccessible* docAcc = iter.UserData();
561
0
    NS_ASSERTION(docAcc,
562
0
                 "No doc accessible for the object in doc accessible cache!");
563
0
    if (docAcc) {
564
0
      docAcc->Shutdown();
565
0
    }
566
0
567
0
    iter.Remove();
568
0
  }
569
0
570
0
  // Ensure that all xpcom accessible documents are shut down as well.
571
0
  while (mXPCDocumentCache.Count() > 0) {
572
0
    auto iter = mXPCDocumentCache.Iter();
573
0
    MOZ_ASSERT(!iter.Done());
574
0
    xpcAccessibleDocument* xpcDoc = iter.UserData();
575
0
    NS_ASSERTION(xpcDoc, "No xpc doc for the object in xpc doc cache!");
576
0
577
0
    if (xpcDoc) {
578
0
      xpcDoc->Shutdown();
579
0
    }
580
0
581
0
    iter.Remove();
582
0
   }
583
0
}
584
585
void
586
DocManager::RemoteDocAdded(DocAccessibleParent* aDoc)
587
0
{
588
0
  if (!sRemoteDocuments) {
589
0
    sRemoteDocuments = new nsTArray<DocAccessibleParent*>;
590
0
    ClearOnShutdown(&sRemoteDocuments);
591
0
  }
592
0
593
0
  MOZ_ASSERT(!sRemoteDocuments->Contains(aDoc),
594
0
      "How did we already have the doc!");
595
0
  sRemoteDocuments->AppendElement(aDoc);
596
0
  ProxyCreated(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
597
0
}