Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsDocument.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
/*
8
 * Base class for all our document implementations.
9
 */
10
11
#include "AudioChannelService.h"
12
#include "nsDocument.h"
13
#include "nsIDocumentInlines.h"
14
#include "mozilla/AnimationComparator.h"
15
#include "mozilla/AntiTrackingCommon.h"
16
#include "mozilla/ArrayUtils.h"
17
#include "mozilla/AutoRestore.h"
18
#include "mozilla/BinarySearch.h"
19
#include "mozilla/CSSEnabledState.h"
20
#include "mozilla/DebugOnly.h"
21
#include "mozilla/EffectSet.h"
22
#include "mozilla/EnumSet.h"
23
#include "mozilla/IntegerRange.h"
24
#include "mozilla/MemoryReporting.h"
25
#include "mozilla/Likely.h"
26
#include "mozilla/PresShell.h"
27
#include "mozilla/StaticPrefs.h"
28
#include "mozilla/URLExtraData.h"
29
#include <algorithm>
30
31
#include "mozilla/Logging.h"
32
#include "plstr.h"
33
#include "mozilla/Sprintf.h"
34
35
#include "mozilla/Telemetry.h"
36
#include "nsIInterfaceRequestor.h"
37
#include "nsIInterfaceRequestorUtils.h"
38
#include "nsILoadContext.h"
39
#include "nsITextControlFrame.h"
40
#include "nsNumberControlFrame.h"
41
#include "nsUnicharUtils.h"
42
#include "nsContentList.h"
43
#include "nsCSSPseudoElements.h"
44
#include "nsIObserver.h"
45
#include "nsIBaseWindow.h"
46
#include "mozilla/css/Loader.h"
47
#include "mozilla/css/ImageLoader.h"
48
#include "nsDocShell.h"
49
#include "nsDocShellLoadTypes.h"
50
#include "nsIDocShellTreeItem.h"
51
#include "nsCOMArray.h"
52
#include "nsQueryObject.h"
53
#include "mozilla/Services.h"
54
#include "nsScreen.h"
55
#include "ChildIterator.h"
56
57
#include "mozilla/AsyncEventDispatcher.h"
58
#include "mozilla/BasicEvents.h"
59
#include "mozilla/EventListenerManager.h"
60
#include "mozilla/EventStateManager.h"
61
#include "mozilla/FullscreenChange.h"
62
63
#include "mozilla/dom/Attr.h"
64
#include "mozilla/dom/BindingDeclarations.h"
65
#include "mozilla/dom/Element.h"
66
#include "mozilla/dom/Event.h"
67
#include "mozilla/dom/FramingChecker.h"
68
#include "mozilla/dom/HTMLSharedElement.h"
69
#include "mozilla/dom/SVGUseElement.h"
70
#include "nsGenericHTMLElement.h"
71
#include "mozilla/dom/CDATASection.h"
72
#include "mozilla/dom/ProcessingInstruction.h"
73
#include "nsDOMString.h"
74
#include "nsNodeUtils.h"
75
#include "nsLayoutUtils.h" // for GetFrameForPoint
76
#include "nsIFrame.h"
77
#include "nsITabChild.h"
78
79
#include "nsRange.h"
80
#include "mozilla/dom/DocumentType.h"
81
#include "mozilla/dom/NodeIterator.h"
82
#include "mozilla/dom/Promise.h"
83
#include "mozilla/dom/PromiseNativeHandler.h"
84
#include "mozilla/dom/TreeWalker.h"
85
86
#include "nsIServiceManager.h"
87
#include "mozilla/dom/ServiceWorkerManager.h"
88
#include "imgLoader.h"
89
90
#include "nsAboutProtocolUtils.h"
91
#include "nsCanvasFrame.h"
92
#include "nsContentCID.h"
93
#include "nsError.h"
94
#include "nsPresContext.h"
95
#include "nsThreadUtils.h"
96
#include "nsNodeInfoManager.h"
97
#include "nsIFileChannel.h"
98
#include "nsIMultiPartChannel.h"
99
#include "nsIRefreshURI.h"
100
#include "nsIWebNavigation.h"
101
#include "nsIScriptError.h"
102
#include "nsISimpleEnumerator.h"
103
#include "nsIRequestContext.h"
104
#include "nsStyleSheetService.h"
105
106
#include "nsNetUtil.h"     // for NS_NewURI
107
#include "nsIInputStreamChannel.h"
108
#include "nsIAuthPrompt.h"
109
#include "nsIAuthPrompt2.h"
110
111
#include "nsIScriptSecurityManager.h"
112
#include "nsIPermissionManager.h"
113
#include "nsIPrincipal.h"
114
#include "ExpandedPrincipal.h"
115
#include "mozilla/NullPrincipal.h"
116
117
#include "nsIDOMWindow.h"
118
#include "nsPIDOMWindow.h"
119
#include "nsFocusManager.h"
120
#include "nsICookieService.h"
121
122
#include "nsBidiUtils.h"
123
124
#include "nsContentCreatorFunctions.h"
125
126
#include "nsIScriptContext.h"
127
#include "nsBindingManager.h"
128
#include "nsHTMLDocument.h"
129
#include "nsIRequest.h"
130
#include "mozilla/dom/BlobURLProtocolHandler.h"
131
132
#include "nsCharsetSource.h"
133
#include "nsIParser.h"
134
#include "nsIContentSink.h"
135
136
#include "mozilla/EventDispatcher.h"
137
#include "mozilla/EventStates.h"
138
#include "mozilla/InternalMutationEvent.h"
139
#include "nsDOMCID.h"
140
141
#include "jsapi.h"
142
#include "nsIXPConnect.h"
143
#include "xpcpublic.h"
144
#include "nsCCUncollectableMarker.h"
145
#include "nsIContentPolicy.h"
146
#include "nsContentPolicyUtils.h"
147
#include "nsICategoryManager.h"
148
#include "nsIDocumentLoaderFactory.h"
149
#include "nsIDocumentLoader.h"
150
#include "nsIContentViewer.h"
151
#include "nsIXMLContentSink.h"
152
#include "nsIPrompt.h"
153
#include "nsIPropertyBag2.h"
154
#include "mozilla/dom/PageTransitionEvent.h"
155
#include "mozilla/dom/StyleRuleChangeEvent.h"
156
#include "mozilla/dom/StyleSheetChangeEvent.h"
157
#include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
158
#include "nsJSUtils.h"
159
#include "nsFrameLoader.h"
160
#include "nsEscape.h"
161
#include "nsObjectLoadingContent.h"
162
#include "nsHtml5TreeOpExecutor.h"
163
#include "mozilla/dom/HTMLFormElement.h"
164
#include "mozilla/dom/HTMLLinkElement.h"
165
#include "mozilla/dom/HTMLMediaElement.h"
166
#include "mozilla/dom/HTMLIFrameElement.h"
167
#include "mozilla/dom/HTMLImageElement.h"
168
#include "mozilla/dom/HTMLTextAreaElement.h"
169
#include "mozilla/dom/MediaSource.h"
170
171
#include "mozAutoDocUpdate.h"
172
#include "nsGlobalWindow.h"
173
#include "mozilla/Encoding.h"
174
#include "nsDOMNavigationTiming.h"
175
176
#include "nsSMILAnimationController.h"
177
#include "imgIContainer.h"
178
#include "nsSVGUtils.h"
179
180
#include "nsRefreshDriver.h"
181
182
// FOR CSP (autogenerated by xpidl)
183
#include "nsIContentSecurityPolicy.h"
184
#include "mozilla/dom/nsCSPContext.h"
185
#include "mozilla/dom/nsCSPService.h"
186
#include "mozilla/dom/nsCSPUtils.h"
187
#include "nsHTMLStyleSheet.h"
188
#include "nsHTMLCSSStyleSheet.h"
189
#include "mozilla/dom/DOMImplementation.h"
190
#include "mozilla/dom/ShadowRoot.h"
191
#include "mozilla/dom/Comment.h"
192
#include "nsTextNode.h"
193
#include "mozilla/dom/Link.h"
194
#include "mozilla/dom/HTMLCollectionBinding.h"
195
#include "mozilla/dom/HTMLElementBinding.h"
196
#include "nsXULAppAPI.h"
197
#include "mozilla/dom/Touch.h"
198
#include "mozilla/dom/TouchEvent.h"
199
200
#include "mozilla/Preferences.h"
201
202
#include "imgILoader.h"
203
#include "imgRequestProxy.h"
204
#include "nsWrapperCacheInlines.h"
205
#include "nsSandboxFlags.h"
206
#include "mozilla/dom/AnimatableBinding.h"
207
#include "mozilla/dom/AnonymousContent.h"
208
#include "mozilla/dom/BindingUtils.h"
209
#include "mozilla/dom/ClientInfo.h"
210
#include "mozilla/dom/ClientState.h"
211
#include "mozilla/dom/DocumentFragment.h"
212
#include "mozilla/dom/DocumentL10n.h"
213
#include "mozilla/dom/DocumentTimeline.h"
214
#include "mozilla/dom/Event.h"
215
#include "mozilla/dom/HTMLBodyElement.h"
216
#include "mozilla/dom/HTMLInputElement.h"
217
#include "mozilla/dom/ImageTracker.h"
218
#include "mozilla/dom/MediaQueryList.h"
219
#include "mozilla/dom/NodeFilterBinding.h"
220
#include "mozilla/OwningNonNull.h"
221
#include "mozilla/dom/TabChild.h"
222
#include "mozilla/dom/WebComponentsBinding.h"
223
#include "mozilla/dom/CustomElementRegistryBinding.h"
224
#include "mozilla/dom/CustomElementRegistry.h"
225
#include "mozilla/dom/ServiceWorkerDescriptor.h"
226
#include "mozilla/dom/TimeoutManager.h"
227
#include "mozilla/ExtensionPolicyService.h"
228
#include "nsFrame.h"
229
#include "nsDOMCaretPosition.h"
230
#include "nsViewportInfo.h"
231
#include "mozilla/StaticPtr.h"
232
#include "nsITextControlElement.h"
233
#include "nsIEditor.h"
234
#include "nsIHttpChannelInternal.h"
235
#include "nsISecurityConsoleMessage.h"
236
#include "nsCharSeparatedTokenizer.h"
237
#include "mozilla/dom/XPathEvaluator.h"
238
#include "mozilla/dom/XPathNSResolverBinding.h"
239
#include "mozilla/dom/XPathResult.h"
240
#include "nsIDocumentEncoder.h"
241
#include "nsIDocumentActivity.h"
242
#include "nsIStructuredCloneContainer.h"
243
#include "nsIMutableArray.h"
244
#include "mozilla/dom/DOMStringList.h"
245
#include "nsWindowSizes.h"
246
#include "mozilla/dom/Location.h"
247
#include "mozilla/dom/FontFaceSet.h"
248
#include "gfxPrefs.h"
249
#include "nsISupportsPrimitives.h"
250
#include "mozilla/ServoStyleSet.h"
251
#include "mozilla/StyleSheet.h"
252
#include "mozilla/StyleSheetInlines.h"
253
#include "mozilla/dom/SVGDocument.h"
254
#include "mozilla/dom/SVGSVGElement.h"
255
#include "mozilla/dom/DocGroup.h"
256
#include "mozilla/dom/TabGroup.h"
257
#ifdef MOZ_XUL
258
#include "mozilla/dom/TreeBoxObject.h"
259
#include "nsIXULWindow.h"
260
#include "nsXULCommandDispatcher.h"
261
#include "nsXULPopupManager.h"
262
#include "nsIDocShellTreeOwner.h"
263
#endif
264
#include "nsIPresShellInlines.h"
265
266
#include "mozilla/DocLoadingTimelineMarker.h"
267
268
#include "nsISpeculativeConnect.h"
269
270
#include "mozilla/MediaManager.h"
271
272
#include "nsIURIClassifier.h"
273
#include "nsIURIMutator.h"
274
#include "mozilla/DocumentStyleRootIterator.h"
275
#include "mozilla/PendingFullscreenEvent.h"
276
#include "mozilla/RestyleManager.h"
277
#include "mozilla/ClearOnShutdown.h"
278
#include "nsHTMLTags.h"
279
#include "NodeUbiReporting.h"
280
#include "nsICookieService.h"
281
282
using namespace mozilla;
283
using namespace mozilla::dom;
284
285
typedef nsTArray<Link*> LinkArray;
286
287
static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
288
static LazyLogModule gCspPRLog("CSP");
289
static LazyLogModule gUserInteractionPRLog("UserInteraction");
290
291
static nsresult
292
GetHttpChannelHelper(nsIChannel* aChannel, nsIHttpChannel** aHttpChannel)
293
0
{
294
0
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
295
0
  if (httpChannel) {
296
0
    httpChannel.forget(aHttpChannel);
297
0
    return NS_OK;
298
0
  }
299
0
300
0
  nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
301
0
  if (!multipart) {
302
0
    *aHttpChannel = nullptr;
303
0
    return NS_OK;
304
0
  }
305
0
306
0
  nsCOMPtr<nsIChannel> baseChannel;
307
0
  nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
308
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
309
0
    return rv;
310
0
  }
311
0
312
0
  httpChannel = do_QueryInterface(baseChannel);
313
0
  httpChannel.forget(aHttpChannel);
314
0
315
0
  return NS_OK;
316
0
}
317
318
////////////////////////////////////////////////////////////////////
319
// PrincipalFlashClassifier
320
321
// Classify the flash based on the document principal.
322
// The usage of this class is as follows:
323
//
324
// 1) Call AsyncClassify() as early as possible to asynchronously do
325
//    classification against all the flash blocking related tables
326
//    via nsIURIClassifier.asyncClassifyLocalWithTables.
327
//
328
// 2) At any time you need the classification result, call Result()
329
//    and it is guaranteed to give you the result. Note that you have
330
//    to specify "aIsThirdParty" to the function so please make sure
331
//    you can already correctly decide if the document is third-party.
332
//
333
//    Behind the scenes, the sync classification API
334
//    (nsIURIClassifier.classifyLocalWithTable) may be called as a fallback to
335
//    synchronously get the result if the asyncClassifyLocalWithTables hasn't
336
//    been done yet.
337
//
338
// 3) You can call Result() as many times as you want and only the first time
339
//    it may unfortunately call the blocking sync API. The subsequent call
340
//    will just return the result that came out at the first time.
341
//
342
class PrincipalFlashClassifier final : public nsIURIClassifierCallback
343
{
344
public:
345
  NS_DECL_THREADSAFE_ISUPPORTS
346
  NS_DECL_NSIURICLASSIFIERCALLBACK
347
348
  PrincipalFlashClassifier();
349
350
  // Fire async classification based on the given principal.
351
  void AsyncClassify(nsIPrincipal* aPrincipal);
352
353
  // Would block if the result hasn't come out.
354
  mozilla::dom::FlashClassification ClassifyMaybeSync(nsIPrincipal* aPrincipal,
355
                                                      bool aIsThirdParty);
356
357
private:
358
0
 ~PrincipalFlashClassifier() = default;
359
360
  void Reset();
361
  bool EnsureUriClassifier();
362
  mozilla::dom::FlashClassification CheckIfClassifyNeeded(nsIPrincipal* aPrincipal);
363
  mozilla::dom::FlashClassification Resolve(bool aIsThirdParty);
364
  mozilla::dom::FlashClassification AsyncClassifyInternal(nsIPrincipal* aPrincipal);
365
  void GetClassificationTables(bool aIsThirdParty, nsACString& aTables);
366
367
  // For the fallback sync classification.
368
  nsCOMPtr<nsIURI> mClassificationURI;
369
370
  nsCOMPtr<nsIURIClassifier> mUriClassifier;
371
  bool mAsyncClassified;
372
  nsTArray<nsCString> mMatchedTables;
373
  mozilla::dom::FlashClassification mResult;
374
};
375
376
377
#define NAME_NOT_VALID ((nsSimpleContentList*)1)
378
379
nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString& aKey)
380
  : mKey(aKey)
381
0
{}
382
383
nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString* aKey)
384
  : mKey(aKey ? *aKey : nullptr)
385
0
{}
386
387
nsIdentifierMapEntry::~nsIdentifierMapEntry()
388
0
{}
389
390
nsIdentifierMapEntry::nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther)
391
  : PLDHashEntryHdr(std::move(aOther))
392
  , mKey(std::move(aOther.mKey))
393
  , mIdContentList(std::move(aOther.mIdContentList))
394
  , mNameContentList(std::move(aOther.mNameContentList))
395
  , mChangeCallbacks(std::move(aOther.mChangeCallbacks))
396
  , mImageElement(std::move(aOther.mImageElement))
397
0
{}
398
399
void
400
nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
401
0
{
402
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
403
0
                                     "mIdentifierMap mNameContentList");
404
0
  aCallback->NoteXPCOMChild(static_cast<nsINodeList*>(mNameContentList));
405
0
406
0
  if (mImageElement) {
407
0
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
408
0
                                       "mIdentifierMap mImageElement element");
409
0
    nsIContent* imageElement = mImageElement;
410
0
    aCallback->NoteXPCOMChild(imageElement);
411
0
  }
412
0
}
413
414
bool
415
nsIdentifierMapEntry::IsEmpty()
416
0
{
417
0
  return mIdContentList.IsEmpty() && !mNameContentList &&
418
0
         !mChangeCallbacks && !mImageElement;
419
0
}
420
421
bool
422
nsIdentifierMapEntry::HasNameElement() const
423
0
{
424
0
  return mNameContentList && mNameContentList->Length() != 0;
425
0
}
426
427
Element*
428
nsIdentifierMapEntry::GetIdElement()
429
0
{
430
0
  return mIdContentList.SafeElementAt(0);
431
0
}
432
433
Element*
434
nsIdentifierMapEntry::GetImageIdElement()
435
0
{
436
0
  return mImageElement ? mImageElement.get() : GetIdElement();
437
0
}
438
439
void
440
nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
441
                                               void* aData, bool aForImage)
442
0
{
443
0
  if (!mChangeCallbacks) {
444
0
    mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
445
0
  }
446
0
447
0
  ChangeCallback cc = { aCallback, aData, aForImage };
448
0
  mChangeCallbacks->PutEntry(cc);
449
0
}
450
451
void
452
nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
453
                                                  void* aData, bool aForImage)
454
0
{
455
0
  if (!mChangeCallbacks)
456
0
    return;
457
0
  ChangeCallback cc = { aCallback, aData, aForImage };
458
0
  mChangeCallbacks->RemoveEntry(cc);
459
0
  if (mChangeCallbacks->Count() == 0) {
460
0
    mChangeCallbacks = nullptr;
461
0
  }
462
0
}
463
464
void
465
nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
466
                                          Element* aNewElement,
467
                                          bool aImageOnly)
468
0
{
469
0
  if (!mChangeCallbacks)
470
0
    return;
471
0
472
0
  for (auto iter = mChangeCallbacks->ConstIter(); !iter.Done(); iter.Next()) {
473
0
    nsIdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get();
474
0
    // Don't fire image changes for non-image observers, and don't fire element
475
0
    // changes for image observers when an image override is active.
476
0
    if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) {
477
0
      continue;
478
0
    }
479
0
480
0
    if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) {
481
0
      iter.Remove();
482
0
    }
483
0
  }
484
0
}
485
486
namespace {
487
488
struct PositionComparator
489
{
490
  Element* const mElement;
491
0
  explicit PositionComparator(Element* const aElement) : mElement(aElement) {}
492
493
0
  int operator()(void* aElement) const {
494
0
    Element* curElement = static_cast<Element*>(aElement);
495
0
    if (mElement == curElement) {
496
0
      return 0;
497
0
    }
498
0
    if (nsContentUtils::PositionIsBefore(mElement, curElement)) {
499
0
      return -1;
500
0
    }
501
0
    return 1;
502
0
  }
503
};
504
505
} // namespace
506
507
bool
508
nsIdentifierMapEntry::AddIdElement(Element* aElement)
509
0
{
510
0
  MOZ_ASSERT(aElement, "Must have element");
511
0
  MOZ_ASSERT(!mIdContentList.Contains(nullptr),
512
0
                  "Why is null in our list?");
513
0
514
#ifdef DEBUG
515
  Element* currentElement = mIdContentList.SafeElementAt(0);
516
#endif
517
518
0
  // Common case
519
0
  if (mIdContentList.IsEmpty()) {
520
0
    if (!mIdContentList.AppendElement(aElement))
521
0
      return false;
522
0
    NS_ASSERTION(currentElement == nullptr, "How did that happen?");
523
0
    FireChangeCallbacks(nullptr, aElement);
524
0
    return true;
525
0
  }
526
0
527
0
  // We seem to have multiple content nodes for the same id, or XUL is messing
528
0
  // with us.  Search for the right place to insert the content.
529
0
530
0
  size_t idx;
531
0
  if (BinarySearchIf(mIdContentList, 0, mIdContentList.Length(),
532
0
                     PositionComparator(aElement), &idx)) {
533
0
    // Already in the list, so already in the right spot.  Get out of here.
534
0
    // XXXbz this only happens because XUL does all sorts of random
535
0
    // UpdateIdTableEntry calls.  Hate, hate, hate!
536
0
    return true;
537
0
  }
538
0
539
0
  if (!mIdContentList.InsertElementAt(idx, aElement)) {
540
0
    return false;
541
0
  }
542
0
543
0
  if (idx == 0) {
544
0
    Element* oldElement = mIdContentList.SafeElementAt(1);
545
0
    NS_ASSERTION(currentElement == oldElement, "How did that happen?");
546
0
    FireChangeCallbacks(oldElement, aElement);
547
0
  }
548
0
  return true;
549
0
}
550
551
void
552
nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
553
0
{
554
0
  MOZ_ASSERT(aElement, "Missing element");
555
0
556
0
  // This should only be called while the document is in an update.
557
0
  // Assertions near the call to this method guarantee this.
558
0
559
0
  // This could fire in OOM situations
560
0
  // Only assert this in HTML documents for now as XUL does all sorts of weird
561
0
  // crap.
562
0
  NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
563
0
               mIdContentList.Contains(aElement),
564
0
               "Removing id entry that doesn't exist");
565
0
566
0
  // XXXbz should this ever Compact() I guess when all the content is gone
567
0
  // we'll just get cleaned up in the natural order of things...
568
0
  Element* currentElement = mIdContentList.SafeElementAt(0);
569
0
  mIdContentList.RemoveElement(aElement);
570
0
  if (currentElement == aElement) {
571
0
    FireChangeCallbacks(currentElement, mIdContentList.SafeElementAt(0));
572
0
  }
573
0
}
574
575
void
576
nsIdentifierMapEntry::SetImageElement(Element* aElement)
577
0
{
578
0
  Element* oldElement = GetImageIdElement();
579
0
  mImageElement = aElement;
580
0
  Element* newElement = GetImageIdElement();
581
0
  if (oldElement != newElement) {
582
0
    FireChangeCallbacks(oldElement, newElement, true);
583
0
  }
584
0
}
585
586
namespace mozilla {
587
namespace dom {
588
class SimpleHTMLCollection final : public nsSimpleContentList
589
                                 , public nsIHTMLCollection
590
{
591
public:
592
0
  explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
593
594
  NS_DECL_ISUPPORTS_INHERITED
595
596
  virtual nsINode* GetParentObject() override
597
0
  {
598
0
    return nsSimpleContentList::GetParentObject();
599
0
  }
600
  virtual uint32_t Length() override
601
0
  {
602
0
    return nsSimpleContentList::Length();
603
0
  }
604
  virtual Element* GetElementAt(uint32_t aIndex) override
605
0
  {
606
0
    return mElements.SafeElementAt(aIndex)->AsElement();
607
0
  }
608
609
  virtual Element* GetFirstNamedElement(const nsAString& aName,
610
                                        bool& aFound) override
611
0
  {
612
0
    aFound = false;
613
0
    RefPtr<nsAtom> name = NS_Atomize(aName);
614
0
    for (uint32_t i = 0; i < mElements.Length(); i++) {
615
0
      MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
616
0
      Element* element = mElements[i]->AsElement();
617
0
      if (element->GetID() == name ||
618
0
          (element->HasName() &&
619
0
           element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
620
0
        aFound = true;
621
0
        return element;
622
0
      }
623
0
    }
624
0
    return nullptr;
625
0
  }
626
627
  virtual void GetSupportedNames(nsTArray<nsString>& aNames) override
628
0
  {
629
0
    AutoTArray<nsAtom*, 8> atoms;
630
0
    for (uint32_t i = 0; i < mElements.Length(); i++) {
631
0
      MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
632
0
      Element* element = mElements[i]->AsElement();
633
0
634
0
      nsAtom* id = element->GetID();
635
0
      MOZ_ASSERT(id != nsGkAtoms::_empty);
636
0
      if (id && !atoms.Contains(id)) {
637
0
        atoms.AppendElement(id);
638
0
      }
639
0
640
0
      if (element->HasName()) {
641
0
        nsAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
642
0
        MOZ_ASSERT(name && name != nsGkAtoms::_empty);
643
0
        if (name && !atoms.Contains(name)) {
644
0
          atoms.AppendElement(name);
645
0
        }
646
0
      }
647
0
    }
648
0
649
0
    nsString* names = aNames.AppendElements(atoms.Length());
650
0
    for (uint32_t i = 0; i < atoms.Length(); i++) {
651
0
      atoms[i]->ToString(names[i]);
652
0
    }
653
0
  }
654
655
  virtual JSObject* GetWrapperPreserveColorInternal() override
656
0
  {
657
0
    return nsWrapperCache::GetWrapperPreserveColor();
658
0
  }
659
  virtual void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override
660
0
  {
661
0
    nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
662
0
  }
663
  virtual JSObject* WrapObject(JSContext *aCx,
664
                               JS::Handle<JSObject*> aGivenProto) override
665
0
  {
666
0
    return HTMLCollection_Binding::Wrap(aCx, this, aGivenProto);
667
0
  }
668
669
  using nsBaseContentList::Item;
670
671
private:
672
0
  virtual ~SimpleHTMLCollection() {}
673
};
674
675
NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
676
                            nsIHTMLCollection)
677
678
} // namespace dom
679
} // namespace mozilla
680
681
void
682
nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
683
0
{
684
0
  if (!mNameContentList) {
685
0
    mNameContentList = new SimpleHTMLCollection(aNode);
686
0
  }
687
0
688
0
  mNameContentList->AppendElement(aElement);
689
0
}
690
691
void
692
nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
693
0
{
694
0
  if (mNameContentList) {
695
0
    mNameContentList->RemoveElement(aElement);
696
0
  }
697
0
}
698
699
bool
700
nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
701
0
{
702
0
  Element* idElement = GetIdElement();
703
0
  return idElement &&
704
0
         nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
705
0
}
706
707
size_t
708
nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
709
0
{
710
0
  return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
711
0
}
712
713
// Helper structs for the content->subdoc map
714
715
class SubDocMapEntry : public PLDHashEntryHdr
716
{
717
public:
718
  // Both of these are strong references
719
  Element *mKey; // must be first, to look like PLDHashEntryStub
720
  nsIDocument *mSubDocument;
721
};
722
723
// nsOnloadBlocker implementation
724
NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
725
726
NS_IMETHODIMP
727
nsOnloadBlocker::GetName(nsACString &aResult)
728
0
{
729
0
  aResult.AssignLiteral("about:document-onload-blocker");
730
0
  return NS_OK;
731
0
}
732
733
NS_IMETHODIMP
734
nsOnloadBlocker::IsPending(bool *_retval)
735
0
{
736
0
  *_retval = true;
737
0
  return NS_OK;
738
0
}
739
740
NS_IMETHODIMP
741
nsOnloadBlocker::GetStatus(nsresult *status)
742
0
{
743
0
  *status = NS_OK;
744
0
  return NS_OK;
745
0
}
746
747
NS_IMETHODIMP
748
nsOnloadBlocker::Cancel(nsresult status)
749
0
{
750
0
  return NS_OK;
751
0
}
752
NS_IMETHODIMP
753
nsOnloadBlocker::Suspend(void)
754
0
{
755
0
  return NS_OK;
756
0
}
757
NS_IMETHODIMP
758
nsOnloadBlocker::Resume(void)
759
0
{
760
0
  return NS_OK;
761
0
}
762
763
NS_IMETHODIMP
764
nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
765
0
{
766
0
  *aLoadGroup = nullptr;
767
0
  return NS_OK;
768
0
}
769
770
NS_IMETHODIMP
771
nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
772
0
{
773
0
  return NS_OK;
774
0
}
775
776
NS_IMETHODIMP
777
nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
778
0
{
779
0
  *aLoadFlags = nsIRequest::LOAD_NORMAL;
780
0
  return NS_OK;
781
0
}
782
783
NS_IMETHODIMP
784
nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
785
0
{
786
0
  return NS_OK;
787
0
}
788
789
// ==================================================================
790
791
nsExternalResourceMap::nsExternalResourceMap()
792
  : mHaveShutDown(false)
793
0
{
794
0
}
795
796
nsIDocument*
797
nsExternalResourceMap::RequestResource(nsIURI* aURI,
798
                                       nsIURI* aReferrer,
799
                                       uint32_t aReferrerPolicy,
800
                                       nsINode* aRequestingNode,
801
                                       nsIDocument* aDisplayDocument,
802
                                       ExternalResourceLoad** aPendingLoad)
803
0
{
804
0
  // If we ever start allowing non-same-origin loads here, we might need to do
805
0
  // something interesting with aRequestingPrincipal even for the hashtable
806
0
  // gets.
807
0
  MOZ_ASSERT(aURI, "Must have a URI");
808
0
  MOZ_ASSERT(aRequestingNode, "Must have a node");
809
0
  *aPendingLoad = nullptr;
810
0
  if (mHaveShutDown) {
811
0
    return nullptr;
812
0
  }
813
0
814
0
  // First, make sure we strip the ref from aURI.
815
0
  nsCOMPtr<nsIURI> clone;
816
0
  nsresult rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(clone));
817
0
  if (NS_FAILED(rv) || !clone) {
818
0
    return nullptr;
819
0
  }
820
0
821
0
  ExternalResource* resource;
822
0
  mMap.Get(clone, &resource);
823
0
  if (resource) {
824
0
    return resource->mDocument;
825
0
  }
826
0
827
0
  RefPtr<PendingLoad>& loadEntry = mPendingLoads.GetOrInsert(clone);
828
0
  if (loadEntry) {
829
0
    RefPtr<PendingLoad> load(loadEntry);
830
0
    load.forget(aPendingLoad);
831
0
    return nullptr;
832
0
  }
833
0
834
0
  RefPtr<PendingLoad> load(new PendingLoad(aDisplayDocument));
835
0
  loadEntry = load;
836
0
837
0
  if (NS_FAILED(load->StartLoad(clone, aReferrer, aReferrerPolicy,
838
0
                                aRequestingNode))) {
839
0
    // Make sure we don't thrash things by trying this load again, since
840
0
    // chances are it failed for good reasons (security check, etc).
841
0
    AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
842
0
  } else {
843
0
    load.forget(aPendingLoad);
844
0
  }
845
0
846
0
  return nullptr;
847
0
}
848
849
void
850
nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
851
                                          void* aData)
852
0
{
853
0
  for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
854
0
    nsExternalResourceMap::ExternalResource* resource = iter.UserData();
855
0
    if (resource->mDocument && !aCallback(resource->mDocument, aData)) {
856
0
      break;
857
0
    }
858
0
  }
859
0
}
860
861
void
862
nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
863
0
{
864
0
  // mPendingLoads will get cleared out as the requests complete, so
865
0
  // no need to worry about those here.
866
0
  for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
867
0
    nsExternalResourceMap::ExternalResource* resource = iter.UserData();
868
0
869
0
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
870
0
                                       "mExternalResourceMap.mMap entry"
871
0
                                       "->mDocument");
872
0
    aCallback->NoteXPCOMChild(resource->mDocument);
873
0
874
0
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
875
0
                                       "mExternalResourceMap.mMap entry"
876
0
                                       "->mViewer");
877
0
    aCallback->NoteXPCOMChild(resource->mViewer);
878
0
879
0
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
880
0
                                       "mExternalResourceMap.mMap entry"
881
0
                                       "->mLoadGroup");
882
0
    aCallback->NoteXPCOMChild(resource->mLoadGroup);
883
0
  }
884
0
}
885
886
void
887
nsExternalResourceMap::HideViewers()
888
0
{
889
0
  for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
890
0
    nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
891
0
    if (viewer) {
892
0
      viewer->Hide();
893
0
    }
894
0
  }
895
0
}
896
897
void
898
nsExternalResourceMap::ShowViewers()
899
0
{
900
0
  for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
901
0
    nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
902
0
    if (viewer) {
903
0
      viewer->Show();
904
0
    }
905
0
  }
906
0
}
907
908
void
909
TransferZoomLevels(nsIDocument* aFromDoc,
910
                   nsIDocument* aToDoc)
911
0
{
912
0
  MOZ_ASSERT(aFromDoc && aToDoc,
913
0
             "transferring zoom levels from/to null doc");
914
0
915
0
  nsPresContext* fromCtxt = aFromDoc->GetPresContext();
916
0
  if (!fromCtxt)
917
0
    return;
918
0
919
0
  nsPresContext* toCtxt = aToDoc->GetPresContext();
920
0
  if (!toCtxt)
921
0
    return;
922
0
923
0
  toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
924
0
  toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
925
0
  toCtxt->SetTextZoom(fromCtxt->TextZoom());
926
0
  toCtxt->SetOverrideDPPX(fromCtxt->GetOverrideDPPX());
927
0
}
928
929
void
930
TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc)
931
0
{
932
0
  MOZ_ASSERT(aFromDoc && aToDoc,
933
0
             "transferring showing state from/to null doc");
934
0
935
0
  if (aFromDoc->IsShowing()) {
936
0
    aToDoc->OnPageShow(true, nullptr);
937
0
  }
938
0
}
939
940
nsresult
941
nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
942
                                           nsIContentViewer* aViewer,
943
                                           nsILoadGroup* aLoadGroup,
944
                                           nsIDocument* aDisplayDocument)
945
0
{
946
0
  MOZ_ASSERT(aURI, "Unexpected call");
947
0
  MOZ_ASSERT((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
948
0
             "Must have both or neither");
949
0
950
0
  RefPtr<PendingLoad> load;
951
0
  mPendingLoads.Remove(aURI, getter_AddRefs(load));
952
0
953
0
  nsresult rv = NS_OK;
954
0
955
0
  nsCOMPtr<nsIDocument> doc;
956
0
  if (aViewer) {
957
0
    doc = aViewer->GetDocument();
958
0
    NS_ASSERTION(doc, "Must have a document");
959
0
960
0
    if (doc->IsXULDocument()) {
961
0
      // We don't handle XUL stuff here yet.
962
0
      rv = NS_ERROR_NOT_AVAILABLE;
963
0
    } else {
964
0
      doc->SetDisplayDocument(aDisplayDocument);
965
0
966
0
      // Make sure that hiding our viewer will tear down its presentation.
967
0
      aViewer->SetSticky(false);
968
0
969
0
      rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
970
0
      if (NS_SUCCEEDED(rv)) {
971
0
        rv = aViewer->Open(nullptr, nullptr);
972
0
      }
973
0
    }
974
0
975
0
    if (NS_FAILED(rv)) {
976
0
      doc = nullptr;
977
0
      aViewer = nullptr;
978
0
      aLoadGroup = nullptr;
979
0
    }
980
0
  }
981
0
982
0
  ExternalResource* newResource = new ExternalResource();
983
0
  mMap.Put(aURI, newResource);
984
0
985
0
  newResource->mDocument = doc;
986
0
  newResource->mViewer = aViewer;
987
0
  newResource->mLoadGroup = aLoadGroup;
988
0
  if (doc) {
989
0
    TransferZoomLevels(aDisplayDocument, doc);
990
0
    TransferShowingState(aDisplayDocument, doc);
991
0
  }
992
0
993
0
  const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
994
0
  for (uint32_t i = 0; i < obs.Length(); ++i) {
995
0
    obs[i]->Observe(doc, "external-resource-document-created", nullptr);
996
0
  }
997
0
998
0
  return rv;
999
0
}
1000
1001
NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,
1002
                  nsIStreamListener,
1003
                  nsIRequestObserver)
1004
1005
NS_IMETHODIMP
1006
nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
1007
                                                   nsISupports *aContext)
1008
0
{
1009
0
  nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
1010
0
  if (map.HaveShutDown()) {
1011
0
    return NS_BINDING_ABORTED;
1012
0
  }
1013
0
1014
0
  nsCOMPtr<nsIContentViewer> viewer;
1015
0
  nsCOMPtr<nsILoadGroup> loadGroup;
1016
0
  nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
1017
0
                            getter_AddRefs(loadGroup));
1018
0
1019
0
  // Make sure to do this no matter what
1020
0
  nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
1021
0
                                         mDisplayDocument);
1022
0
  if (NS_FAILED(rv)) {
1023
0
    return rv;
1024
0
  }
1025
0
  if (NS_FAILED(rv2)) {
1026
0
    mTargetListener = nullptr;
1027
0
    return rv2;
1028
0
  }
1029
0
1030
0
  return mTargetListener->OnStartRequest(aRequest, aContext);
1031
0
}
1032
1033
nsresult
1034
nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
1035
                                                nsIContentViewer** aViewer,
1036
                                                nsILoadGroup** aLoadGroup)
1037
0
{
1038
0
  MOZ_ASSERT(!mTargetListener, "Unexpected call to OnStartRequest");
1039
0
  *aViewer = nullptr;
1040
0
  *aLoadGroup = nullptr;
1041
0
1042
0
  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
1043
0
  NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
1044
0
1045
0
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
1046
0
  if (httpChannel) {
1047
0
    bool requestSucceeded;
1048
0
    if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
1049
0
        !requestSucceeded) {
1050
0
      // Bail out on this load, since it looks like we have an HTTP error page
1051
0
      return NS_BINDING_ABORTED;
1052
0
    }
1053
0
  }
1054
0
1055
0
  nsAutoCString type;
1056
0
  chan->GetContentType(type);
1057
0
1058
0
  nsCOMPtr<nsILoadGroup> loadGroup;
1059
0
  chan->GetLoadGroup(getter_AddRefs(loadGroup));
1060
0
1061
0
  // Give this document its own loadgroup
1062
0
  nsCOMPtr<nsILoadGroup> newLoadGroup =
1063
0
        do_CreateInstance(NS_LOADGROUP_CONTRACTID);
1064
0
  NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
1065
0
  newLoadGroup->SetLoadGroup(loadGroup);
1066
0
1067
0
  nsCOMPtr<nsIInterfaceRequestor> callbacks;
1068
0
  loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1069
0
1070
0
  nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1071
0
    new LoadgroupCallbacks(callbacks);
1072
0
  newLoadGroup->SetNotificationCallbacks(newCallbacks);
1073
0
1074
0
  // This is some serious hackery cribbed from docshell
1075
0
  nsCOMPtr<nsICategoryManager> catMan =
1076
0
    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1077
0
  NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1078
0
  nsCString contractId;
1079
0
  nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type,
1080
0
                                         contractId);
1081
0
  NS_ENSURE_SUCCESS(rv, rv);
1082
0
  nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1083
0
    do_GetService(contractId.get());
1084
0
  NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1085
0
1086
0
  nsCOMPtr<nsIContentViewer> viewer;
1087
0
  nsCOMPtr<nsIStreamListener> listener;
1088
0
  rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
1089
0
                                        type, nullptr, nullptr,
1090
0
                                        getter_AddRefs(listener),
1091
0
                                        getter_AddRefs(viewer));
1092
0
  NS_ENSURE_SUCCESS(rv, rv);
1093
0
  NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1094
0
1095
0
  nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1096
0
  if (!parser) {
1097
0
    /// We don't want to deal with the various fake documents yet
1098
0
    return NS_ERROR_NOT_IMPLEMENTED;
1099
0
  }
1100
0
1101
0
  // We can't handle HTML and other weird things here yet.
1102
0
  nsIContentSink* sink = parser->GetContentSink();
1103
0
  nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1104
0
  if (!xmlSink) {
1105
0
    return NS_ERROR_NOT_IMPLEMENTED;
1106
0
  }
1107
0
1108
0
  listener.swap(mTargetListener);
1109
0
  viewer.forget(aViewer);
1110
0
  newLoadGroup.forget(aLoadGroup);
1111
0
  return NS_OK;
1112
0
}
1113
1114
NS_IMETHODIMP
1115
nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1116
                                                    nsISupports* aContext,
1117
                                                    nsIInputStream* aStream,
1118
                                                    uint64_t aOffset,
1119
                                                    uint32_t aCount)
1120
0
{
1121
0
  // mTargetListener might be null if SetupViewer or AddExternalResource failed.
1122
0
  NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE);
1123
0
  if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1124
0
    return NS_BINDING_ABORTED;
1125
0
  }
1126
0
  return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1127
0
                                          aCount);
1128
0
}
1129
1130
NS_IMETHODIMP
1131
nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1132
                                                  nsISupports* aContext,
1133
                                                  nsresult aStatus)
1134
0
{
1135
0
  // mTargetListener might be null if SetupViewer or AddExternalResource failed
1136
0
  if (mTargetListener) {
1137
0
    nsCOMPtr<nsIStreamListener> listener;
1138
0
    mTargetListener.swap(listener);
1139
0
    return listener->OnStopRequest(aRequest, aContext, aStatus);
1140
0
  }
1141
0
1142
0
  return NS_OK;
1143
0
}
1144
1145
nsresult
1146
nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
1147
                                              nsIURI* aReferrer,
1148
                                              uint32_t aReferrerPolicy,
1149
                                              nsINode* aRequestingNode)
1150
0
{
1151
0
  MOZ_ASSERT(aURI, "Must have a URI");
1152
0
  MOZ_ASSERT(aRequestingNode, "Must have a node");
1153
0
1154
0
  nsCOMPtr<nsILoadGroup> loadGroup =
1155
0
    aRequestingNode->OwnerDoc()->GetDocumentLoadGroup();
1156
0
1157
0
  nsresult rv = NS_OK;
1158
0
  nsCOMPtr<nsIChannel> channel;
1159
0
  rv = NS_NewChannel(getter_AddRefs(channel),
1160
0
                     aURI,
1161
0
                     aRequestingNode,
1162
0
                     nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
1163
0
                     nsIContentPolicy::TYPE_OTHER,
1164
0
                     nullptr, // aPerformanceStorage
1165
0
                     loadGroup);
1166
0
  NS_ENSURE_SUCCESS(rv, rv);
1167
0
1168
0
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
1169
0
  if (httpChannel) {
1170
0
    rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
1171
0
    Unused << NS_WARN_IF(NS_FAILED(rv));
1172
0
  }
1173
0
1174
0
  mURI = aURI;
1175
0
1176
0
  return channel->AsyncOpen2(this);
1177
0
}
1178
1179
NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
1180
                  nsIInterfaceRequestor)
1181
1182
#define IMPL_SHIM(_i) \
1183
  NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1184
1185
IMPL_SHIM(nsILoadContext)
1186
IMPL_SHIM(nsIProgressEventSink)
1187
IMPL_SHIM(nsIChannelEventSink)
1188
IMPL_SHIM(nsISecurityEventSink)
1189
IMPL_SHIM(nsIApplicationCacheContainer)
1190
1191
#undef IMPL_SHIM
1192
1193
0
#define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1194
1195
#define TRY_SHIM(_i)                                                       \
1196
0
  PR_BEGIN_MACRO                                                           \
1197
0
    if (IID_IS(_i)) {                                                      \
1198
0
      nsCOMPtr<_i> real = do_GetInterface(mCallbacks);                     \
1199
0
      if (!real) {                                                         \
1200
0
        return NS_NOINTERFACE;                                             \
1201
0
      }                                                                    \
1202
0
      nsCOMPtr<_i> shim = new _i##Shim(this, real);                        \
1203
0
      shim.forget(aSink);                                                  \
1204
0
      return NS_OK;                                                        \
1205
0
    }                                                                      \
1206
0
  PR_END_MACRO
1207
1208
NS_IMETHODIMP
1209
nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
1210
                                                        void **aSink)
1211
0
{
1212
0
  if (mCallbacks &&
1213
0
      (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) ||
1214
0
       IID_IS(nsITabChild))) {
1215
0
    return mCallbacks->GetInterface(aIID, aSink);
1216
0
  }
1217
0
1218
0
  *aSink = nullptr;
1219
0
1220
0
  TRY_SHIM(nsILoadContext);
1221
0
  TRY_SHIM(nsIProgressEventSink);
1222
0
  TRY_SHIM(nsIChannelEventSink);
1223
0
  TRY_SHIM(nsISecurityEventSink);
1224
0
  TRY_SHIM(nsIApplicationCacheContainer);
1225
0
1226
0
  return NS_NOINTERFACE;
1227
0
}
1228
1229
#undef TRY_SHIM
1230
#undef IID_IS
1231
1232
nsExternalResourceMap::ExternalResource::~ExternalResource()
1233
0
{
1234
0
  if (mViewer) {
1235
0
    mViewer->Close(nullptr);
1236
0
    mViewer->Destroy();
1237
0
  }
1238
0
}
1239
1240
// ==================================================================
1241
// =
1242
// ==================================================================
1243
1244
// If we ever have an nsIDocumentObserver notification for stylesheet title
1245
// changes we should update the list from that instead of overriding
1246
// EnsureFresh.
1247
class nsDOMStyleSheetSetList final : public DOMStringList
1248
{
1249
public:
1250
  explicit nsDOMStyleSheetSetList(nsIDocument* aDocument);
1251
1252
  void Disconnect()
1253
0
  {
1254
0
    mDocument = nullptr;
1255
0
  }
1256
1257
  virtual void EnsureFresh() override;
1258
1259
protected:
1260
  nsIDocument* mDocument;  // Our document; weak ref.  It'll let us know if it
1261
                           // dies.
1262
};
1263
1264
nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1265
  : mDocument(aDocument)
1266
0
{
1267
0
  NS_ASSERTION(mDocument, "Must have document!");
1268
0
}
1269
1270
void
1271
nsDOMStyleSheetSetList::EnsureFresh()
1272
0
{
1273
0
  MOZ_ASSERT(NS_IsMainThread());
1274
0
1275
0
  mNames.Clear();
1276
0
1277
0
  if (!mDocument) {
1278
0
    return; // Spec says "no exceptions", and we have no style sets if we have
1279
0
            // no document, for sure
1280
0
  }
1281
0
1282
0
  size_t count = mDocument->SheetCount();
1283
0
  nsAutoString title;
1284
0
  for (size_t index = 0; index < count; index++) {
1285
0
    StyleSheet* sheet = mDocument->SheetAt(index);
1286
0
    NS_ASSERTION(sheet, "Null sheet in sheet list!");
1287
0
    sheet->GetTitle(title);
1288
0
    if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1289
0
      return;
1290
0
    }
1291
0
  }
1292
0
}
1293
1294
// ==================================================================
1295
nsIDocument::SelectorCache::SelectorCache(nsIEventTarget* aEventTarget)
1296
  : nsExpirationTracker<SelectorCacheKey, 4>(
1297
      1000, "nsIDocument::SelectorCache", aEventTarget)
1298
0
{ }
1299
1300
nsIDocument::SelectorCache::~SelectorCache()
1301
0
{
1302
0
  AgeAllGenerations();
1303
0
}
1304
1305
void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector)
1306
0
{
1307
0
  MOZ_ASSERT(NS_IsMainThread());
1308
0
  MOZ_ASSERT(aSelector);
1309
0
1310
0
  // There is no guarantee that this method won't be re-entered when selector
1311
0
  // matching is ongoing because "memory-pressure" could be notified immediately
1312
0
  // when OOM happens according to the design of nsExpirationTracker.
1313
0
  // The perfect solution is to delete the |aSelector| and its
1314
0
  // RawServoSelectorList in mTable asynchronously.
1315
0
  // We remove these objects synchronously for now because NotifyExpired() will
1316
0
  // never be triggered by "memory-pressure" which is not implemented yet in
1317
0
  // the stage 2 of mozalloc_handle_oom().
1318
0
  // Once these objects are removed asynchronously, we should update the warning
1319
0
  // added in mozalloc_handle_oom() as well.
1320
0
  RemoveObject(aSelector);
1321
0
  mTable.Remove(aSelector->mKey);
1322
0
  delete aSelector;
1323
0
}
1324
1325
struct nsIDocument::FrameRequest
1326
{
1327
  FrameRequest(FrameRequestCallback& aCallback,
1328
               int32_t aHandle) :
1329
    mCallback(&aCallback),
1330
    mHandle(aHandle)
1331
0
  {}
1332
1333
  // Conversion operator so that we can append these to a
1334
  // FrameRequestCallbackList
1335
0
  operator const RefPtr<FrameRequestCallback>& () const {
1336
0
    return mCallback;
1337
0
  }
1338
1339
  // Comparator operators to allow RemoveElementSorted with an
1340
  // integer argument on arrays of FrameRequest
1341
0
  bool operator==(int32_t aHandle) const {
1342
0
    return mHandle == aHandle;
1343
0
  }
1344
0
  bool operator<(int32_t aHandle) const {
1345
0
    return mHandle < aHandle;
1346
0
  }
1347
1348
  RefPtr<FrameRequestCallback> mCallback;
1349
  int32_t mHandle;
1350
};
1351
1352
// ==================================================================
1353
// =
1354
// ==================================================================
1355
nsIDocument::nsIDocument()
1356
  : nsINode(nullptr),
1357
    DocumentOrShadowRoot(*this),
1358
    mReferrerPolicySet(false),
1359
    mReferrerPolicy(mozilla::net::RP_Unset),
1360
    mBlockAllMixedContent(false),
1361
    mBlockAllMixedContentPreloads(false),
1362
    mUpgradeInsecureRequests(false),
1363
    mUpgradeInsecurePreloads(false),
1364
    mCharacterSet(WINDOWS_1252_ENCODING),
1365
    mCharacterSetSource(0),
1366
    mParentDocument(nullptr),
1367
    mCachedRootElement(nullptr),
1368
    mNodeInfoManager(nullptr),
1369
#ifdef DEBUG
1370
    mStyledLinksCleared(false),
1371
#endif
1372
    mBidiEnabled(false),
1373
    mMathMLEnabled(false),
1374
    mIsInitialDocumentInWindow(false),
1375
    mIgnoreDocGroupMismatches(false),
1376
    mLoadedAsData(false),
1377
    mLoadedAsInteractiveData(false),
1378
    mMayStartLayout(true),
1379
    mHaveFiredTitleChange(false),
1380
    mIsShowing(false),
1381
    mVisible(true),
1382
    mRemovedFromDocShell(false),
1383
    // mAllowDNSPrefetch starts true, so that we can always reliably && it
1384
    // with various values that might disable it.  Since we never prefetch
1385
    // unless we get a window, and in that case the docshell value will get
1386
    // &&-ed in, this is safe.
1387
    mAllowDNSPrefetch(true),
1388
    mIsStaticDocument(false),
1389
    mCreatingStaticClone(false),
1390
    mInUnlinkOrDeletion(false),
1391
    mHasHadScriptHandlingObject(false),
1392
    mIsBeingUsedAsImage(false),
1393
    mIsSyntheticDocument(false),
1394
    mHasLinksToUpdateRunnable(false),
1395
    mFlushingPendingLinkUpdates(false),
1396
    mMayHaveDOMMutationObservers(false),
1397
    mMayHaveAnimationObservers(false),
1398
    mHasMixedActiveContentLoaded(false),
1399
    mHasMixedActiveContentBlocked(false),
1400
    mHasMixedDisplayContentLoaded(false),
1401
    mHasMixedDisplayContentBlocked(false),
1402
    mHasMixedContentObjectSubrequest(false),
1403
    mHasCSP(false),
1404
    mHasUnsafeEvalCSP(false),
1405
    mHasUnsafeInlineCSP(false),
1406
    mHasTrackingContentBlocked(false),
1407
    mHasSlowTrackingContentBlocked(false),
1408
    mHasAllCookiesBlocked(false),
1409
    mHasTrackingCookiesBlocked(false),
1410
    mHasForeignCookiesBlocked(false),
1411
    mHasCookiesBlockedByPermission(false),
1412
    mHasTrackingContentLoaded(false),
1413
    mBFCacheDisallowed(false),
1414
    mHasHadDefaultView(false),
1415
    mStyleSheetChangeEventsEnabled(false),
1416
    mIsSrcdocDocument(false),
1417
    mDidDocumentOpen(false),
1418
    mHasDisplayDocument(false),
1419
    mFontFaceSetDirty(true),
1420
    mGetUserFontSetCalled(false),
1421
    mDidFireDOMContentLoaded(true),
1422
    mHasScrollLinkedEffect(false),
1423
    mFrameRequestCallbacksScheduled(false),
1424
    mIsTopLevelContentDocument(false),
1425
    mIsContentDocument(false),
1426
    mDidCallBeginLoad(false),
1427
    mAllowPaymentRequest(false),
1428
    mEncodingMenuDisabled(false),
1429
    mIsShadowDOMEnabled(false),
1430
    mIsSVGGlyphsDocument(false),
1431
    mInDestructor(false),
1432
    mIsGoingAway(false),
1433
    mInXBLUpdate(false),
1434
    mNeedsReleaseAfterStackRefCntRelease(false),
1435
    mStyleSetFilled(false),
1436
    mSSApplicableStateNotificationPending(false),
1437
    mMayHaveTitleElement(false),
1438
    mDOMLoadingSet(false),
1439
    mDOMInteractiveSet(false),
1440
    mDOMCompleteSet(false),
1441
    mAutoFocusFired(false),
1442
    mScrolledToRefAlready(false),
1443
    mChangeScrollPosWhenScrollingToRef(false),
1444
    mHasWarnedAboutBoxObjects(false),
1445
    mDelayFrameLoaderInitialization(false),
1446
    mSynchronousDOMContentLoaded(false),
1447
    mMaybeServiceWorkerControlled(false),
1448
    mValidWidth(false),
1449
    mValidHeight(false),
1450
    mAutoSize(false),
1451
    mAllowZoom(false),
1452
    mAllowDoubleTapZoom(false),
1453
    mValidScaleFloat(false),
1454
    mValidMaxScale(false),
1455
    mScaleStrEmpty(false),
1456
    mWidthStrEmpty(false),
1457
    mParserAborted(false),
1458
    mReportedUseCounters(false),
1459
    mHasReportedShadowDOMUsage(false),
1460
    mDocTreeHadAudibleMedia(false),
1461
    mDocTreeHadPlayRevoked(false),
1462
#ifdef DEBUG
1463
    mWillReparent(false),
1464
#endif
1465
    mPendingFullscreenRequests(0),
1466
    mXMLDeclarationBits(0),
1467
    mOnloadBlockCount(0),
1468
    mAsyncOnloadBlockCount(0),
1469
    mCompatMode(eCompatibility_FullStandards),
1470
    mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
1471
#ifdef MOZILLA_INTERNAL_API
1472
    mVisibilityState(dom::VisibilityState::Hidden),
1473
#else
1474
    mDummy(0),
1475
#endif
1476
    mType(eUnknown),
1477
    mDefaultElementType(0),
1478
    mAllowXULXBL(eTriUnset),
1479
    mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
1480
    mSandboxFlags(0),
1481
    mPartID(0),
1482
    mMarkedCCGeneration(0),
1483
    mPresShell(nullptr),
1484
    mSubtreeModifiedDepth(0),
1485
    mPreloadPictureDepth(0),
1486
    mEventsSuppressed(0),
1487
    mIgnoreDestructiveWritesCounter(0),
1488
    mFrameRequestCallbackCounter(0),
1489
    mStaticCloneCount(0),
1490
    mWindow(nullptr),
1491
    mBFCacheEntry(nullptr),
1492
    mInSyncOperationCount(0),
1493
    mBlockDOMContentLoaded(0),
1494
    mUseCounters(0),
1495
    mChildDocumentUseCounters(0),
1496
    mNotifiedPageForUseCounter(0),
1497
    mUserHasInteracted(false),
1498
    mHasUserInteractionTimerScheduled(false),
1499
    mUserGestureActivated(false),
1500
    mStackRefCnt(0),
1501
    mUpdateNestLevel(0),
1502
    mViewportType(Unknown),
1503
    mViewportOverflowType(ViewportOverflowType::NoOverflow),
1504
    mSubDocuments(nullptr),
1505
    mHeaderData(nullptr),
1506
    mFlashClassification(FlashClassification::Unclassified),
1507
    mBoxObjectTable(nullptr),
1508
    mCurrentOrientationAngle(0),
1509
    mCurrentOrientationType(OrientationType::Portrait_primary),
1510
    mServoRestyleRootDirtyBits(0),
1511
    mThrowOnDynamicMarkupInsertionCounter(0),
1512
    mIgnoreOpensDuringUnloadCounter(0),
1513
    mNumTrackersFound(0),
1514
    mNumTrackersBlocked(0),
1515
    mDocLWTheme(Doc_Theme_Uninitialized)
1516
0
{
1517
0
  SetIsInDocument();
1518
0
  SetIsConnected(true);
1519
0
1520
0
  if (StaticPrefs::layout_css_use_counters_enabled()) {
1521
0
    mStyleUseCounters.reset(Servo_UseCounters_Create());
1522
0
  }
1523
0
}
1524
1525
nsDocument::nsDocument(const char* aContentType)
1526
  : nsIDocument()
1527
0
{
1528
0
  SetContentTypeInternal(nsDependentCString(aContentType));
1529
0
1530
0
  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
1531
0
1532
0
  // Start out mLastStyleSheetSet as null, per spec
1533
0
  SetDOMStringToNull(mLastStyleSheetSet);
1534
0
1535
0
  // void state used to differentiate an empty source from an unselected source
1536
0
  mPreloadPictureFoundSource.SetIsVoid(true);
1537
0
  // For determining if this is a flash document which should be
1538
0
  // blocked based on its principal.
1539
0
  mPrincipalFlashClassifier = new PrincipalFlashClassifier();
1540
0
}
1541
1542
void
1543
nsIDocument::ClearAllBoxObjects()
1544
0
{
1545
0
  if (mBoxObjectTable) {
1546
0
    for (auto iter = mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1547
0
      nsPIBoxObject* boxObject = iter.UserData();
1548
0
      if (boxObject) {
1549
0
        boxObject->Clear();
1550
0
      }
1551
0
    }
1552
0
    delete mBoxObjectTable;
1553
0
    mBoxObjectTable = nullptr;
1554
0
  }
1555
0
}
1556
1557
nsIDocument::~nsIDocument()
1558
0
{
1559
0
  MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
1560
0
             "must not have media query lists left");
1561
0
1562
0
  if (mNodeInfoManager) {
1563
0
    mNodeInfoManager->DropDocumentReference();
1564
0
  }
1565
0
1566
0
  if (mDocGroup) {
1567
0
    mDocGroup->RemoveDocument(this);
1568
0
  }
1569
0
1570
0
  UnlinkOriginalDocumentIfStatic();
1571
0
}
1572
1573
bool
1574
nsIDocument::IsAboutPage() const
1575
0
{
1576
0
  nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
1577
0
  nsCOMPtr<nsIURI> uri;
1578
0
  principal->GetURI(getter_AddRefs(uri));
1579
0
  bool isAboutScheme = true;
1580
0
  if (uri) {
1581
0
    uri->SchemeIs("about", &isAboutScheme);
1582
0
  }
1583
0
  return isAboutScheme;
1584
0
}
1585
1586
void
1587
nsIDocument::ConstructUbiNode(void* storage)
1588
0
{
1589
0
  JS::ubi::Concrete<nsIDocument>::construct(storage, this);
1590
0
}
1591
1592
1593
nsDocument::~nsDocument()
1594
0
{
1595
0
  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
1596
0
1597
0
  NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
1598
0
1599
0
  if (IsTopLevelContentDocument()) {
1600
0
    //don't report for about: pages
1601
0
    if (!IsAboutPage()) {
1602
0
      // Record the page load
1603
0
      uint32_t pageLoaded = 1;
1604
0
      Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1605
0
      // Record the mixed content status of the docshell in Telemetry
1606
0
      enum {
1607
0
        NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1608
0
        MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
1609
0
        MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
1610
0
        MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content
1611
0
      };
1612
0
1613
0
      bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
1614
0
      bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
1615
0
1616
0
      bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
1617
0
      bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
1618
0
1619
0
      bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
1620
0
      bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
1621
0
1622
0
      uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1623
0
      if (hasMixedDisplay && hasMixedActive) {
1624
0
        mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1625
0
      } else if (hasMixedActive){
1626
0
        mixedContentLevel = MIXED_ACTIVE_CONTENT;
1627
0
      } else if (hasMixedDisplay) {
1628
0
        mixedContentLevel = MIXED_DISPLAY_CONTENT;
1629
0
      }
1630
0
      Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1631
0
1632
0
      // record mixed object subrequest telemetry
1633
0
      if (mHasMixedContentObjectSubrequest) {
1634
0
        /* mixed object subrequest loaded on page*/
1635
0
        Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 1);
1636
0
      } else {
1637
0
        /* no mixed object subrequests loaded on page*/
1638
0
        Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 0);
1639
0
      }
1640
0
1641
0
      // record CSP telemetry on this document
1642
0
      if (mHasCSP) {
1643
0
        Accumulate(Telemetry::CSP_DOCUMENTS_COUNT, 1);
1644
0
      }
1645
0
      if (mHasUnsafeInlineCSP) {
1646
0
        Accumulate(Telemetry::CSP_UNSAFE_INLINE_DOCUMENTS_COUNT, 1);
1647
0
      }
1648
0
      if (mHasUnsafeEvalCSP) {
1649
0
        Accumulate(Telemetry::CSP_UNSAFE_EVAL_DOCUMENTS_COUNT, 1);
1650
0
      }
1651
0
1652
0
      if (MOZ_UNLIKELY(mMathMLEnabled)) {
1653
0
        ScalarAdd(Telemetry::ScalarID::MATHML_DOC_COUNT, 1);
1654
0
      }
1655
0
1656
0
      ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_COUNT, 1);
1657
0
      if (mDocTreeHadAudibleMedia) {
1658
0
        ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_HAD_MEDIA_COUNT, 1);
1659
0
      }
1660
0
      if (mDocTreeHadPlayRevoked) {
1661
0
        ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_HAD_PLAY_REVOKED_COUNT, 1);
1662
0
      }
1663
0
    }
1664
0
1665
0
    // Report the fastblock telemetry probes when the document is dying if
1666
0
    // fastblock is enabled and we're not a private document.  We always report
1667
0
    // the all probe, and for the rest, report each category's probe depending
1668
0
    // on whether the respective bit has been set in our enum set.
1669
0
    if (StaticPrefs::browser_contentblocking_enabled() &&
1670
0
        StaticPrefs::browser_fastblock_enabled() &&
1671
0
        !nsContentUtils::IsInPrivateBrowsing(this)) {
1672
0
      for (auto label : mTrackerBlockedReasons) {
1673
0
        AccumulateCategorical(label);
1674
0
      }
1675
0
      // Always accumulate the "all" probe since we will use it as a baseline counter.
1676
0
      AccumulateCategorical(Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all);
1677
0
    }
1678
0
  }
1679
0
1680
0
  ReportUseCounters();
1681
0
1682
0
  mInDestructor = true;
1683
0
  mInUnlinkOrDeletion = true;
1684
0
1685
0
  mozilla::DropJSObjects(this);
1686
0
1687
0
  // Clear mObservers to keep it in sync with the mutationobserver list
1688
0
  mObservers.Clear();
1689
0
1690
0
  mIntersectionObservers.Clear();
1691
0
1692
0
  if (mStyleSheetSetList) {
1693
0
    mStyleSheetSetList->Disconnect();
1694
0
  }
1695
0
1696
0
  if (mAnimationController) {
1697
0
    mAnimationController->Disconnect();
1698
0
  }
1699
0
1700
0
  MOZ_ASSERT(mTimelines.isEmpty());
1701
0
1702
0
  mParentDocument = nullptr;
1703
0
1704
0
  // Kill the subdocument map, doing this will release its strong
1705
0
  // references, if any.
1706
0
  delete mSubDocuments;
1707
0
  mSubDocuments = nullptr;
1708
0
1709
0
  // Destroy link map now so we don't waste time removing
1710
0
  // links one by one
1711
0
  DestroyElementMaps();
1712
0
1713
0
  nsAutoScriptBlocker scriptBlocker;
1714
0
1715
0
  // Invalidate cached array of child nodes
1716
0
  InvalidateChildNodes();
1717
0
1718
0
  // We should not have child nodes when destructor is called,
1719
0
  // since child nodes keep their owner document alive.
1720
0
  MOZ_ASSERT(!HasChildren());
1721
0
1722
0
  mCachedRootElement = nullptr;
1723
0
1724
0
  for (auto& sheets : mAdditionalSheets) {
1725
0
    for (StyleSheet* sheet : sheets) {
1726
0
      sheet->ClearAssociatedDocumentOrShadowRoot();
1727
0
    }
1728
0
  }
1729
0
1730
0
  if (mAttrStyleSheet) {
1731
0
    mAttrStyleSheet->SetOwningDocument(nullptr);
1732
0
  }
1733
0
1734
0
  if (mListenerManager) {
1735
0
    mListenerManager->Disconnect();
1736
0
    UnsetFlags(NODE_HAS_LISTENERMANAGER);
1737
0
  }
1738
0
1739
0
  if (mScriptLoader) {
1740
0
    mScriptLoader->DropDocumentReference();
1741
0
  }
1742
0
1743
0
  if (mCSSLoader) {
1744
0
    // Could be null here if Init() failed or if we have been unlinked.
1745
0
    mCSSLoader->DropDocumentReference();
1746
0
  }
1747
0
1748
0
  if (mStyleImageLoader) {
1749
0
    mStyleImageLoader->DropDocumentReference();
1750
0
  }
1751
0
1752
0
  delete mHeaderData;
1753
0
1754
0
  ClearAllBoxObjects();
1755
0
1756
0
  mPendingTitleChangeEvent.Revoke();
1757
0
1758
0
  mPlugins.Clear();
1759
0
}
1760
1761
0
NS_INTERFACE_TABLE_HEAD(nsDocument)
1762
0
  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
1763
0
  NS_INTERFACE_TABLE_BEGIN
1764
0
    NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
1765
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1766
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1767
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1768
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
1769
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1770
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1771
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1772
0
    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1773
0
  NS_INTERFACE_TABLE_END
1774
0
  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
1775
0
NS_INTERFACE_MAP_END
1776
1777
1778
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
1779
NS_IMETHODIMP_(MozExternalRefCountType)
1780
nsDocument::Release()
1781
0
{
1782
0
  MOZ_ASSERT(0 != mRefCnt, "dup release");
1783
0
  NS_ASSERT_OWNINGTHREAD(nsDocument);
1784
0
  nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
1785
0
  bool shouldDelete = false;
1786
0
  nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
1787
0
  NS_LOG_RELEASE(this, count, "nsDocument");
1788
0
  if (count == 0) {
1789
0
    if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
1790
0
      mNeedsReleaseAfterStackRefCntRelease = true;
1791
0
      NS_ADDREF_THIS();
1792
0
      return mRefCnt.get();
1793
0
    }
1794
0
    mRefCnt.incr(base);
1795
0
    nsNodeUtils::LastRelease(this);
1796
0
    mRefCnt.decr(base);
1797
0
    if (shouldDelete) {
1798
0
      mRefCnt.stabilizeForDeletion();
1799
0
      DeleteCycleCollectable();
1800
0
    }
1801
0
  }
1802
0
  return count;
1803
0
}
1804
1805
NS_IMETHODIMP_(void)
1806
nsDocument::DeleteCycleCollectable()
1807
0
{
1808
0
  delete this;
1809
0
}
1810
1811
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
1812
0
  if (Element::CanSkip(tmp, aRemovingAllowed)) {
1813
0
    EventListenerManager* elm = tmp->GetExistingListenerManager();
1814
0
    if (elm) {
1815
0
      elm->MarkForCC();
1816
0
    }
1817
0
    return true;
1818
0
  }
1819
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1820
0
1821
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
1822
0
  return Element::CanSkipInCC(tmp);
1823
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1824
1825
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
1826
0
  return Element::CanSkipThis(tmp);
1827
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1828
1829
static const char* kNSURIs[] = {
1830
  "([none])",
1831
  "(xmlns)",
1832
  "(xml)",
1833
  "(xhtml)",
1834
  "(XLink)",
1835
  "(XSLT)",
1836
  "(XBL)",
1837
  "(MathML)",
1838
  "(RDF)",
1839
  "(XUL)"
1840
};
1841
1842
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
1843
0
  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1844
0
    char name[512];
1845
0
    nsAutoCString loadedAsData;
1846
0
    if (tmp->IsLoadedAsData()) {
1847
0
      loadedAsData.AssignLiteral("data");
1848
0
    } else {
1849
0
      loadedAsData.AssignLiteral("normal");
1850
0
    }
1851
0
    uint32_t nsid = tmp->GetDefaultNamespaceID();
1852
0
    nsAutoCString uri;
1853
0
    if (tmp->mDocumentURI)
1854
0
      uri = tmp->mDocumentURI->GetSpecOrDefault();
1855
0
    if (nsid < ArrayLength(kNSURIs)) {
1856
0
      SprintfLiteral(name, "nsDocument %s %s %s",
1857
0
                     loadedAsData.get(), kNSURIs[nsid], uri.get());
1858
0
    }
1859
0
    else {
1860
0
      SprintfLiteral(name, "nsDocument %s %s",
1861
0
                     loadedAsData.get(), uri.get());
1862
0
    }
1863
0
    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1864
0
  }
1865
0
  else {
1866
0
    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
1867
0
  }
1868
0
1869
0
  if (!nsINode::Traverse(tmp, cb)) {
1870
0
    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1871
0
  }
1872
0
1873
0
  if (tmp->mMaybeEndOutermostXBLUpdateRunner) {
1874
0
    // The cached runnable keeps a reference to the document object..
1875
0
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1876
0
                                       "mMaybeEndOutermostXBLUpdateRunner.mObj");
1877
0
    cb.NoteXPCOMChild(ToSupports(tmp));
1878
0
  }
1879
0
1880
0
  for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done();
1881
0
       iter.Next()) {
1882
0
    iter.Get()->Traverse(&cb);
1883
0
  }
1884
0
1885
0
  tmp->mExternalResourceMap.Traverse(&cb);
1886
0
1887
0
  // Traverse all nsIDocument pointer members.
1888
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
1889
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
1890
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
1891
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
1892
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n)
1893
0
1894
0
  // Traverse all nsDocument nsCOMPtrs.
1895
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
1896
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
1897
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1898
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
1899
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
1900
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
1901
0
1902
0
  DocumentOrShadowRoot::Traverse(tmp, cb);
1903
0
1904
0
  // The boxobject for an element will only exist as long as it's in the
1905
0
  // document, so we'll traverse the table here instead of from the element.
1906
0
  if (tmp->mBoxObjectTable) {
1907
0
    for (auto iter = tmp->mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1908
0
      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mBoxObjectTable entry");
1909
0
      cb.NoteXPCOMChild(iter.UserData());
1910
0
    }
1911
0
  }
1912
0
1913
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
1914
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
1915
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
1916
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
1917
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
1918
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
1919
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
1920
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
1921
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
1922
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
1923
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
1924
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
1925
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
1926
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
1927
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages);
1928
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds);
1929
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks);
1930
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms);
1931
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts);
1932
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
1933
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
1934
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
1935
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
1936
0
1937
0
  // Traverse all our nsCOMArrays.
1938
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
1939
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
1940
0
1941
0
  for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
1942
0
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
1943
0
    cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
1944
0
  }
1945
0
1946
0
  // Traverse animation components
1947
0
  if (tmp->mAnimationController) {
1948
0
    tmp->mAnimationController->Traverse(&cb);
1949
0
  }
1950
0
1951
0
  if (tmp->mSubDocuments) {
1952
0
    for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
1953
0
      auto entry = static_cast<SubDocMapEntry*>(iter.Get());
1954
0
1955
0
      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1956
0
                                         "mSubDocuments entry->mKey");
1957
0
      cb.NoteXPCOMChild(entry->mKey);
1958
0
      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1959
0
                                         "mSubDocuments entry->mSubDocument");
1960
0
      cb.NoteXPCOMChild(entry->mSubDocument);
1961
0
    }
1962
0
  }
1963
0
1964
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
1965
0
1966
0
  // We own only the items in mDOMMediaQueryLists that have listeners;
1967
0
  // this reference is managed by their AddListener and RemoveListener
1968
0
  // methods.
1969
0
  for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;
1970
0
       mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
1971
0
    if (mql->HasListeners() &&
1972
0
        NS_SUCCEEDED(mql->CheckInnerWindowCorrectness())) {
1973
0
      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
1974
0
      cb.NoteXPCOMChild(mql);
1975
0
    }
1976
0
  }
1977
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1978
1979
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
1980
1981
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDocument)
1982
1983
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
1984
0
  tmp->mInUnlinkOrDeletion = true;
1985
0
1986
0
  // Clear out our external resources
1987
0
  tmp->mExternalResourceMap.Shutdown();
1988
0
1989
0
  nsAutoScriptBlocker scriptBlocker;
1990
0
1991
0
  nsINode::Unlink(tmp);
1992
0
1993
0
  while (tmp->HasChildren()) {
1994
0
    // Hold a strong ref to the node when we remove it, because we may be
1995
0
    // the last reference to it.
1996
0
    // If this code changes, change the corresponding code in nsDocument's
1997
0
    // unlink impl and ContentUnbinder::UnbindSubtree.
1998
0
    nsCOMPtr<nsIContent> child = tmp->GetLastChild();
1999
0
    tmp->DisconnectChild(child);
2000
0
    child->UnbindFromTree();
2001
0
  }
2002
0
2003
0
  tmp->UnlinkOriginalDocumentIfStatic();
2004
0
2005
0
  tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
2006
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
2007
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
2008
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaybeEndOutermostXBLUpdateRunner)
2009
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
2010
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
2011
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
2012
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
2013
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
2014
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
2015
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
2016
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages);
2017
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds);
2018
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks);
2019
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
2020
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
2021
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
2022
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
2023
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
2024
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
2025
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
2026
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
2027
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n);
2028
0
2029
0
  tmp->mParentDocument = nullptr;
2030
0
2031
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
2032
0
2033
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
2034
0
2035
0
  tmp->ClearAllBoxObjects();
2036
0
2037
0
  if (tmp->mListenerManager) {
2038
0
    tmp->mListenerManager->Disconnect();
2039
0
    tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
2040
0
    tmp->mListenerManager = nullptr;
2041
0
  }
2042
0
2043
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
2044
0
2045
0
  if (tmp->mStyleSheetSetList) {
2046
0
    tmp->mStyleSheetSetList->Disconnect();
2047
0
    tmp->mStyleSheetSetList = nullptr;
2048
0
  }
2049
0
2050
0
  delete tmp->mSubDocuments;
2051
0
  tmp->mSubDocuments = nullptr;
2052
0
2053
0
  tmp->mFrameRequestCallbacks.Clear();
2054
0
  MOZ_RELEASE_ASSERT(!tmp->mFrameRequestCallbacksScheduled,
2055
0
                     "How did we get here without our presshell going away "
2056
0
                     "first?");
2057
0
2058
0
  DocumentOrShadowRoot::Unlink(tmp);
2059
0
2060
0
  // nsDocument has a pretty complex destructor, so we're going to
2061
0
  // assume that *most* cycles you actually want to break somewhere
2062
0
  // else, and not unlink an awful lot here.
2063
0
2064
0
  tmp->mIdentifierMap.Clear();
2065
0
  tmp->mExpandoAndGeneration.OwnerUnlinked();
2066
0
2067
0
  if (tmp->mAnimationController) {
2068
0
    tmp->mAnimationController->Unlink();
2069
0
  }
2070
0
2071
0
  tmp->mPendingTitleChangeEvent.Revoke();
2072
0
2073
0
  if (tmp->mCSSLoader) {
2074
0
    tmp->mCSSLoader->DropDocumentReference();
2075
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
2076
0
  }
2077
0
2078
0
  // We own only the items in mDOMMediaQueryLists that have listeners;
2079
0
  // this reference is managed by their AddListener and RemoveListener
2080
0
  // methods.
2081
0
  for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
2082
0
    MediaQueryList* next =
2083
0
      static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext();
2084
0
    mql->Disconnect();
2085
0
    mql = next;
2086
0
  }
2087
0
2088
0
  tmp->mInUnlinkOrDeletion = false;
2089
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2090
2091
nsresult
2092
nsDocument::Init()
2093
0
{
2094
0
  if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
2095
0
    return NS_ERROR_ALREADY_INITIALIZED;
2096
0
  }
2097
0
2098
0
  // Force initialization.
2099
0
  nsINode::nsSlots* slots = Slots();
2100
0
2101
0
  // Prepend self as mutation-observer whether we need it or not (some
2102
0
  // subclasses currently do, other don't). This is because the code in
2103
0
  // nsNodeUtils always notifies the first observer first, expecting the
2104
0
  // first observer to be the document.
2105
0
  NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
2106
0
                 NS_ERROR_OUT_OF_MEMORY);
2107
0
2108
0
2109
0
  mOnloadBlocker = new nsOnloadBlocker();
2110
0
  mCSSLoader = new mozilla::css::Loader(this);
2111
0
  // Assume we're not quirky, until we know otherwise
2112
0
  mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
2113
0
2114
0
  mStyleImageLoader = new mozilla::css::ImageLoader(this);
2115
0
2116
0
  mNodeInfoManager = new nsNodeInfoManager();
2117
0
  nsresult rv = mNodeInfoManager->Init(this);
2118
0
  NS_ENSURE_SUCCESS(rv, rv);
2119
0
2120
0
  // mNodeInfo keeps NodeInfoManager alive!
2121
0
  mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2122
0
  NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2123
0
  MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE,
2124
0
             "Bad NodeType in aNodeInfo");
2125
0
2126
0
  NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2127
0
2128
0
  // Set this when document is initialized and value stays the same for the
2129
0
  // lifetime of the document.
2130
0
  mIsShadowDOMEnabled = nsContentUtils::IsShadowDOMEnabled() ||
2131
0
    (XRE_IsParentProcess() && AllowXULXBL());
2132
0
2133
0
  // If after creation the owner js global is not set for a document
2134
0
  // we use the default compartment for this document, instead of creating
2135
0
  // wrapper in some random compartment when the document is exposed to js
2136
0
  // via some events.
2137
0
  nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2138
0
  NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2139
0
  mScopeObject = do_GetWeakReference(global);
2140
0
  MOZ_ASSERT(mScopeObject);
2141
0
2142
0
  mScriptLoader = new dom::ScriptLoader(this);
2143
0
2144
0
  mozilla::HoldJSObjects(this);
2145
0
2146
0
  return NS_OK;
2147
0
}
2148
2149
void
2150
nsIDocument::DeleteAllProperties()
2151
0
{
2152
0
  PropertyTable().DeleteAllProperties();
2153
0
}
2154
2155
void
2156
nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
2157
0
{
2158
0
  PropertyTable().DeleteAllPropertiesFor(aNode);
2159
0
}
2160
2161
bool
2162
nsIDocument::IsVisibleConsideringAncestors() const
2163
0
{
2164
0
  const nsIDocument *parent = this;
2165
0
  do {
2166
0
    if (!parent->IsVisible()) {
2167
0
      return false;
2168
0
    }
2169
0
  } while ((parent = parent->GetParentDocument()));
2170
0
2171
0
  return true;
2172
0
      }
2173
2174
void
2175
nsIDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
2176
0
{
2177
0
  nsCOMPtr<nsIURI> uri;
2178
0
  nsCOMPtr<nsIPrincipal> principal;
2179
0
  if (aChannel) {
2180
0
    // Note: this code is duplicated in XULDocument::StartDocumentLoad and
2181
0
    // nsScriptSecurityManager::GetChannelResultPrincipal.
2182
0
    // Note: this should match nsDocShell::OnLoadingSite
2183
0
    NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2184
0
2185
0
    bool isWyciwyg = false;
2186
0
    uri->SchemeIs("wyciwyg", &isWyciwyg);
2187
0
    if (isWyciwyg) {
2188
0
      nsCOMPtr<nsIURI> cleanURI;
2189
0
      nsresult rv =
2190
0
        nsContentUtils::RemoveWyciwygScheme(uri, getter_AddRefs(cleanURI));
2191
0
      if (NS_SUCCEEDED(rv)) {
2192
0
        uri = cleanURI;
2193
0
      }
2194
0
    }
2195
0
2196
0
    nsIScriptSecurityManager *securityManager =
2197
0
      nsContentUtils::GetSecurityManager();
2198
0
    if (securityManager) {
2199
0
      securityManager->GetChannelResultPrincipal(aChannel,
2200
0
                                                 getter_AddRefs(principal));
2201
0
    }
2202
0
  }
2203
0
2204
0
  principal = MaybeDowngradePrincipal(principal);
2205
0
2206
0
  ResetToURI(uri, aLoadGroup, principal);
2207
0
2208
0
  // Note that, since mTiming does not change during a reset, the
2209
0
  // navigationStart time remains unchanged and therefore any future new
2210
0
  // timeline will have the same global clock time as the old one.
2211
0
  mDocumentTimeline = nullptr;
2212
0
2213
0
  nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2214
0
  if (bag) {
2215
0
    nsCOMPtr<nsIURI> baseURI;
2216
0
    bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
2217
0
                                NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
2218
0
    if (baseURI) {
2219
0
      mDocumentBaseURI = baseURI;
2220
0
      mChromeXHRDocBaseURI = nullptr;
2221
0
    }
2222
0
  }
2223
0
2224
0
  mChannel = aChannel;
2225
0
}
2226
2227
/**
2228
 * DocumentL10n is currently allowed for system
2229
 * principal.
2230
 *
2231
 * In the future we'll want to expose it to non-web-exposed
2232
 * about:* pages.
2233
 */
2234
bool
2235
PrincipalAllowsL10n(nsIPrincipal* principal)
2236
0
{
2237
0
  if (nsContentUtils::IsSystemPrincipal(principal)) {
2238
0
    return true;
2239
0
  }
2240
0
2241
0
  return false;
2242
0
}
2243
2244
void
2245
nsIDocument::ResetToURI(nsIURI* aURI,
2246
                        nsILoadGroup* aLoadGroup,
2247
                        nsIPrincipal* aPrincipal)
2248
0
{
2249
0
  MOZ_ASSERT(aURI, "Null URI passed to ResetToURI");
2250
0
2251
0
  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2252
0
          ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
2253
0
2254
0
  mSecurityInfo = nullptr;
2255
0
2256
0
  nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
2257
0
  if (!aLoadGroup || group != aLoadGroup) {
2258
0
    mDocumentLoadGroup = nullptr;
2259
0
  }
2260
0
2261
0
  // Delete references to sub-documents and kill the subdocument map,
2262
0
  // if any. It holds strong references
2263
0
  delete mSubDocuments;
2264
0
  mSubDocuments = nullptr;
2265
0
2266
0
  // Destroy link map now so we don't waste time removing
2267
0
  // links one by one
2268
0
  DestroyElementMaps();
2269
0
2270
0
  bool oldVal = mInUnlinkOrDeletion;
2271
0
  mInUnlinkOrDeletion = true;
2272
0
  { // Scope for update
2273
0
    MOZ_AUTO_DOC_UPDATE(this, true);
2274
0
2275
0
    // Invalidate cached array of child nodes
2276
0
    InvalidateChildNodes();
2277
0
2278
0
    while (HasChildren()) {
2279
0
      nsCOMPtr<nsIContent> content = GetLastChild();
2280
0
      nsIContent* previousSibling = content->GetPreviousSibling();
2281
0
      DisconnectChild(content);
2282
0
      if (content == mCachedRootElement) {
2283
0
        // Immediately clear mCachedRootElement, now that it's been removed
2284
0
        // from mChildren, so that GetRootElement() will stop returning this
2285
0
        // now-stale value.
2286
0
        mCachedRootElement = nullptr;
2287
0
      }
2288
0
      nsNodeUtils::ContentRemoved(this, content, previousSibling);
2289
0
      content->UnbindFromTree();
2290
0
    }
2291
0
    MOZ_ASSERT(!mCachedRootElement,
2292
0
               "After removing all children, there should be no root elem");
2293
0
  }
2294
0
  mInUnlinkOrDeletion = oldVal;
2295
0
2296
0
  // Reset our stylesheets
2297
0
  ResetStylesheetsToURI(aURI);
2298
0
2299
0
  // Release the listener manager
2300
0
  if (mListenerManager) {
2301
0
    mListenerManager->Disconnect();
2302
0
    mListenerManager = nullptr;
2303
0
  }
2304
0
2305
0
  // Release the stylesheets list.
2306
0
  mDOMStyleSheets = nullptr;
2307
0
2308
0
  // Release our principal after tearing down the document, rather than before.
2309
0
  // This ensures that, during teardown, the document and the dying window (which
2310
0
  // already nulled out its document pointer and cached the principal) have
2311
0
  // matching principals.
2312
0
  SetPrincipal(nullptr);
2313
0
2314
0
  // Clear the original URI so SetDocumentURI sets it.
2315
0
  mOriginalURI = nullptr;
2316
0
2317
0
  SetDocumentURI(aURI);
2318
0
  mChromeXHRDocURI = nullptr;
2319
0
  // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
2320
0
  // mDocumentURI.
2321
0
  mDocumentBaseURI = nullptr;
2322
0
  mChromeXHRDocBaseURI = nullptr;
2323
0
2324
0
  if (aLoadGroup) {
2325
0
    mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2326
0
    // there was an assertion here that aLoadGroup was not null.  This
2327
0
    // is no longer valid: nsDocShell::SetDocument does not create a
2328
0
    // load group, and it works just fine
2329
0
2330
0
    // XXXbz what does "just fine" mean exactly?  And given that there
2331
0
    // is no nsDocShell::SetDocument, what is this talking about?
2332
0
2333
0
    if (IsContentDocument()) {
2334
0
      // Inform the associated request context about this load start so
2335
0
      // any of its internal load progress flags gets reset.
2336
0
      nsCOMPtr<nsIRequestContextService> rcsvc =
2337
0
        do_GetService("@mozilla.org/network/request-context-service;1");
2338
0
      if (rcsvc) {
2339
0
        nsCOMPtr<nsIRequestContext> rc;
2340
0
        rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc));
2341
0
        if (rc) {
2342
0
          rc->BeginLoad();
2343
0
        }
2344
0
      }
2345
0
    }
2346
0
  }
2347
0
2348
0
  mLastModified.Truncate();
2349
0
  // XXXbz I guess we're assuming that the caller will either pass in
2350
0
  // a channel with a useful type or call SetContentType?
2351
0
  SetContentTypeInternal(EmptyCString());
2352
0
  mContentLanguage.Truncate();
2353
0
  mBaseTarget.Truncate();
2354
0
  mReferrer.Truncate();
2355
0
2356
0
  mXMLDeclarationBits = 0;
2357
0
2358
0
  // Now get our new principal
2359
0
  if (aPrincipal) {
2360
0
    SetPrincipal(aPrincipal);
2361
0
  } else {
2362
0
    nsIScriptSecurityManager *securityManager =
2363
0
      nsContentUtils::GetSecurityManager();
2364
0
    if (securityManager) {
2365
0
      nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
2366
0
2367
0
      if (!loadContext && aLoadGroup) {
2368
0
        nsCOMPtr<nsIInterfaceRequestor> cbs;
2369
0
        aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2370
0
        loadContext = do_GetInterface(cbs);
2371
0
      }
2372
0
2373
0
      MOZ_ASSERT(loadContext,
2374
0
                 "must have a load context or pass in an explicit principal");
2375
0
2376
0
      nsCOMPtr<nsIPrincipal> principal;
2377
0
      nsresult rv = securityManager->
2378
0
        GetLoadContextCodebasePrincipal(mDocumentURI, loadContext,
2379
0
                                        getter_AddRefs(principal));
2380
0
      if (NS_SUCCEEDED(rv)) {
2381
0
        SetPrincipal(principal);
2382
0
      }
2383
0
    }
2384
0
  }
2385
0
2386
0
  if (mFontFaceSet) {
2387
0
    mFontFaceSet->RefreshStandardFontLoadPrincipal();
2388
0
  }
2389
0
2390
0
  // Refresh the principal on the realm.
2391
0
  if (nsPIDOMWindowInner* win = GetInnerWindow()) {
2392
0
    nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal();
2393
0
  }
2394
0
}
2395
2396
already_AddRefed<nsIPrincipal>
2397
nsIDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal)
2398
0
{
2399
0
  if (!aPrincipal) {
2400
0
    return nullptr;
2401
0
  }
2402
0
2403
0
  // We can't load a document with an expanded principal. If we're given one,
2404
0
  // automatically downgrade it to the last principal it subsumes (which is the
2405
0
  // extension principal, in the case of extension content scripts).
2406
0
  auto* basePrin = BasePrincipal::Cast(aPrincipal);
2407
0
  if (basePrin->Is<ExpandedPrincipal>()) {
2408
0
    MOZ_DIAGNOSTIC_ASSERT(false, "Should never try to create a document with "
2409
0
                                 "an expanded principal");
2410
0
2411
0
    auto* expanded = basePrin->As<ExpandedPrincipal>();
2412
0
    return do_AddRef(expanded->WhiteList().LastElement());
2413
0
  }
2414
0
2415
0
  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
2416
0
    // We basically want the parent document here, but because this is very
2417
0
    // early in the load, GetParentDocument() returns null, so we use the
2418
0
    // docshell hierarchy to get this information instead.
2419
0
    if (mDocumentContainer) {
2420
0
      nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
2421
0
      mDocumentContainer->GetParent(getter_AddRefs(parentDocShellItem));
2422
0
      nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentDocShellItem);
2423
0
      if (parentDocShell) {
2424
0
        nsCOMPtr<nsIDocument> parentDoc;
2425
0
        parentDoc = parentDocShell->GetDocument();
2426
0
        if (!parentDoc ||
2427
0
            !nsContentUtils::IsSystemPrincipal(parentDoc->NodePrincipal())) {
2428
0
          nsCOMPtr<nsIPrincipal> nullPrincipal =
2429
0
            do_CreateInstance("@mozilla.org/nullprincipal;1");
2430
0
          return nullPrincipal.forget();
2431
0
        }
2432
0
      }
2433
0
    }
2434
0
  }
2435
0
  nsCOMPtr<nsIPrincipal> principal(aPrincipal);
2436
0
  return principal.forget();
2437
0
}
2438
2439
void
2440
nsIDocument::RemoveDocStyleSheetsFromStyleSets()
2441
0
{
2442
0
  // The stylesheets should forget us
2443
0
  for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2444
0
    sheet->ClearAssociatedDocumentOrShadowRoot();
2445
0
2446
0
    if (sheet->IsApplicable()) {
2447
0
      nsCOMPtr<nsIPresShell> shell = GetShell();
2448
0
      if (shell) {
2449
0
        shell->StyleSet()->RemoveDocStyleSheet(sheet);
2450
0
      }
2451
0
    }
2452
0
    // XXX Tell observers?
2453
0
  }
2454
0
}
2455
2456
void
2457
nsIDocument::RemoveStyleSheetsFromStyleSets(
2458
    const nsTArray<RefPtr<StyleSheet>>& aSheets,
2459
    SheetType aType)
2460
0
{
2461
0
  // The stylesheets should forget us
2462
0
  for (StyleSheet* sheet : Reversed(aSheets)) {
2463
0
    sheet->ClearAssociatedDocumentOrShadowRoot();
2464
0
2465
0
    if (sheet->IsApplicable()) {
2466
0
      nsCOMPtr<nsIPresShell> shell = GetShell();
2467
0
      if (shell) {
2468
0
        shell->StyleSet()->RemoveStyleSheet(aType, sheet);
2469
0
      }
2470
0
    }
2471
0
    // XXX Tell observers?
2472
0
  }
2473
0
}
2474
2475
void
2476
nsIDocument::ResetStylesheetsToURI(nsIURI* aURI)
2477
0
{
2478
0
  MOZ_ASSERT(aURI);
2479
0
2480
0
  if (mStyleSetFilled) {
2481
0
    // Skip removing style sheets from the style set if we know we haven't
2482
0
    // filled the style set.  (This allows us to avoid calling
2483
0
    // GetStyleBackendType() too early.)
2484
0
    RemoveDocStyleSheetsFromStyleSets();
2485
0
    RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], SheetType::Agent);
2486
0
    RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User);
2487
0
    RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc);
2488
0
2489
0
    if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
2490
0
      RemoveStyleSheetsFromStyleSets(
2491
0
        *sheetService->AuthorStyleSheets(), SheetType::Doc);
2492
0
    }
2493
0
2494
0
    mStyleSetFilled = false;
2495
0
  }
2496
0
2497
0
  // Release all the sheets
2498
0
  mStyleSheets.Clear();
2499
0
  for (auto& sheets : mAdditionalSheets) {
2500
0
    sheets.Clear();
2501
0
  }
2502
0
2503
0
  // NOTE:  We don't release the catalog sheets.  It doesn't really matter
2504
0
  // now, but it could in the future -- in which case not releasing them
2505
0
  // is probably the right thing to do.
2506
0
2507
0
  // Now reset our inline style and attribute sheets.
2508
0
  if (mAttrStyleSheet) {
2509
0
    mAttrStyleSheet->Reset();
2510
0
    mAttrStyleSheet->SetOwningDocument(this);
2511
0
  } else {
2512
0
    mAttrStyleSheet = new nsHTMLStyleSheet(this);
2513
0
  }
2514
0
2515
0
  if (!mStyleAttrStyleSheet) {
2516
0
    mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
2517
0
  }
2518
0
2519
0
  // Now set up our style sets
2520
0
  if (nsIPresShell* shell = GetShell()) {
2521
0
    FillStyleSet(shell->StyleSet());
2522
0
    if (shell->StyleSet()->StyleSheetsHaveChanged()) {
2523
0
      shell->ApplicableStylesChanged();
2524
0
    }
2525
0
  }
2526
0
}
2527
2528
static void
2529
AppendSheetsToStyleSet(ServoStyleSet* aStyleSet,
2530
                       const nsTArray<RefPtr<StyleSheet>>& aSheets,
2531
                       SheetType aType)
2532
0
{
2533
0
  for (StyleSheet* sheet : Reversed(aSheets)) {
2534
0
    aStyleSet->AppendStyleSheet(aType, sheet);
2535
0
  }
2536
0
}
2537
2538
2539
void
2540
nsIDocument::FillStyleSet(ServoStyleSet* aStyleSet)
2541
0
{
2542
0
  MOZ_ASSERT(aStyleSet, "Must have a style set");
2543
0
  MOZ_ASSERT(aStyleSet->SheetCount(SheetType::Doc) == 0,
2544
0
             "Style set already has document sheets?");
2545
0
2546
0
  MOZ_ASSERT(!mStyleSetFilled);
2547
0
2548
0
  for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2549
0
    if (sheet->IsApplicable()) {
2550
0
      aStyleSet->AddDocStyleSheet(sheet, this);
2551
0
    }
2552
0
  }
2553
0
2554
0
  if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
2555
0
    nsTArray<RefPtr<StyleSheet>>& sheets =
2556
0
      *sheetService->AuthorStyleSheets();
2557
0
    for (StyleSheet* sheet : sheets) {
2558
0
      aStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
2559
0
    }
2560
0
  }
2561
0
2562
0
  AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
2563
0
                         SheetType::Agent);
2564
0
  AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
2565
0
                         SheetType::User);
2566
0
  AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
2567
0
                         SheetType::Doc);
2568
0
2569
0
  mStyleSetFilled = true;
2570
0
}
2571
2572
static void
2573
WarnIfSandboxIneffective(nsIDocShell* aDocShell,
2574
                         uint32_t aSandboxFlags,
2575
                         nsIChannel* aChannel)
2576
0
{
2577
0
  // If the document is sandboxed (via the HTML5 iframe sandbox
2578
0
  // attribute) and both the allow-scripts and allow-same-origin
2579
0
  // keywords are supplied, the sandboxed document can call into its
2580
0
  // parent document and remove its sandboxing entirely - we print a
2581
0
  // warning to the web console in this case.
2582
0
  if (aSandboxFlags & SANDBOXED_NAVIGATION &&
2583
0
      !(aSandboxFlags & SANDBOXED_SCRIPTS) &&
2584
0
      !(aSandboxFlags & SANDBOXED_ORIGIN)) {
2585
0
    nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2586
0
    aDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
2587
0
    nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentAsItem);
2588
0
    if (!parentDocShell) {
2589
0
      return;
2590
0
    }
2591
0
2592
0
    // Don't warn if our parent is not the top-level document.
2593
0
    nsCOMPtr<nsIDocShellTreeItem> grandParentAsItem;
2594
0
    parentDocShell->GetSameTypeParent(getter_AddRefs(grandParentAsItem));
2595
0
    if (grandParentAsItem) {
2596
0
      return;
2597
0
    }
2598
0
2599
0
    nsCOMPtr<nsIChannel> parentChannel;
2600
0
    parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
2601
0
    if (!parentChannel) {
2602
0
      return;
2603
0
    }
2604
0
    nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
2605
0
    if (NS_FAILED(rv)) {
2606
0
      return;
2607
0
    }
2608
0
2609
0
    nsCOMPtr<nsIDocument> parentDocument = parentDocShell->GetDocument();
2610
0
    nsCOMPtr<nsIURI> iframeUri;
2611
0
    parentChannel->GetURI(getter_AddRefs(iframeUri));
2612
0
    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2613
0
                                    NS_LITERAL_CSTRING("Iframe Sandbox"),
2614
0
                                    parentDocument,
2615
0
                                    nsContentUtils::eSECURITY_PROPERTIES,
2616
0
                                    "BothAllowScriptsAndSameOriginPresent",
2617
0
                                    nullptr, 0, iframeUri);
2618
0
  }
2619
0
}
2620
2621
bool
2622
0
nsIDocument::IsSynthesized() {
2623
0
  nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->GetLoadInfo() : nullptr;
2624
0
  return loadInfo && loadInfo->GetServiceWorkerTaintingSynthesized();
2625
0
}
2626
2627
bool
2628
nsDocument::IsShadowDOMEnabled(JSContext* aCx, JSObject* aGlobal)
2629
0
{
2630
0
  MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
2631
0
  nsCOMPtr<nsPIDOMWindowInner> window = xpc::WindowOrNull(aGlobal);
2632
0
2633
0
  nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
2634
0
  if (!doc) {
2635
0
    return false;
2636
0
  }
2637
0
2638
0
  return doc->IsShadowDOMEnabled();
2639
0
}
2640
2641
// static
2642
bool
2643
nsDocument::IsShadowDOMEnabledAndCallerIsChromeOrAddon(JSContext* aCx,
2644
                                                       JSObject* aObject)
2645
0
{
2646
0
  if (IsShadowDOMEnabled(aCx, aObject)) {
2647
0
    nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
2648
0
    return principal &&
2649
0
      (nsContentUtils::IsSystemPrincipal(principal) ||
2650
0
       principal->GetIsAddonOrExpandedAddonPrincipal());
2651
0
  }
2652
0
2653
0
  return false;
2654
0
}
2655
2656
bool
2657
nsDocument::IsShadowDOMEnabled(const nsINode* aNode)
2658
0
{
2659
0
  return aNode->OwnerDoc()->IsShadowDOMEnabled();
2660
0
}
2661
2662
nsresult
2663
nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
2664
                              nsILoadGroup* aLoadGroup,
2665
                              nsISupports* aContainer,
2666
                              nsIStreamListener **aDocListener,
2667
                              bool aReset, nsIContentSink* aSink)
2668
0
{
2669
0
  if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
2670
0
    nsCOMPtr<nsIURI> uri;
2671
0
    aChannel->GetURI(getter_AddRefs(uri));
2672
0
    MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2673
0
            ("DOCUMENT %p StartDocumentLoad %s",
2674
0
             this, uri ? uri->GetSpecOrDefault().get() : ""));
2675
0
  }
2676
0
2677
0
  MOZ_ASSERT(NodePrincipal()->GetAppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID,
2678
0
             "Document should never have UNKNOWN_APP_ID");
2679
0
2680
0
  MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
2681
0
             "Bad readyState");
2682
0
  SetReadyStateInternal(READYSTATE_LOADING);
2683
0
2684
0
  if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2685
0
    mLoadedAsData = true;
2686
0
    // We need to disable script & style loading in this case.
2687
0
    // We leave them disabled even in EndLoad(), and let anyone
2688
0
    // who puts the document on display to worry about enabling.
2689
0
2690
0
    // Do not load/process scripts when loading as data
2691
0
    ScriptLoader()->SetEnabled(false);
2692
0
2693
0
    // styles
2694
0
    CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
2695
0
  } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2696
0
    // Allow CSS, but not scripts
2697
0
    ScriptLoader()->SetEnabled(false);
2698
0
  }
2699
0
2700
0
  mMayStartLayout = false;
2701
0
  MOZ_ASSERT(!mReadyForIdle, "We should never hit DOMContentLoaded before this point");
2702
0
2703
0
  if (aReset) {
2704
0
    Reset(aChannel, aLoadGroup);
2705
0
  }
2706
0
2707
0
  nsAutoCString contentType;
2708
0
  nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2709
0
  if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
2710
0
                NS_LITERAL_STRING("contentType"), contentType))) ||
2711
0
      NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2712
0
    // XXX this is only necessary for viewsource:
2713
0
    nsACString::const_iterator start, end, semicolon;
2714
0
    contentType.BeginReading(start);
2715
0
    contentType.EndReading(end);
2716
0
    semicolon = start;
2717
0
    FindCharInReadable(';', semicolon, end);
2718
0
    SetContentTypeInternal(Substring(start, semicolon));
2719
0
  }
2720
0
2721
0
  RetrieveRelevantHeaders(aChannel);
2722
0
2723
0
  mChannel = aChannel;
2724
0
  nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
2725
0
  if (inStrmChan) {
2726
0
    bool isSrcdocChannel;
2727
0
    inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
2728
0
    if (isSrcdocChannel) {
2729
0
      mIsSrcdocDocument = true;
2730
0
    }
2731
0
  }
2732
0
2733
0
  if (mChannel) {
2734
0
    nsLoadFlags loadFlags;
2735
0
    mChannel->GetLoadFlags(&loadFlags);
2736
0
    bool isDocument = false;
2737
0
    mChannel->GetIsDocument(&isDocument);
2738
0
    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
2739
0
        isDocument &&
2740
0
        IsSynthesized() &&
2741
0
        XRE_IsContentProcess()) {
2742
0
      ContentChild::UpdateCookieStatus(mChannel);
2743
0
    }
2744
0
  }
2745
0
2746
0
  // If this document is being loaded by a docshell, copy its sandbox flags
2747
0
  // to the document, and store the fullscreen enabled flag. These are
2748
0
  // immutable after being set here.
2749
0
  nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
2750
0
2751
0
  // If this is an error page, don't inherit sandbox flags from docshell
2752
0
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2753
0
  if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) {
2754
0
    nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
2755
0
    NS_ENSURE_SUCCESS(rv, rv);
2756
0
    WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
2757
0
  }
2758
0
2759
0
  // The CSP directive upgrade-insecure-requests not only applies to the
2760
0
  // toplevel document, but also to nested documents. Let's propagate that
2761
0
  // flag from the parent to the nested document.
2762
0
  nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell();
2763
0
  if (treeItem) {
2764
0
    nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
2765
0
    treeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
2766
0
    if (sameTypeParent) {
2767
0
      nsIDocument* doc = sameTypeParent->GetDocument();
2768
0
      mBlockAllMixedContent = doc->GetBlockAllMixedContent(false);
2769
0
      // if the parent document makes use of block-all-mixed-content
2770
0
      // then subdocument preloads should always be blocked.
2771
0
      mBlockAllMixedContentPreloads =
2772
0
        mBlockAllMixedContent || doc->GetBlockAllMixedContent(true);
2773
0
2774
0
      mUpgradeInsecureRequests = doc->GetUpgradeInsecureRequests(false);
2775
0
      // if the parent document makes use of upgrade-insecure-requests
2776
0
      // then subdocument preloads should always be upgraded.
2777
0
      mUpgradeInsecurePreloads =
2778
0
        mUpgradeInsecureRequests || doc->GetUpgradeInsecureRequests(true);
2779
0
    }
2780
0
  }
2781
0
2782
0
  // If this is not a data document, set CSP.
2783
0
  if (!mLoadedAsData) {
2784
0
    nsresult rv = InitCSP(aChannel);
2785
0
    NS_ENSURE_SUCCESS(rv, rv);
2786
0
  }
2787
0
2788
0
  // XFO needs to be checked after CSP because it is ignored if
2789
0
  // the CSP defines frame-ancestors.
2790
0
  if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
2791
0
    MOZ_LOG(gCspPRLog, LogLevel::Debug,
2792
0
            ("XFO doesn't like frame's ancestry, not loading."));
2793
0
    // stop!  ERROR page!
2794
0
    aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2795
0
  }
2796
0
2797
0
  // Perform a async flash classification based on the doc principal
2798
0
  // in an early stage to reduce the blocking time.
2799
0
  mFlashClassification = FlashClassification::Unclassified;
2800
0
  mPrincipalFlashClassifier->AsyncClassify(GetPrincipal());
2801
0
2802
0
  return NS_OK;
2803
0
}
2804
2805
void
2806
nsIDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
2807
0
{
2808
0
  for (uint32_t i = 0; i < aMessages.Length(); ++i) {
2809
0
    nsAutoString messageTag;
2810
0
    aMessages[i]->GetTag(messageTag);
2811
0
2812
0
    nsAutoString category;
2813
0
    aMessages[i]->GetCategory(category);
2814
0
2815
0
    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2816
0
                                    NS_ConvertUTF16toUTF8(category),
2817
0
                                    this, nsContentUtils::eSECURITY_PROPERTIES,
2818
0
                                    NS_ConvertUTF16toUTF8(messageTag).get());
2819
0
  }
2820
0
}
2821
2822
void
2823
nsIDocument::ApplySettingsFromCSP(bool aSpeculative)
2824
0
{
2825
0
  nsresult rv = NS_OK;
2826
0
  if (!aSpeculative) {
2827
0
    // 1) apply settings from regular CSP
2828
0
    nsCOMPtr<nsIContentSecurityPolicy> csp;
2829
0
    rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
2830
0
    NS_ENSURE_SUCCESS_VOID(rv);
2831
0
    if (csp) {
2832
0
      // Set up 'block-all-mixed-content' if not already inherited
2833
0
      // from the parent context or set by any other CSP.
2834
0
      if (!mBlockAllMixedContent) {
2835
0
        rv = csp->GetBlockAllMixedContent(&mBlockAllMixedContent);
2836
0
        NS_ENSURE_SUCCESS_VOID(rv);
2837
0
      }
2838
0
      if (!mBlockAllMixedContentPreloads) {
2839
0
        mBlockAllMixedContentPreloads = mBlockAllMixedContent;
2840
0
     }
2841
0
2842
0
      // Set up 'upgrade-insecure-requests' if not already inherited
2843
0
      // from the parent context or set by any other CSP.
2844
0
      if (!mUpgradeInsecureRequests) {
2845
0
        rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
2846
0
        NS_ENSURE_SUCCESS_VOID(rv);
2847
0
      }
2848
0
      if (!mUpgradeInsecurePreloads) {
2849
0
        mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
2850
0
      }
2851
0
    }
2852
0
    return;
2853
0
  }
2854
0
2855
0
  // 2) apply settings from speculative csp
2856
0
  nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
2857
0
  rv = NodePrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
2858
0
  NS_ENSURE_SUCCESS_VOID(rv);
2859
0
  if (preloadCsp) {
2860
0
    if (!mBlockAllMixedContentPreloads) {
2861
0
      rv = preloadCsp->GetBlockAllMixedContent(&mBlockAllMixedContentPreloads);
2862
0
      NS_ENSURE_SUCCESS_VOID(rv);
2863
0
    }
2864
0
    if (!mUpgradeInsecurePreloads) {
2865
0
      rv = preloadCsp->GetUpgradeInsecureRequests(&mUpgradeInsecurePreloads);
2866
0
      NS_ENSURE_SUCCESS_VOID(rv);
2867
0
    }
2868
0
  }
2869
0
}
2870
2871
nsresult
2872
nsIDocument::InitCSP(nsIChannel* aChannel)
2873
0
{
2874
0
  MOZ_ASSERT(!mScriptGlobalObject,
2875
0
             "CSP must be initialized before mScriptGlobalObject is set!");
2876
0
  if (!StaticPrefs::security_csp_enable()) {
2877
0
    MOZ_LOG(gCspPRLog, LogLevel::Debug,
2878
0
           ("CSP is disabled, skipping CSP init for document %p", this));
2879
0
    return NS_OK;
2880
0
  }
2881
0
2882
0
  nsAutoCString tCspHeaderValue, tCspROHeaderValue;
2883
0
2884
0
  nsCOMPtr<nsIHttpChannel> httpChannel;
2885
0
  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
2886
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2887
0
    return rv;
2888
0
  }
2889
0
2890
0
  if (httpChannel) {
2891
0
    Unused << httpChannel->GetResponseHeader(
2892
0
        NS_LITERAL_CSTRING("content-security-policy"),
2893
0
        tCspHeaderValue);
2894
0
2895
0
    Unused << httpChannel->GetResponseHeader(
2896
0
        NS_LITERAL_CSTRING("content-security-policy-report-only"),
2897
0
        tCspROHeaderValue);
2898
0
  }
2899
0
  NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
2900
0
  NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
2901
0
2902
0
  // Check if this is a document from a WebExtension.
2903
0
  nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
2904
0
  auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
2905
0
2906
0
  // Check if this is a signed content to apply default CSP.
2907
0
  bool applySignedContentCSP = false;
2908
0
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2909
0
  if (loadInfo && loadInfo->GetVerifySignedContent()) {
2910
0
    applySignedContentCSP = true;
2911
0
  }
2912
0
2913
0
  // If there's no CSP to apply, go ahead and return early
2914
0
  if (!addonPolicy &&
2915
0
      !applySignedContentCSP &&
2916
0
      cspHeaderValue.IsEmpty() &&
2917
0
      cspROHeaderValue.IsEmpty()) {
2918
0
    if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
2919
0
      nsCOMPtr<nsIURI> chanURI;
2920
0
      aChannel->GetURI(getter_AddRefs(chanURI));
2921
0
      nsAutoCString aspec;
2922
0
      chanURI->GetAsciiSpec(aspec);
2923
0
      MOZ_LOG(gCspPRLog, LogLevel::Debug,
2924
0
             ("no CSP for document, %s",
2925
0
              aspec.get()));
2926
0
    }
2927
0
2928
0
    return NS_OK;
2929
0
  }
2930
0
2931
0
  MOZ_LOG(gCspPRLog, LogLevel::Debug, ("Document is an add-on or CSP header specified %p", this));
2932
0
2933
0
  nsCOMPtr<nsIContentSecurityPolicy> csp;
2934
0
  rv = principal->EnsureCSP(static_cast<nsDocument*>(this), getter_AddRefs(csp));
2935
0
  NS_ENSURE_SUCCESS(rv, rv);
2936
0
2937
0
  // ----- if the doc is an addon, apply its CSP.
2938
0
  if (addonPolicy) {
2939
0
    nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
2940
0
2941
0
    nsAutoString addonCSP;
2942
0
    Unused << ExtensionPolicyService::GetSingleton().GetBaseCSP(addonCSP);
2943
0
    csp->AppendPolicy(addonCSP, false, false);
2944
0
2945
0
    csp->AppendPolicy(addonPolicy->ContentSecurityPolicy(), false, false);
2946
0
  }
2947
0
2948
0
  // ----- if the doc is a signed content, apply the default CSP.
2949
0
  // Note that when the content signing becomes a standard, we might have
2950
0
  // to restrict this enforcement to "remote content" only.
2951
0
  if (applySignedContentCSP) {
2952
0
    nsAutoString signedContentCSP;
2953
0
    Preferences::GetString("security.signed_content.CSP.default",
2954
0
                           signedContentCSP);
2955
0
    csp->AppendPolicy(signedContentCSP, false, false);
2956
0
  }
2957
0
2958
0
  // ----- if there's a full-strength CSP header, apply it.
2959
0
  if (!cspHeaderValue.IsEmpty()) {
2960
0
    rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
2961
0
    NS_ENSURE_SUCCESS(rv, rv);
2962
0
  }
2963
0
2964
0
  // ----- if there's a report-only CSP header, apply it.
2965
0
  if (!cspROHeaderValue.IsEmpty()) {
2966
0
    rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
2967
0
    NS_ENSURE_SUCCESS(rv, rv);
2968
0
  }
2969
0
2970
0
  // ----- Enforce sandbox policy if supplied in CSP header
2971
0
  // The document may already have some sandbox flags set (e.g. if the document
2972
0
  // is an iframe with the sandbox attribute set). If we have a CSP sandbox
2973
0
  // directive, intersect the CSP sandbox flags with the existing flags. This
2974
0
  // corresponds to the _least_ permissive policy.
2975
0
  uint32_t cspSandboxFlags = SANDBOXED_NONE;
2976
0
  rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
2977
0
  NS_ENSURE_SUCCESS(rv, rv);
2978
0
2979
0
  // Probably the iframe sandbox attribute already caused the creation of a
2980
0
  // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
2981
0
  // and no one has been created yet.
2982
0
  bool needNewNullPrincipal =
2983
0
    (cspSandboxFlags & SANDBOXED_ORIGIN) && !(mSandboxFlags & SANDBOXED_ORIGIN);
2984
0
2985
0
  mSandboxFlags |= cspSandboxFlags;
2986
0
2987
0
  if (needNewNullPrincipal) {
2988
0
    principal = NullPrincipal::CreateWithInheritedAttributes(principal);
2989
0
    principal->SetCsp(csp);
2990
0
    SetPrincipal(principal);
2991
0
  }
2992
0
2993
0
  // ----- Enforce frame-ancestor policy on any applied policies
2994
0
  nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2995
0
  if (docShell) {
2996
0
    bool safeAncestry = false;
2997
0
2998
0
    // PermitsAncestry sends violation reports when necessary
2999
0
    rv = csp->PermitsAncestry(docShell, &safeAncestry);
3000
0
3001
0
    if (NS_FAILED(rv) || !safeAncestry) {
3002
0
      MOZ_LOG(gCspPRLog, LogLevel::Debug,
3003
0
              ("CSP doesn't like frame's ancestry, not loading."));
3004
0
      // stop!  ERROR page!
3005
0
      aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
3006
0
    }
3007
0
  }
3008
0
  ApplySettingsFromCSP(false);
3009
0
  return NS_OK;
3010
0
}
3011
3012
void
3013
nsDocument::StopDocumentLoad()
3014
0
{
3015
0
  if (mParser) {
3016
0
    mParserAborted = true;
3017
0
    mParser->Terminate();
3018
0
  }
3019
0
}
3020
3021
void
3022
nsIDocument::SetDocumentURI(nsIURI* aURI)
3023
0
{
3024
0
  nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
3025
0
  mDocumentURI = aURI;
3026
0
  nsIURI* newBase = GetDocBaseURI();
3027
0
3028
0
  bool equalBases = false;
3029
0
  // Changing just the ref of a URI does not change how relative URIs would
3030
0
  // resolve wrt to it, so we can treat the bases as equal as long as they're
3031
0
  // equal ignoring the ref.
3032
0
  if (oldBase && newBase) {
3033
0
    oldBase->EqualsExceptRef(newBase, &equalBases);
3034
0
  }
3035
0
  else {
3036
0
    equalBases = !oldBase && !newBase;
3037
0
  }
3038
0
3039
0
  // If this is the first time we're setting the document's URI, set the
3040
0
  // document's original URI.
3041
0
  if (!mOriginalURI)
3042
0
    mOriginalURI = mDocumentURI;
3043
0
3044
0
  // If changing the document's URI changed the base URI of the document, we
3045
0
  // need to refresh the hrefs of all the links on the page.
3046
0
  if (!equalBases) {
3047
0
    RefreshLinkHrefs();
3048
0
  }
3049
0
}
3050
3051
static void
3052
GetFormattedTimeString(PRTime aTime, nsAString& aFormattedTimeString)
3053
0
{
3054
0
  PRExplodedTime prtime;
3055
0
  PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime);
3056
0
  // "MM/DD/YYYY hh:mm:ss"
3057
0
  char formatedTime[24];
3058
0
  if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
3059
0
                     prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
3060
0
                     prtime.tm_hour     ,  prtime.tm_min,  prtime.tm_sec)) {
3061
0
    CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
3062
0
  } else {
3063
0
    // If we for whatever reason failed to find the last modified time
3064
0
    // (or even the current time), fall back to what NS4.x returned.
3065
0
    aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
3066
0
  }
3067
0
}
3068
3069
void
3070
nsIDocument::GetLastModified(nsAString& aLastModified) const
3071
0
{
3072
0
  if (!mLastModified.IsEmpty()) {
3073
0
    aLastModified.Assign(mLastModified);
3074
0
  } else {
3075
0
    GetFormattedTimeString(PR_Now(), aLastModified);
3076
0
  }
3077
0
}
3078
3079
static void
3080
IncrementExpandoGeneration(nsIDocument& aDoc)
3081
0
{
3082
0
  ++static_cast<nsDocument&>(aDoc).mExpandoAndGeneration.generation;
3083
0
}
3084
3085
void
3086
nsIDocument::AddToNameTable(Element* aElement, nsAtom* aName)
3087
0
{
3088
0
  MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
3089
0
             "Only put elements that need to be exposed as document['name'] in "
3090
0
             "the named table.");
3091
0
3092
0
  nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
3093
0
3094
0
  // Null for out-of-memory
3095
0
  if (entry) {
3096
0
    if (!entry->HasNameElement() &&
3097
0
        !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3098
0
      IncrementExpandoGeneration(*this);
3099
0
    }
3100
0
    entry->AddNameElement(this, aElement);
3101
0
  }
3102
0
}
3103
3104
void
3105
nsIDocument::RemoveFromNameTable(Element* aElement, nsAtom* aName)
3106
0
{
3107
0
  // Speed up document teardown
3108
0
  if (mIdentifierMap.Count() == 0)
3109
0
    return;
3110
0
3111
0
  nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
3112
0
  if (!entry) // Could be false if the element was anonymous, hence never added
3113
0
    return;
3114
0
3115
0
  entry->RemoveNameElement(aElement);
3116
0
  if (!entry->HasNameElement() &&
3117
0
      !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3118
0
    IncrementExpandoGeneration(*this);
3119
0
  }
3120
0
}
3121
3122
void
3123
nsIDocument::AddToIdTable(Element* aElement, nsAtom* aId)
3124
0
{
3125
0
  nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
3126
0
3127
0
  if (entry) { /* True except on OOM */
3128
0
    if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3129
0
        !entry->HasNameElement() &&
3130
0
        !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3131
0
      IncrementExpandoGeneration(*this);
3132
0
    }
3133
0
    entry->AddIdElement(aElement);
3134
0
  }
3135
0
}
3136
3137
void
3138
nsIDocument::RemoveFromIdTable(Element* aElement, nsAtom* aId)
3139
0
{
3140
0
  NS_ASSERTION(aId, "huhwhatnow?");
3141
0
3142
0
  // Speed up document teardown
3143
0
  if (mIdentifierMap.Count() == 0) {
3144
0
    return;
3145
0
  }
3146
0
3147
0
  nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
3148
0
  if (!entry) // Can be null for XML elements with changing ids.
3149
0
    return;
3150
0
3151
0
  entry->RemoveIdElement(aElement);
3152
0
  if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3153
0
      !entry->HasNameElement() &&
3154
0
      !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3155
0
    IncrementExpandoGeneration(*this);
3156
0
  }
3157
0
  if (entry->IsEmpty()) {
3158
0
    mIdentifierMap.RemoveEntry(entry);
3159
0
  }
3160
0
}
3161
3162
nsIPrincipal*
3163
nsDocument::GetPrincipal()
3164
0
{
3165
0
  return NodePrincipal();
3166
0
}
3167
3168
extern bool sDisablePrefetchHTTPSPref;
3169
3170
void
3171
nsIDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
3172
0
{
3173
0
  if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
3174
0
    nsCOMPtr<nsIURI> uri;
3175
0
    aNewPrincipal->GetURI(getter_AddRefs(uri));
3176
0
    bool isHTTPS;
3177
0
    if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
3178
0
        isHTTPS) {
3179
0
      mAllowDNSPrefetch = false;
3180
0
    }
3181
0
  }
3182
0
  mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
3183
0
3184
#ifdef DEBUG
3185
  // Validate that the docgroup is set correctly by calling its getter and
3186
  // triggering its sanity check.
3187
  //
3188
  // If we're setting the principal to null, we don't want to perform the check,
3189
  // as the document is entering an intermediate state where it does not have a
3190
  // principal. It will be given another real principal shortly which we will
3191
  // check. It's not unsafe to have a document which has a null principal in the
3192
  // same docgroup as another document, so this should not be a problem.
3193
  if (aNewPrincipal) {
3194
    GetDocGroup();
3195
  }
3196
#endif
3197
}
3198
3199
mozilla::dom::DocGroup*
3200
nsIDocument::GetDocGroup() const
3201
0
{
3202
#ifdef DEBUG
3203
  // Sanity check that we have an up-to-date and accurate docgroup
3204
  if (mDocGroup) {
3205
    nsAutoCString docGroupKey;
3206
3207
    // GetKey() can fail, e.g. after the TLD service has shut down.
3208
    nsresult rv = mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
3209
    if (NS_SUCCEEDED(rv)) {
3210
      MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
3211
    }
3212
    // XXX: Check that the TabGroup is correct as well!
3213
  }
3214
#endif
3215
3216
0
  return mDocGroup;
3217
0
}
3218
3219
nsresult
3220
nsIDocument::Dispatch(TaskCategory aCategory,
3221
                      already_AddRefed<nsIRunnable>&& aRunnable)
3222
0
{
3223
0
  // Note that this method may be called off the main thread.
3224
0
  if (mDocGroup) {
3225
0
    return mDocGroup->Dispatch(aCategory, std::move(aRunnable));
3226
0
  }
3227
0
  return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
3228
0
}
3229
3230
nsISerialEventTarget*
3231
nsIDocument::EventTargetFor(TaskCategory aCategory) const
3232
0
{
3233
0
  if (mDocGroup) {
3234
0
    return mDocGroup->EventTargetFor(aCategory);
3235
0
  }
3236
0
  return DispatcherTrait::EventTargetFor(aCategory);
3237
0
}
3238
3239
AbstractThread*
3240
nsIDocument::AbstractMainThreadFor(mozilla::TaskCategory aCategory)
3241
0
{
3242
0
  MOZ_ASSERT(NS_IsMainThread());
3243
0
  if (mDocGroup) {
3244
0
    return mDocGroup->AbstractMainThreadFor(aCategory);
3245
0
  }
3246
0
  return DispatcherTrait::AbstractMainThreadFor(aCategory);
3247
0
}
3248
3249
void
3250
nsIDocument::NoteScriptTrackingStatus(const nsACString& aURL, bool aIsTracking)
3251
0
{
3252
0
  if (aIsTracking) {
3253
0
    mTrackingScripts.PutEntry(aURL);
3254
0
  } else {
3255
0
    MOZ_ASSERT(!mTrackingScripts.Contains(aURL));
3256
0
  }
3257
0
}
3258
3259
bool
3260
nsIDocument::IsScriptTracking(const nsACString& aURL) const
3261
0
{
3262
0
  return mTrackingScripts.Contains(aURL);
3263
0
}
3264
3265
NS_IMETHODIMP
3266
nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
3267
0
{
3268
0
  NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3269
0
3270
0
  return NS_OK;
3271
0
}
3272
3273
NS_IMETHODIMP
3274
nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
3275
0
{
3276
0
  mApplicationCache = aApplicationCache;
3277
0
3278
0
  return NS_OK;
3279
0
}
3280
3281
void
3282
nsIDocument::GetContentType(nsAString& aContentType)
3283
0
{
3284
0
  CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
3285
0
}
3286
3287
void
3288
nsIDocument::SetContentType(const nsAString& aContentType)
3289
0
{
3290
0
  SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
3291
0
}
3292
3293
bool
3294
nsIDocument::GetAllowPlugins()
3295
0
{
3296
0
  // First, we ask our docshell if it allows plugins.
3297
0
  nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3298
0
3299
0
  if (docShell) {
3300
0
    bool allowPlugins = false;
3301
0
    docShell->GetAllowPlugins(&allowPlugins);
3302
0
    if (!allowPlugins) {
3303
0
      return false;
3304
0
    }
3305
0
3306
0
    // If the docshell allows plugins, we check whether
3307
0
    // we are sandboxed and plugins should not be allowed.
3308
0
    if (mSandboxFlags & SANDBOXED_PLUGINS) {
3309
0
      return false;
3310
0
    }
3311
0
  }
3312
0
3313
0
  FlashClassification classification = DocumentFlashClassification();
3314
0
  if (classification == FlashClassification::Denied) {
3315
0
    return false;
3316
0
  }
3317
0
3318
0
  return true;
3319
0
}
3320
3321
void
3322
nsIDocument::InitializeLocalization(nsTArray<nsString>& aResourceIds)
3323
0
{
3324
0
  MOZ_ASSERT(!mDocumentL10n, "mDocumentL10n should not be initialized yet");
3325
0
3326
0
  DocumentL10n* l10n = new DocumentL10n(this);
3327
0
  MOZ_ALWAYS_TRUE(l10n->Init(aResourceIds));
3328
0
  mDocumentL10n = l10n;
3329
0
}
3330
3331
DocumentL10n*
3332
nsIDocument::GetL10n()
3333
0
{
3334
0
  return mDocumentL10n;
3335
0
}
3336
3337
bool
3338
nsDocument::DocumentSupportsL10n(JSContext* aCx, JSObject* aObject)
3339
0
{
3340
0
  return PrincipalAllowsL10n(nsContentUtils::SubjectPrincipal(aCx));
3341
0
}
3342
3343
void
3344
nsIDocument::LocalizationLinkAdded(Element* aLinkElement)
3345
0
{
3346
0
  if (!PrincipalAllowsL10n(NodePrincipal())) {
3347
0
    return;
3348
0
  }
3349
0
3350
0
  nsAutoString href;
3351
0
  aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
3352
0
  // If the link is added after the DocumentL10n instance
3353
0
  // has been initialized, just pass the resource ID to it.
3354
0
  if (mDocumentL10n) {
3355
0
    AutoTArray<nsString, 1> resourceIds;
3356
0
    resourceIds.AppendElement(href);
3357
0
    mDocumentL10n->AddResourceIds(resourceIds);
3358
0
  } else if (mReadyState >= READYSTATE_INTERACTIVE) {
3359
0
    // Otherwise, if the document has already been parsed
3360
0
    // we need to lazily initialize the localization.
3361
0
    AutoTArray<nsString, 1> resourceIds;
3362
0
    resourceIds.AppendElement(href);
3363
0
    InitializeLocalization(resourceIds);
3364
0
    mDocumentL10n->TriggerInitialDocumentTranslation();
3365
0
  } else {
3366
0
    // Otherwise, we're still parsing the document.
3367
0
    // In that case, add it to the pending list. This list
3368
0
    // will be resolved once the end of l10n resource
3369
0
    // container is reached.
3370
0
    mL10nResources.AppendElement(href);
3371
0
  }
3372
0
}
3373
3374
void
3375
nsIDocument::LocalizationLinkRemoved(Element* aLinkElement)
3376
0
{
3377
0
  if (!PrincipalAllowsL10n(NodePrincipal())) {
3378
0
    return;
3379
0
  }
3380
0
3381
0
  nsAutoString href;
3382
0
  aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
3383
0
  if (mDocumentL10n) {
3384
0
    AutoTArray<nsString, 1> resourceIds;
3385
0
    resourceIds.AppendElement(href);
3386
0
    uint32_t remaining = mDocumentL10n->RemoveResourceIds(resourceIds);
3387
0
    if (remaining == 0) {
3388
0
      mDocumentL10n = nullptr;
3389
0
    }
3390
0
  } else {
3391
0
    mL10nResources.RemoveElement(href);
3392
0
  }
3393
0
}
3394
3395
/**
3396
 * This method should be called once the end of the l10n
3397
 * resource container has been parsed.
3398
 *
3399
 * In XUL this is the end of the first </linkset>,
3400
 * In XHTML/HTML this is the end of </head>.
3401
 *
3402
 * This milestone is used to allow for batch
3403
 * localization context I/O and building done
3404
 * once when all resources in the document have been
3405
 * collected.
3406
 */
3407
void
3408
nsIDocument::OnL10nResourceContainerParsed()
3409
0
{
3410
0
  if (!mL10nResources.IsEmpty()) {
3411
0
    InitializeLocalization(mL10nResources);
3412
0
    mL10nResources.Clear();
3413
0
  }
3414
0
}
3415
3416
void
3417
nsIDocument::TriggerInitialDocumentTranslation()
3418
0
{
3419
0
  if (mDocumentL10n) {
3420
0
    mDocumentL10n->TriggerInitialDocumentTranslation();
3421
0
  }
3422
0
}
3423
3424
bool
3425
nsDocument::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/)
3426
0
{
3427
0
  MOZ_ASSERT(NS_IsMainThread());
3428
0
3429
0
  return nsContentUtils::IsSystemCaller(aCx) ||
3430
0
         nsContentUtils::AnimationsAPICoreEnabled();
3431
0
}
3432
3433
bool
3434
nsDocument::IsWebAnimationsEnabled(CallerType aCallerType)
3435
0
{
3436
0
  MOZ_ASSERT(NS_IsMainThread());
3437
0
3438
0
  return aCallerType == dom::CallerType::System ||
3439
0
         nsContentUtils::AnimationsAPICoreEnabled();
3440
0
}
3441
3442
bool
3443
nsDocument::IsWebAnimationsGetAnimationsEnabled(JSContext* aCx,
3444
                                                JSObject* /*unused*/
3445
)
3446
0
{
3447
0
  MOZ_ASSERT(NS_IsMainThread());
3448
0
3449
0
  return nsContentUtils::IsSystemCaller(aCx) ||
3450
0
         StaticPrefs::dom_animations_api_getAnimations_enabled();
3451
0
}
3452
3453
bool
3454
nsDocument::AreWebAnimationsImplicitKeyframesEnabled(JSContext* aCx,
3455
                                                     JSObject* /*unused*/
3456
)
3457
0
{
3458
0
  MOZ_ASSERT(NS_IsMainThread());
3459
0
3460
0
  return nsContentUtils::IsSystemCaller(aCx) ||
3461
0
         StaticPrefs::dom_animations_api_implicit_keyframes_enabled();
3462
0
}
3463
3464
bool
3465
nsDocument::AreWebAnimationsTimelinesEnabled(JSContext* aCx,
3466
                                             JSObject* /*unused*/
3467
)
3468
0
{
3469
0
  MOZ_ASSERT(NS_IsMainThread());
3470
0
3471
0
  return nsContentUtils::IsSystemCaller(aCx) ||
3472
0
         StaticPrefs::dom_animations_api_timelines_enabled();
3473
0
}
3474
3475
DocumentTimeline*
3476
nsIDocument::Timeline()
3477
0
{
3478
0
  if (!mDocumentTimeline) {
3479
0
    mDocumentTimeline = new DocumentTimeline(this, TimeDuration(0));
3480
0
  }
3481
0
3482
0
  return mDocumentTimeline;
3483
0
}
3484
3485
void
3486
nsIDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
3487
0
{
3488
0
  // Hold a strong ref for the root element since Element::GetAnimations() calls
3489
0
  // FlushPendingNotifications() which may destroy the element.
3490
0
  RefPtr<Element> root = GetRootElement();
3491
0
  if (!root) {
3492
0
    return;
3493
0
  }
3494
0
  AnimationFilter filter;
3495
0
  filter.mSubtree = true;
3496
0
  root->GetAnimations(filter, aAnimations);
3497
0
}
3498
3499
SVGSVGElement*
3500
nsIDocument::GetSVGRootElement() const
3501
0
{
3502
0
  Element* root = GetRootElement();
3503
0
  if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {
3504
0
    return nullptr;
3505
0
  }
3506
0
  return static_cast<SVGSVGElement*>(root);
3507
0
}
3508
3509
/* Return true if the document is in the focused top-level window, and is an
3510
 * ancestor of the focused DOMWindow. */
3511
bool
3512
nsIDocument::HasFocus(ErrorResult& rv) const
3513
0
{
3514
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3515
0
  if (!fm) {
3516
0
    rv.Throw(NS_ERROR_NOT_AVAILABLE);
3517
0
    return false;
3518
0
  }
3519
0
3520
0
  // Is there a focused DOMWindow?
3521
0
  nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
3522
0
  fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3523
0
  if (!focusedWindow) {
3524
0
    return false;
3525
0
  }
3526
0
3527
0
  nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(focusedWindow);
3528
0
3529
0
  // Are we an ancestor of the focused DOMWindow?
3530
0
  for (nsIDocument* currentDoc = piWindow->GetDoc(); currentDoc;
3531
0
       currentDoc = currentDoc->GetParentDocument()) {
3532
0
    if (currentDoc == this) {
3533
0
      // Yes, we are an ancestor
3534
0
      return true;
3535
0
    }
3536
0
  }
3537
0
3538
0
  return false;
3539
0
}
3540
3541
TimeStamp
3542
nsIDocument::LastFocusTime() const
3543
0
{
3544
0
  return mLastFocusTime;
3545
0
}
3546
3547
void
3548
nsIDocument::SetLastFocusTime(const TimeStamp& aFocusTime)
3549
0
{
3550
0
  MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull());
3551
0
  MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() ||
3552
0
                        aFocusTime >= mLastFocusTime);
3553
0
  mLastFocusTime = aFocusTime;
3554
0
}
3555
3556
void
3557
nsIDocument::GetReferrer(nsAString& aReferrer) const
3558
0
{
3559
0
  if (mIsSrcdocDocument && mParentDocument)
3560
0
      mParentDocument->GetReferrer(aReferrer);
3561
0
  else
3562
0
    CopyUTF8toUTF16(mReferrer, aReferrer);
3563
0
}
3564
3565
nsresult
3566
nsIDocument::GetSrcdocData(nsAString &aSrcdocData)
3567
0
{
3568
0
  if (mIsSrcdocDocument) {
3569
0
    nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3570
0
    if (inStrmChan) {
3571
0
      return inStrmChan->GetSrcdocData(aSrcdocData);
3572
0
    }
3573
0
  }
3574
0
  aSrcdocData = VoidString();
3575
0
  return NS_OK;
3576
0
}
3577
3578
Element*
3579
nsIDocument::GetActiveElement()
3580
0
{
3581
0
  // Get the focused element.
3582
0
  Element* focusedElement = GetRetargetedFocusedElement();
3583
0
  if (focusedElement) {
3584
0
    return focusedElement;
3585
0
  }
3586
0
3587
0
  // No focused element anywhere in this document.  Try to get the BODY.
3588
0
  if (IsHTMLOrXHTML()) {
3589
0
    // Because of IE compatibility, return null when html document doesn't have
3590
0
    // a body.
3591
0
    return AsHTMLDocument()->GetBody();
3592
0
  }
3593
0
3594
0
  // If we couldn't get a BODY, return the root element.
3595
0
  return GetDocumentElement();
3596
0
}
3597
3598
Element*
3599
nsIDocument::GetCurrentScript()
3600
0
{
3601
0
  nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
3602
0
  return el;
3603
0
}
3604
3605
nsresult
3606
nsIDocument::NodesFromRectHelper(float aX, float aY,
3607
                                 float aTopSize, float aRightSize,
3608
                                 float aBottomSize, float aLeftSize,
3609
                                 bool aIgnoreRootScrollFrame,
3610
                                 bool aFlushLayout,
3611
                                 nsINodeList** aReturn)
3612
0
{
3613
0
  NS_ENSURE_ARG_POINTER(aReturn);
3614
0
3615
0
  nsSimpleContentList* elements = new nsSimpleContentList(this);
3616
0
  NS_ADDREF(elements);
3617
0
  *aReturn = elements;
3618
0
3619
0
  // Following the same behavior of elementFromPoint,
3620
0
  // we don't return anything if either coord is negative
3621
0
  if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0))
3622
0
    return NS_OK;
3623
0
3624
0
  nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
3625
0
  nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
3626
0
  nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
3627
0
  nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
3628
0
3629
0
  nsRect rect(x, y, w, h);
3630
0
3631
0
  // Make sure the layout information we get is up-to-date, and
3632
0
  // ensure we get a root frame (for everything but XUL)
3633
0
  if (aFlushLayout) {
3634
0
    FlushPendingNotifications(FlushType::Layout);
3635
0
  }
3636
0
3637
0
  nsIPresShell *ps = GetShell();
3638
0
  NS_ENSURE_STATE(ps);
3639
0
  nsIFrame *rootFrame = ps->GetRootFrame();
3640
0
3641
0
  // XUL docs, unlike HTML, have no frame tree until everything's done loading
3642
0
  if (!rootFrame)
3643
0
    return NS_OK; // return nothing to premature XUL callers as a reminder to wait
3644
0
3645
0
  AutoTArray<nsIFrame*,8> outFrames;
3646
0
  nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
3647
0
    nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3648
0
    (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3649
0
3650
0
  // Used to filter out repeated elements in sequence.
3651
0
  nsIContent* lastAdded = nullptr;
3652
0
3653
0
  for (uint32_t i = 0; i < outFrames.Length(); i++) {
3654
0
    nsIContent* node = GetContentInThisDocument(outFrames[i]);
3655
0
3656
0
    if (node && !node->IsElement() && !node->IsText()) {
3657
0
      // We have a node that isn't an element or a text node,
3658
0
      // use its parent content instead.
3659
0
      node = node->GetParent();
3660
0
    }
3661
0
    if (node && node != lastAdded) {
3662
0
      elements->AppendElement(node);
3663
0
      lastAdded = node;
3664
0
    }
3665
0
  }
3666
0
3667
0
  return NS_OK;
3668
0
}
3669
3670
void
3671
nsIDocument::ReleaseCapture() const
3672
0
{
3673
0
  // only release the capture if the caller can access it. This prevents a
3674
0
  // page from stopping a scrollbar grab for example.
3675
0
  nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
3676
0
  if (node && nsContentUtils::CanCallerAccess(node)) {
3677
0
    nsIPresShell::SetCapturingContent(nullptr, 0);
3678
0
  }
3679
0
}
3680
3681
already_AddRefed<nsIURI>
3682
nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const
3683
0
{
3684
0
  nsCOMPtr<nsIURI> uri;
3685
0
  if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
3686
0
    uri = mChromeXHRDocBaseURI;
3687
0
  } else {
3688
0
    uri = GetDocBaseURI();
3689
0
  }
3690
0
3691
0
  return uri.forget();
3692
0
}
3693
3694
void
3695
nsIDocument::SetBaseURI(nsIURI* aURI)
3696
0
{
3697
0
  if (!aURI && !mDocumentBaseURI) {
3698
0
    return;
3699
0
  }
3700
0
3701
0
  // Don't do anything if the URI wasn't actually changed.
3702
0
  if (aURI && mDocumentBaseURI) {
3703
0
    bool equalBases = false;
3704
0
    mDocumentBaseURI->Equals(aURI, &equalBases);
3705
0
    if (equalBases) {
3706
0
      return;
3707
0
    }
3708
0
  }
3709
0
3710
0
  mDocumentBaseURI = aURI;
3711
0
  RefreshLinkHrefs();
3712
0
}
3713
3714
URLExtraData*
3715
nsIDocument::DefaultStyleAttrURLData()
3716
0
{
3717
0
  MOZ_ASSERT(NS_IsMainThread());
3718
0
  nsIURI* baseURI = GetDocBaseURI();
3719
0
  nsIURI* docURI = GetDocumentURI();
3720
0
  nsIPrincipal* principal = NodePrincipal();
3721
0
  mozilla::net::ReferrerPolicy policy = GetReferrerPolicy();
3722
0
  if (!mCachedURLData ||
3723
0
      mCachedURLData->BaseURI() != baseURI ||
3724
0
      mCachedURLData->GetReferrer() != docURI ||
3725
0
      mCachedURLData->GetReferrerPolicy() != policy ||
3726
0
      mCachedURLData->GetPrincipal() != principal) {
3727
0
    mCachedURLData = new URLExtraData(baseURI, docURI, principal, policy);
3728
0
  }
3729
0
  return mCachedURLData;
3730
0
}
3731
3732
void
3733
nsIDocument::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding)
3734
0
{
3735
0
  if (mCharacterSet != aEncoding) {
3736
0
    mCharacterSet = aEncoding;
3737
0
    mEncodingMenuDisabled = aEncoding == UTF_8_ENCODING;
3738
0
3739
0
    if (nsPresContext* context = GetPresContext()) {
3740
0
      context->DispatchCharSetChange(aEncoding);
3741
0
    }
3742
0
  }
3743
0
}
3744
3745
void
3746
nsIDocument::GetSandboxFlagsAsString(nsAString& aFlags)
3747
0
{
3748
0
  nsContentUtils::SandboxFlagsToString(mSandboxFlags, aFlags);
3749
0
}
3750
3751
void
3752
nsIDocument::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const
3753
0
{
3754
0
  aData.Truncate();
3755
0
  const nsDocHeaderData* data = mHeaderData;
3756
0
  while (data) {
3757
0
    if (data->mField == aHeaderField) {
3758
0
      aData = data->mData;
3759
0
3760
0
      break;
3761
0
    }
3762
0
    data = data->mNext;
3763
0
  }
3764
0
}
3765
3766
void
3767
nsIDocument::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData)
3768
0
{
3769
0
  if (!aHeaderField) {
3770
0
    NS_ERROR("null headerField");
3771
0
    return;
3772
0
  }
3773
0
3774
0
  if (!mHeaderData) {
3775
0
    if (!aData.IsEmpty()) { // don't bother storing empty string
3776
0
      mHeaderData = new nsDocHeaderData(aHeaderField, aData);
3777
0
    }
3778
0
  }
3779
0
  else {
3780
0
    nsDocHeaderData* data = mHeaderData;
3781
0
    nsDocHeaderData** lastPtr = &mHeaderData;
3782
0
    bool found = false;
3783
0
    do {  // look for existing and replace
3784
0
      if (data->mField == aHeaderField) {
3785
0
        if (!aData.IsEmpty()) {
3786
0
          data->mData.Assign(aData);
3787
0
        }
3788
0
        else {  // don't store empty string
3789
0
          *lastPtr = data->mNext;
3790
0
          data->mNext = nullptr;
3791
0
          delete data;
3792
0
        }
3793
0
        found = true;
3794
0
3795
0
        break;
3796
0
      }
3797
0
      lastPtr = &(data->mNext);
3798
0
      data = *lastPtr;
3799
0
    } while (data);
3800
0
3801
0
    if (!aData.IsEmpty() && !found) {
3802
0
      // didn't find, append
3803
0
      *lastPtr = new nsDocHeaderData(aHeaderField, aData);
3804
0
    }
3805
0
  }
3806
0
3807
0
  if (aHeaderField == nsGkAtoms::headerContentLanguage) {
3808
0
    CopyUTF16toUTF8(aData, mContentLanguage);
3809
0
  }
3810
0
3811
0
  if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
3812
0
    SetPreferredStyleSheetSet(aData);
3813
0
  }
3814
0
3815
0
  if (aHeaderField == nsGkAtoms::refresh) {
3816
0
    // We get into this code before we have a script global yet, so get to
3817
0
    // our container via mDocumentContainer.
3818
0
    nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
3819
0
    if (refresher) {
3820
0
      // Note: using mDocumentURI instead of mBaseURI here, for consistency
3821
0
      // (used to just use the current URI of our webnavigation, but that
3822
0
      // should really be the same thing).  Note that this code can run
3823
0
      // before the current URI of the webnavigation has been updated, so we
3824
0
      // can't assert equality here.
3825
0
      refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
3826
0
                                           NS_ConvertUTF16toUTF8(aData));
3827
0
    }
3828
0
  }
3829
0
3830
0
  if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
3831
0
      mAllowDNSPrefetch) {
3832
0
    // Chromium treats any value other than 'on' (case insensitive) as 'off'.
3833
0
    mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
3834
0
  }
3835
0
3836
0
  if (aHeaderField == nsGkAtoms::viewport ||
3837
0
      aHeaderField == nsGkAtoms::handheldFriendly ||
3838
0
      aHeaderField == nsGkAtoms::viewport_minimum_scale ||
3839
0
      aHeaderField == nsGkAtoms::viewport_maximum_scale ||
3840
0
      aHeaderField == nsGkAtoms::viewport_initial_scale ||
3841
0
      aHeaderField == nsGkAtoms::viewport_height ||
3842
0
      aHeaderField == nsGkAtoms::viewport_width ||
3843
0
      aHeaderField ==  nsGkAtoms::viewport_user_scalable) {
3844
0
    mViewportType = Unknown;
3845
0
    mViewportOverflowType = ViewportOverflowType::NoOverflow;
3846
0
  }
3847
0
3848
0
  // Referrer policy spec says to ignore any empty referrer policies.
3849
0
  if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
3850
0
     enum mozilla::net::ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
3851
0
    // If policy is not the empty string, then set element's node document's
3852
0
    // referrer policy to policy
3853
0
    if (policy != mozilla::net::RP_Unset) {
3854
0
      // Referrer policy spec (section 6.1) says that we always use the newest
3855
0
      // referrer policy we find
3856
0
      mReferrerPolicy = policy;
3857
0
      mReferrerPolicySet = true;
3858
0
    }
3859
0
  }
3860
0
3861
0
  if (aHeaderField == nsGkAtoms::headerReferrerPolicy && !aData.IsEmpty()) {
3862
0
     enum mozilla::net::ReferrerPolicy policy = nsContentUtils::GetReferrerPolicyFromHeader(aData);
3863
0
    if (policy != mozilla::net::RP_Unset) {
3864
0
      mReferrerPolicy = policy;
3865
0
      mReferrerPolicySet = true;
3866
0
    }
3867
0
  }
3868
0
3869
0
}
3870
void
3871
nsDocument::TryChannelCharset(nsIChannel *aChannel,
3872
                              int32_t& aCharsetSource,
3873
                              NotNull<const Encoding*>& aEncoding,
3874
                              nsHtml5TreeOpExecutor* aExecutor)
3875
0
{
3876
0
  if (aChannel) {
3877
0
    nsAutoCString charsetVal;
3878
0
    nsresult rv = aChannel->GetContentCharset(charsetVal);
3879
0
    if (NS_SUCCEEDED(rv)) {
3880
0
      const Encoding* preferred = Encoding::ForLabel(charsetVal);
3881
0
      if (preferred) {
3882
0
        aEncoding = WrapNotNull(preferred);
3883
0
        aCharsetSource = kCharsetFromChannel;
3884
0
        return;
3885
0
      } else if (aExecutor && !charsetVal.IsEmpty()) {
3886
0
        aExecutor->ComplainAboutBogusProtocolCharset(this);
3887
0
      }
3888
0
    }
3889
0
  }
3890
0
}
3891
3892
static inline void
3893
AssertNoStaleServoDataIn(const nsINode& aSubtreeRoot)
3894
0
{
3895
#ifdef DEBUG
3896
  for (const nsINode* node = &aSubtreeRoot;
3897
       node;
3898
       node = node->GetNextNode(&aSubtreeRoot)) {
3899
    const Element* element = Element::FromNode(node);
3900
    if (!element) {
3901
      continue;
3902
    }
3903
    MOZ_ASSERT(!element->HasServoData());
3904
    if (auto* shadow = element->GetShadowRoot()) {
3905
      AssertNoStaleServoDataIn(*shadow);
3906
    }
3907
    if (nsXBLBinding* binding = element->GetXBLBinding()) {
3908
      if (nsXBLBinding* bindingWithContent = binding->GetBindingWithContent()) {
3909
        nsIContent* content = bindingWithContent->GetAnonymousContent();
3910
        MOZ_ASSERT(!content->AsElement()->HasServoData());
3911
        for (nsINode* child = content->GetFirstChild();
3912
             child;
3913
             child = child->GetNextSibling()) {
3914
          AssertNoStaleServoDataIn(*child);
3915
        }
3916
      }
3917
    }
3918
  }
3919
#endif
3920
}
3921
3922
already_AddRefed<nsIPresShell>
3923
nsIDocument::CreateShell(nsPresContext* aContext,
3924
                         nsViewManager* aViewManager,
3925
                         UniquePtr<ServoStyleSet> aStyleSet)
3926
0
{
3927
0
  NS_ASSERTION(!mPresShell, "We have a presshell already!");
3928
0
3929
0
  NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
3930
0
3931
0
  FillStyleSet(aStyleSet.get());
3932
0
  AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
3933
0
3934
0
  RefPtr<PresShell> shell = new PresShell;
3935
0
  // Note: we don't hold a ref to the shell (it holds a ref to us)
3936
0
  mPresShell = shell;
3937
0
  shell->Init(this, aContext, aViewManager, std::move(aStyleSet));
3938
0
3939
0
  // Make sure to never paint if we belong to an invisible DocShell.
3940
0
  nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3941
0
  if (docShell && docShell->IsInvisible())
3942
0
    shell->SetNeverPainting(true);
3943
0
3944
0
  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p with PressShell %p and DocShell %p",
3945
0
                                                this, shell.get(), docShell.get()));
3946
0
3947
0
  mExternalResourceMap.ShowViewers();
3948
0
3949
0
  UpdateFrameRequestCallbackSchedulingState();
3950
0
3951
0
  // Now that we have a shell, we might have @font-face rules (the presence of a
3952
0
  // shell may change which rules apply to us). We don't need to do anything
3953
0
  // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
3954
0
  // is ready to update we'll flush the font set.
3955
0
  MarkUserFontSetDirty();
3956
0
3957
0
  return shell.forget();
3958
0
}
3959
3960
void
3961
nsIDocument::UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell)
3962
0
{
3963
0
  // If the condition for shouldBeScheduled changes to depend on some other
3964
0
  // variable, add UpdateFrameRequestCallbackSchedulingState() calls to the
3965
0
  // places where that variable can change.
3966
0
  bool shouldBeScheduled =
3967
0
    mPresShell && IsEventHandlingEnabled() && !mFrameRequestCallbacks.IsEmpty();
3968
0
  if (shouldBeScheduled == mFrameRequestCallbacksScheduled) {
3969
0
    // nothing to do
3970
0
    return;
3971
0
  }
3972
0
3973
0
  nsIPresShell* presShell = aOldShell ? aOldShell : mPresShell;
3974
0
  MOZ_RELEASE_ASSERT(presShell);
3975
0
3976
0
  nsRefreshDriver* rd = presShell->GetPresContext()->RefreshDriver();
3977
0
  if (shouldBeScheduled) {
3978
0
    rd->ScheduleFrameRequestCallbacks(this);
3979
0
  } else {
3980
0
    rd->RevokeFrameRequestCallbacks(this);
3981
0
  }
3982
0
3983
0
  mFrameRequestCallbacksScheduled = shouldBeScheduled;
3984
0
}
3985
3986
void
3987
nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
3988
0
{
3989
0
  aCallbacks.AppendElements(mFrameRequestCallbacks);
3990
0
  mFrameRequestCallbacks.Clear();
3991
0
  // No need to manually remove ourselves from the refresh driver; it will
3992
0
  // handle that part.  But we do have to update our state.
3993
0
  mFrameRequestCallbacksScheduled = false;
3994
0
}
3995
3996
bool
3997
nsIDocument::ShouldThrottleFrameRequests()
3998
0
{
3999
0
  if (mStaticCloneCount > 0) {
4000
0
    // Even if we're not visible, a static clone may be, so run at full speed.
4001
0
    return false;
4002
0
  }
4003
0
4004
0
  if (Hidden()) {
4005
0
    // We're not visible (probably in a background tab or the bf cache).
4006
0
    return true;
4007
0
  }
4008
0
4009
0
  if (!mPresShell) {
4010
0
    return false;  // Can't do anything smarter.
4011
0
  }
4012
0
4013
0
  nsIFrame* frame = mPresShell->GetRootFrame();
4014
0
  if (!frame) {
4015
0
    return false;  // Can't do anything smarter.
4016
0
  }
4017
0
4018
0
  nsIFrame* displayRootFrame = nsLayoutUtils::GetDisplayRootFrame(frame);
4019
0
  if (!displayRootFrame) {
4020
0
    return false;  // Can't do anything smarter.
4021
0
  }
4022
0
4023
0
  if (!displayRootFrame->DidPaintPresShell(mPresShell)) {
4024
0
    // We didn't get painted during the last paint, so we're not visible.
4025
0
    // Throttle. Note that because we have to paint this document at least
4026
0
    // once to unthrottle it, we will drop one requestAnimationFrame frame
4027
0
    // when a document that previously wasn't visible scrolls into view. This
4028
0
    // is acceptable since it would happen outside the viewport on APZ
4029
0
    // platforms and is unlikely to be human-perceivable on non-APZ platforms.
4030
0
    return true;
4031
0
  }
4032
0
4033
0
  // We got painted during the last paint, so run at full speed.
4034
0
  return false;
4035
0
}
4036
4037
void
4038
nsIDocument::DeleteShell()
4039
0
{
4040
0
  mExternalResourceMap.HideViewers();
4041
0
  if (nsPresContext* presContext = mPresShell->GetPresContext()) {
4042
0
    presContext->RefreshDriver()->CancelPendingFullscreenEvents(this);
4043
0
  }
4044
0
4045
0
  // When our shell goes away, request that all our images be immediately
4046
0
  // discarded, so we don't carry around decoded image data for a document we
4047
0
  // no longer intend to paint.
4048
0
  ImageTracker()->RequestDiscardAll();
4049
0
4050
0
  // Now that we no longer have a shell, we need to forget about any FontFace
4051
0
  // objects for @font-face rules that came from the style set. There's no need
4052
0
  // to call EnsureStyleFlush either, the shell is going away anyway, so there's
4053
0
  // no point on it.
4054
0
  MarkUserFontSetDirty();
4055
0
4056
0
  nsIPresShell* oldShell = mPresShell;
4057
0
  mPresShell = nullptr;
4058
0
  UpdateFrameRequestCallbackSchedulingState(oldShell);
4059
0
  mStyleSetFilled = false;
4060
0
4061
0
  ClearStaleServoData();
4062
0
  AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
4063
0
}
4064
4065
void
4066
nsIDocument::SetBFCacheEntry(nsIBFCacheEntry* aEntry)
4067
0
{
4068
0
  MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!");
4069
0
4070
0
  if (mPresShell) {
4071
0
    if (aEntry) {
4072
0
      mPresShell->StopObservingRefreshDriver();
4073
0
    } else if (mBFCacheEntry) {
4074
0
      mPresShell->StartObservingRefreshDriver();
4075
0
    }
4076
0
  }
4077
0
  mBFCacheEntry = aEntry;
4078
0
}
4079
4080
static void
4081
SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
4082
0
{
4083
0
  SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
4084
0
4085
0
  NS_RELEASE(e->mKey);
4086
0
  if (e->mSubDocument) {
4087
0
    e->mSubDocument->SetParentDocument(nullptr);
4088
0
    NS_RELEASE(e->mSubDocument);
4089
0
  }
4090
0
}
4091
4092
static void
4093
SubDocInitEntry(PLDHashEntryHdr *entry, const void *key)
4094
0
{
4095
0
  SubDocMapEntry *e =
4096
0
    const_cast<SubDocMapEntry *>
4097
0
              (static_cast<const SubDocMapEntry *>(entry));
4098
0
4099
0
  e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
4100
0
  NS_ADDREF(e->mKey);
4101
0
4102
0
  e->mSubDocument = nullptr;
4103
0
}
4104
4105
nsresult
4106
nsIDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
4107
0
{
4108
0
  NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
4109
0
4110
0
  if (!aSubDoc) {
4111
0
    // aSubDoc is nullptr, remove the mapping
4112
0
4113
0
    if (mSubDocuments) {
4114
0
      nsIDocument* subDoc = GetSubDocumentFor(aElement);
4115
0
      if (subDoc) {
4116
0
        subDoc->SetAllowPaymentRequest(false);
4117
0
      }
4118
0
      mSubDocuments->Remove(aElement);
4119
0
    }
4120
0
  } else {
4121
0
    if (!mSubDocuments) {
4122
0
      // Create a new hashtable
4123
0
4124
0
      static const PLDHashTableOps hash_table_ops =
4125
0
      {
4126
0
        PLDHashTable::HashVoidPtrKeyStub,
4127
0
        PLDHashTable::MatchEntryStub,
4128
0
        PLDHashTable::MoveEntryStub,
4129
0
        SubDocClearEntry,
4130
0
        SubDocInitEntry
4131
0
      };
4132
0
4133
0
      mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
4134
0
    }
4135
0
4136
0
    // Add a mapping to the hash table
4137
0
    auto entry =
4138
0
      static_cast<SubDocMapEntry*>(mSubDocuments->Add(aElement, fallible));
4139
0
4140
0
    if (!entry) {
4141
0
      return NS_ERROR_OUT_OF_MEMORY;
4142
0
    }
4143
0
4144
0
    if (entry->mSubDocument) {
4145
0
      entry->mSubDocument->SetAllowPaymentRequest(false);
4146
0
      entry->mSubDocument->SetParentDocument(nullptr);
4147
0
4148
0
      // Release the old sub document
4149
0
      NS_RELEASE(entry->mSubDocument);
4150
0
    }
4151
0
4152
0
    entry->mSubDocument = aSubDoc;
4153
0
    NS_ADDREF(entry->mSubDocument);
4154
0
4155
0
    // set allowpaymentrequest for the binding subdocument
4156
0
    if (!mAllowPaymentRequest) {
4157
0
      aSubDoc->SetAllowPaymentRequest(false);
4158
0
    } else {
4159
0
      nsresult rv = nsContentUtils::CheckSameOrigin(aElement, aSubDoc);
4160
0
      if (NS_SUCCEEDED(rv)) {
4161
0
        aSubDoc->SetAllowPaymentRequest(true);
4162
0
      } else {
4163
0
        if (aElement->IsHTMLElement(nsGkAtoms::iframe) &&
4164
0
            aElement->GetBoolAttr(nsGkAtoms::allowpaymentrequest)) {
4165
0
          aSubDoc->SetAllowPaymentRequest(true);
4166
0
        } else {
4167
0
          aSubDoc->SetAllowPaymentRequest(false);
4168
0
        }
4169
0
      }
4170
0
    }
4171
0
4172
0
    aSubDoc->SetParentDocument(this);
4173
0
  }
4174
0
4175
0
  return NS_OK;
4176
0
}
4177
4178
nsIDocument*
4179
nsIDocument::GetSubDocumentFor(nsIContent *aContent) const
4180
0
{
4181
0
  if (mSubDocuments && aContent->IsElement()) {
4182
0
    auto entry = static_cast<SubDocMapEntry*>
4183
0
                            (mSubDocuments->Search(aContent->AsElement()));
4184
0
4185
0
    if (entry) {
4186
0
      return entry->mSubDocument;
4187
0
    }
4188
0
  }
4189
0
4190
0
  return nullptr;
4191
0
}
4192
4193
Element*
4194
nsIDocument::FindContentForSubDocument(nsIDocument *aDocument) const
4195
0
{
4196
0
  NS_ENSURE_TRUE(aDocument, nullptr);
4197
0
4198
0
  if (!mSubDocuments) {
4199
0
    return nullptr;
4200
0
  }
4201
0
4202
0
  for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
4203
0
    auto entry = static_cast<SubDocMapEntry*>(iter.Get());
4204
0
    if (entry->mSubDocument == aDocument) {
4205
0
      return entry->mKey;
4206
0
    }
4207
0
  }
4208
0
  return nullptr;
4209
0
}
4210
4211
bool
4212
nsIDocument::IsNodeOfType(uint32_t aFlags) const
4213
0
{
4214
0
  return false;
4215
0
}
4216
4217
Element*
4218
nsIDocument::GetRootElement() const
4219
0
{
4220
0
  return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
4221
0
         mCachedRootElement : GetRootElementInternal();
4222
0
}
4223
4224
nsIContent*
4225
nsIDocument::GetUnfocusedKeyEventTarget()
4226
0
{
4227
0
  return GetRootElement();
4228
0
}
4229
4230
Element*
4231
nsIDocument::GetRootElementInternal() const
4232
0
{
4233
0
  // We invoke GetRootElement() immediately before the servo traversal, so we
4234
0
  // should always have a cache hit from Servo.
4235
0
  MOZ_ASSERT(NS_IsMainThread());
4236
0
4237
0
  // Loop backwards because any non-elements, such as doctypes and PIs
4238
0
  // are likely to appear before the root element.
4239
0
  for (nsIContent* child = GetLastChild(); child;
4240
0
       child = child->GetPreviousSibling()) {
4241
0
    if (Element* element = Element::FromNode(child)) {
4242
0
      const_cast<nsIDocument*>(this)->mCachedRootElement = element;
4243
0
      return element;
4244
0
    }
4245
0
  }
4246
0
4247
0
  const_cast<nsIDocument*>(this)->mCachedRootElement = nullptr;
4248
0
  return nullptr;
4249
0
}
4250
4251
nsresult
4252
nsIDocument::InsertChildBefore(nsIContent* aKid,
4253
                               nsIContent* aBeforeThis,
4254
                               bool aNotify)
4255
0
{
4256
0
  if (aKid->IsElement() && GetRootElement()) {
4257
0
    NS_WARNING("Inserting root element when we already have one");
4258
0
    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
4259
0
  }
4260
0
4261
0
  return nsINode::InsertChildBefore(aKid, aBeforeThis, aNotify);
4262
0
}
4263
4264
void
4265
nsIDocument::RemoveChildNode(nsIContent* aKid, bool aNotify)
4266
0
{
4267
0
  if (aKid->IsElement()) {
4268
0
    // Destroy the link map up front before we mess with the child list.
4269
0
    DestroyElementMaps();
4270
0
  }
4271
0
4272
0
  // Preemptively clear mCachedRootElement, since we may be about to remove it
4273
0
  // from our child list, and we don't want to return this maybe-obsolete value
4274
0
  // from any GetRootElement() calls that happen inside of RemoveChildNode().
4275
0
  // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any
4276
0
  // GetRootElement() calls until after it's removed the child from mChildren.
4277
0
  // Any call before that point would restore this soon-to-be-obsolete cached
4278
0
  // answer, and our clearing here would be fruitless.)
4279
0
  mCachedRootElement = nullptr;
4280
0
  nsINode::RemoveChildNode(aKid, aNotify);
4281
0
  MOZ_ASSERT(mCachedRootElement != aKid,
4282
0
             "Stale pointer in mCachedRootElement, after we tried to clear it "
4283
0
             "(maybe somebody called GetRootElement() too early?)");
4284
0
}
4285
4286
void
4287
nsIDocument::AddStyleSheetToStyleSets(StyleSheet* aSheet)
4288
0
{
4289
0
  if (nsIPresShell* shell = GetShell()) {
4290
0
    shell->StyleSet()->AddDocStyleSheet(aSheet, this);
4291
0
    shell->ApplicableStylesChanged();
4292
0
  }
4293
0
}
4294
4295
#define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName)      \
4296
0
  do {                                                                        \
4297
0
    className##Init init;                                                     \
4298
0
    init.mBubbles = true;                                                     \
4299
0
    init.mCancelable = true;                                                  \
4300
0
    init.mStylesheet = aSheet;                                                \
4301
0
    init.memberName = argName;                                                \
4302
0
                                                                              \
4303
0
    RefPtr<className> event =                                                 \
4304
0
      className::Constructor(this, NS_LITERAL_STRING(type), init);            \
4305
0
    event->SetTrusted(true);                                                  \
4306
0
    event->SetTarget(this);                                                   \
4307
0
    RefPtr<AsyncEventDispatcher> asyncDispatcher =                            \
4308
0
      new AsyncEventDispatcher(this, event);                                  \
4309
0
    asyncDispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes;           \
4310
0
    asyncDispatcher->PostDOMEvent();                                          \
4311
0
  } while (0);
4312
4313
void
4314
nsIDocument::NotifyStyleSheetAdded(StyleSheet* aSheet, bool aDocumentSheet)
4315
0
{
4316
0
  if (StyleSheetChangeEventsEnabled()) {
4317
0
    DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4318
0
                               "StyleSheetAdded",
4319
0
                               mDocumentSheet,
4320
0
                               aDocumentSheet);
4321
0
  }
4322
0
}
4323
4324
void
4325
nsIDocument::NotifyStyleSheetRemoved(StyleSheet* aSheet, bool aDocumentSheet)
4326
0
{
4327
0
  if (StyleSheetChangeEventsEnabled()) {
4328
0
    DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4329
0
                               "StyleSheetRemoved",
4330
0
                               mDocumentSheet,
4331
0
                               aDocumentSheet);
4332
0
  }
4333
0
}
4334
4335
void
4336
nsIDocument::RemoveStyleSheetFromStyleSets(StyleSheet* aSheet)
4337
0
{
4338
0
  if (nsIPresShell* shell = GetShell()) {
4339
0
    shell->StyleSet()->RemoveDocStyleSheet(aSheet);
4340
0
    shell->ApplicableStylesChanged();
4341
0
  }
4342
0
}
4343
4344
void
4345
nsIDocument::RemoveStyleSheet(StyleSheet* aSheet)
4346
0
{
4347
0
  MOZ_ASSERT(aSheet);
4348
0
  RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet);
4349
0
4350
0
  if (!sheet) {
4351
0
    NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
4352
0
    return;
4353
0
  }
4354
0
4355
0
  if (!mIsGoingAway) {
4356
0
    if (sheet->IsApplicable()) {
4357
0
      RemoveStyleSheetFromStyleSets(sheet);
4358
0
    }
4359
0
4360
0
    NotifyStyleSheetRemoved(sheet, true);
4361
0
  }
4362
0
4363
0
  sheet->ClearAssociatedDocumentOrShadowRoot();
4364
0
}
4365
4366
void
4367
nsIDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets,
4368
                               nsTArray<RefPtr<StyleSheet>>& aNewSheets)
4369
0
{
4370
0
  // XXX Need to set the sheet on the ownernode, if any
4371
0
  MOZ_ASSERT(aOldSheets.Length() == aNewSheets.Length(),
4372
0
             "The lists must be the same length!");
4373
0
  int32_t count = aOldSheets.Length();
4374
0
4375
0
  RefPtr<StyleSheet> oldSheet;
4376
0
  int32_t i;
4377
0
  for (i = 0; i < count; ++i) {
4378
0
    oldSheet = aOldSheets[i];
4379
0
4380
0
    // First remove the old sheet.
4381
0
    NS_ASSERTION(oldSheet, "None of the old sheets should be null");
4382
0
    int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
4383
0
    RemoveStyleSheet(oldSheet);  // This does the right notifications
4384
0
4385
0
    // Now put the new one in its place.  If it's null, just ignore it.
4386
0
    StyleSheet* newSheet = aNewSheets[i];
4387
0
    if (newSheet) {
4388
0
      DocumentOrShadowRoot::InsertSheetAt(oldIndex, *newSheet);
4389
0
      if (newSheet->IsApplicable()) {
4390
0
        AddStyleSheetToStyleSets(newSheet);
4391
0
      }
4392
0
4393
0
      NotifyStyleSheetAdded(newSheet, true);
4394
0
    }
4395
0
  }
4396
0
}
4397
4398
void
4399
nsIDocument::InsertSheetAt(size_t aIndex, StyleSheet& aSheet)
4400
0
{
4401
0
  DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
4402
0
4403
0
  if (aSheet.IsApplicable()) {
4404
0
    AddStyleSheetToStyleSets(&aSheet);
4405
0
  }
4406
0
4407
0
  NotifyStyleSheetAdded(&aSheet, true);
4408
0
}
4409
4410
4411
void
4412
nsIDocument::SetStyleSheetApplicableState(StyleSheet* aSheet, bool aApplicable)
4413
0
{
4414
0
  MOZ_ASSERT(aSheet, "null arg");
4415
0
4416
0
  // If we're actually in the document style sheet list
4417
0
  if (mStyleSheets.IndexOf(aSheet) != mStyleSheets.NoIndex) {
4418
0
    if (aApplicable) {
4419
0
      AddStyleSheetToStyleSets(aSheet);
4420
0
    } else {
4421
0
      RemoveStyleSheetFromStyleSets(aSheet);
4422
0
    }
4423
0
  }
4424
0
4425
0
  if (StyleSheetChangeEventsEnabled()) {
4426
0
    DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
4427
0
                               "StyleSheetApplicableStateChanged",
4428
0
                               mApplicable,
4429
0
                               aApplicable);
4430
0
  }
4431
0
4432
0
  if (!mSSApplicableStateNotificationPending) {
4433
0
    MOZ_RELEASE_ASSERT(NS_IsMainThread());
4434
0
    nsCOMPtr<nsIRunnable> notification =
4435
0
      NewRunnableMethod("nsIDocument::NotifyStyleSheetApplicableStateChanged",
4436
0
                        this,
4437
0
                        &nsIDocument::NotifyStyleSheetApplicableStateChanged);
4438
0
    mSSApplicableStateNotificationPending =
4439
0
      NS_SUCCEEDED(
4440
0
        Dispatch(TaskCategory::Other, notification.forget()));
4441
0
  }
4442
0
}
4443
4444
void
4445
nsIDocument::NotifyStyleSheetApplicableStateChanged()
4446
0
{
4447
0
  mSSApplicableStateNotificationPending = false;
4448
0
  nsCOMPtr<nsIObserverService> observerService =
4449
0
    mozilla::services::GetObserverService();
4450
0
  if (observerService) {
4451
0
    observerService->NotifyObservers(this,
4452
0
                                     "style-sheet-applicable-state-changed",
4453
0
                                     nullptr);
4454
0
  }
4455
0
}
4456
4457
static SheetType
4458
ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
4459
0
{
4460
0
  switch(aType) {
4461
0
    case nsIDocument::eAgentSheet:
4462
0
      return SheetType::Agent;
4463
0
    case nsIDocument::eUserSheet:
4464
0
      return SheetType::User;
4465
0
    case nsIDocument::eAuthorSheet:
4466
0
      return SheetType::Doc;
4467
0
    default:
4468
0
      MOZ_ASSERT(false, "wrong type");
4469
0
      // we must return something although this should never happen
4470
0
      return SheetType::Count;
4471
0
  }
4472
0
}
4473
4474
static int32_t
4475
FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets, nsIURI* aSheetURI)
4476
0
{
4477
0
  for (int32_t i = aSheets.Length() - 1; i >= 0; i-- ) {
4478
0
    bool bEqual;
4479
0
    nsIURI* uri = aSheets[i]->GetSheetURI();
4480
0
4481
0
    if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
4482
0
      return i;
4483
0
  }
4484
0
4485
0
  return -1;
4486
0
}
4487
4488
nsresult
4489
nsIDocument::LoadAdditionalStyleSheet(additionalSheetType aType,
4490
                                      nsIURI* aSheetURI)
4491
0
{
4492
0
  MOZ_ASSERT(aSheetURI, "null arg");
4493
0
4494
0
  // Checking if we have loaded this one already.
4495
0
  if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
4496
0
    return NS_ERROR_INVALID_ARG;
4497
0
4498
0
  // Loading the sheet sync.
4499
0
  RefPtr<css::Loader> loader = new css::Loader(GetDocGroup());
4500
0
4501
0
  css::SheetParsingMode parsingMode;
4502
0
  switch (aType) {
4503
0
    case nsIDocument::eAgentSheet:
4504
0
      parsingMode = css::eAgentSheetFeatures;
4505
0
      break;
4506
0
4507
0
    case nsIDocument::eUserSheet:
4508
0
      parsingMode = css::eUserSheetFeatures;
4509
0
      break;
4510
0
4511
0
    case nsIDocument::eAuthorSheet:
4512
0
      parsingMode = css::eAuthorSheetFeatures;
4513
0
      break;
4514
0
4515
0
    default:
4516
0
      MOZ_CRASH("impossible value for aType");
4517
0
  }
4518
0
4519
0
  RefPtr<StyleSheet> sheet;
4520
0
  nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet);
4521
0
  NS_ENSURE_SUCCESS(rv, rv);
4522
0
4523
0
  sheet->SetAssociatedDocumentOrShadowRoot(
4524
0
     this, StyleSheet::OwnedByDocumentOrShadowRoot);
4525
0
  MOZ_ASSERT(sheet->IsApplicable());
4526
0
4527
0
  return AddAdditionalStyleSheet(aType, sheet);
4528
0
}
4529
4530
nsresult
4531
nsIDocument::AddAdditionalStyleSheet(additionalSheetType aType, StyleSheet* aSheet)
4532
0
{
4533
0
  if (mAdditionalSheets[aType].Contains(aSheet))
4534
0
    return NS_ERROR_INVALID_ARG;
4535
0
4536
0
  if (!aSheet->IsApplicable())
4537
0
    return NS_ERROR_INVALID_ARG;
4538
0
4539
0
  mAdditionalSheets[aType].AppendElement(aSheet);
4540
0
4541
0
  if (nsIPresShell* shell = GetShell()) {
4542
0
    SheetType type = ConvertAdditionalSheetType(aType);
4543
0
    shell->StyleSet()->AppendStyleSheet(type, aSheet);
4544
0
    shell->ApplicableStylesChanged();
4545
0
  }
4546
0
4547
0
  // Passing false, so documet.styleSheets.length will not be affected by
4548
0
  // these additional sheets.
4549
0
  NotifyStyleSheetAdded(aSheet, false);
4550
0
  return NS_OK;
4551
0
}
4552
4553
void
4554
nsIDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4555
0
{
4556
0
  MOZ_ASSERT(aSheetURI);
4557
0
4558
0
  nsTArray<RefPtr<StyleSheet>>& sheets = mAdditionalSheets[aType];
4559
0
4560
0
  int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
4561
0
  if (i >= 0) {
4562
0
    RefPtr<StyleSheet> sheetRef = sheets[i];
4563
0
    sheets.RemoveElementAt(i);
4564
0
4565
0
    if (!mIsGoingAway) {
4566
0
      MOZ_ASSERT(sheetRef->IsApplicable());
4567
0
      if (nsIPresShell* shell = GetShell()) {
4568
0
        SheetType type = ConvertAdditionalSheetType(aType);
4569
0
        shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
4570
0
        shell->ApplicableStylesChanged();
4571
0
      }
4572
0
    }
4573
0
4574
0
    // Passing false, so documet.styleSheets.length will not be affected by
4575
0
    // these additional sheets.
4576
0
    NotifyStyleSheetRemoved(sheetRef, false);
4577
0
    sheetRef->ClearAssociatedDocumentOrShadowRoot();
4578
0
  }
4579
0
}
4580
4581
nsIGlobalObject*
4582
nsIDocument::GetScopeObject() const
4583
0
{
4584
0
  nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
4585
0
  return scope;
4586
0
}
4587
4588
void
4589
nsIDocument::SetScopeObject(nsIGlobalObject* aGlobal)
4590
0
{
4591
0
  mScopeObject = do_GetWeakReference(aGlobal);
4592
0
  if (aGlobal) {
4593
0
    mHasHadScriptHandlingObject = true;
4594
0
4595
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
4596
0
    if (window) {
4597
0
      // We want to get the tabgroup unconditionally, such that we can make
4598
0
      // certain that it is cached in the inner window early enough.
4599
0
      mozilla::dom::TabGroup* tabgroup = window->TabGroup();
4600
0
      // We should already have the principal, and now that we have been added to a
4601
0
      // window, we should be able to join a DocGroup!
4602
0
      nsAutoCString docGroupKey;
4603
0
      nsresult rv =
4604
0
        mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
4605
0
      if (mDocGroup) {
4606
0
        if (NS_SUCCEEDED(rv)) {
4607
0
          MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
4608
0
        }
4609
0
        MOZ_RELEASE_ASSERT(mDocGroup->GetTabGroup() == tabgroup);
4610
0
      } else {
4611
0
        mDocGroup = tabgroup->AddDocument(docGroupKey, this);
4612
0
        MOZ_ASSERT(mDocGroup);
4613
0
      }
4614
0
    }
4615
0
  }
4616
0
}
4617
4618
static void
4619
CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
4620
0
{
4621
0
  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4622
0
  if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
4623
0
    bool* contains = static_cast<bool*>(aContainsEME);
4624
0
    if (mediaElem->GetMediaKeys()) {
4625
0
      *contains = true;
4626
0
    }
4627
0
  }
4628
0
}
4629
4630
bool
4631
nsIDocument::ContainsEMEContent()
4632
0
{
4633
0
  bool containsEME = false;
4634
0
  EnumerateActivityObservers(CheckIfContainsEMEContent,
4635
0
                             static_cast<void*>(&containsEME));
4636
0
  return containsEME;
4637
0
}
4638
4639
static void
4640
CheckIfContainsMSEContent(nsISupports* aSupports, void* aContainsMSE)
4641
0
{
4642
0
  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4643
0
  if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
4644
0
    bool* contains = static_cast<bool*>(aContainsMSE);
4645
0
    RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
4646
0
    if (ms) {
4647
0
      *contains = true;
4648
0
    }
4649
0
  }
4650
0
}
4651
4652
bool
4653
nsIDocument::ContainsMSEContent()
4654
0
{
4655
0
  bool containsMSE = false;
4656
0
  EnumerateActivityObservers(CheckIfContainsMSEContent,
4657
0
                             static_cast<void*>(&containsMSE));
4658
0
  return containsMSE;
4659
0
}
4660
4661
static void
4662
NotifyActivityChanged(nsISupports *aSupports, void *aUnused)
4663
0
{
4664
0
  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4665
0
  if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
4666
0
    mediaElem->NotifyOwnerDocumentActivityChanged();
4667
0
  }
4668
0
  nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aSupports));
4669
0
  if (objectLoadingContent) {
4670
0
    nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
4671
0
    olc->NotifyOwnerDocumentActivityChanged();
4672
0
  }
4673
0
  nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(do_QueryInterface(aSupports));
4674
0
  if (objectDocumentActivity) {
4675
0
    objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
4676
0
  }
4677
0
}
4678
4679
bool
4680
nsIDocument::IsTopLevelWindowInactive() const
4681
0
{
4682
0
  nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShell();
4683
0
  if (!treeItem) {
4684
0
    return false;
4685
0
  }
4686
0
4687
0
  nsCOMPtr<nsIDocShellTreeItem> rootItem;
4688
0
  treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
4689
0
  if (!rootItem) {
4690
0
    return false;
4691
0
  }
4692
0
4693
0
  nsCOMPtr<nsPIDOMWindowOuter> domWindow = rootItem->GetWindow();
4694
0
  return domWindow && !domWindow->IsActive();
4695
0
}
4696
4697
void
4698
nsIDocument::SetContainer(nsDocShell* aContainer)
4699
0
{
4700
0
  if (aContainer) {
4701
0
    mDocumentContainer = aContainer;
4702
0
  } else {
4703
0
    mDocumentContainer = WeakPtr<nsDocShell>();
4704
0
  }
4705
0
4706
0
  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4707
0
4708
0
  // IsTopLevelWindowInactive depends on the docshell, so
4709
0
  // update the cached value now that it's available.
4710
0
  UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
4711
0
  if (!aContainer) {
4712
0
    return;
4713
0
  }
4714
0
4715
0
  // Get the Docshell
4716
0
  if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
4717
0
    // check if same type root
4718
0
    nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
4719
0
    aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
4720
0
    NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
4721
0
4722
0
    if (sameTypeRoot == aContainer) {
4723
0
      static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
4724
0
    }
4725
0
4726
0
    static_cast<nsDocument*>(this)->SetIsContentDocument(true);
4727
0
  }
4728
0
4729
0
  mAncestorPrincipals = aContainer->AncestorPrincipals();
4730
0
  mAncestorOuterWindowIDs = aContainer->AncestorOuterWindowIDs();
4731
0
}
4732
4733
nsISupports*
4734
nsIDocument::GetContainer() const
4735
0
{
4736
0
  return static_cast<nsIDocShell*>(mDocumentContainer);
4737
0
}
4738
4739
void
4740
nsIDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
4741
0
{
4742
0
  MOZ_ASSERT(aScriptGlobalObject || !mAnimationController ||
4743
0
             mAnimationController->IsPausedByType(
4744
0
               nsSMILTimeContainer::PAUSE_PAGEHIDE |
4745
0
               nsSMILTimeContainer::PAUSE_BEGIN),
4746
0
             "Clearing window pointer while animations are unpaused");
4747
0
4748
0
  if (mScriptGlobalObject && !aScriptGlobalObject) {
4749
0
    // We're detaching from the window.  We need to grab a pointer to
4750
0
    // our layout history state now.
4751
0
    mLayoutHistoryState = GetLayoutHistoryState();
4752
0
4753
0
    // Also make sure to remove our onload blocker now if we haven't done it yet
4754
0
    if (mOnloadBlockCount != 0) {
4755
0
      nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
4756
0
      if (loadGroup) {
4757
0
        loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
4758
0
      }
4759
0
    }
4760
0
4761
0
    ErrorResult error;
4762
0
    if (GetController().isSome()) {
4763
0
      imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
4764
0
      if (loader) {
4765
0
        loader->ClearCacheForControlledDocument(this);
4766
0
      }
4767
0
4768
0
      // We may become controlled again if this document comes back out
4769
0
      // of bfcache.  Clear our state to allow that to happen.  Only
4770
0
      // clear this flag if we are actually controlled, though, so pages
4771
0
      // that were force reloaded don't become controlled when they
4772
0
      // come out of bfcache.
4773
0
      mMaybeServiceWorkerControlled = false;
4774
0
    }
4775
0
  }
4776
0
4777
0
  // BlockOnload() might be called before mScriptGlobalObject is set.
4778
0
  // We may need to add the blocker once mScriptGlobalObject is set.
4779
0
  bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
4780
0
4781
0
  mScriptGlobalObject = aScriptGlobalObject;
4782
0
4783
0
  if (needOnloadBlocker) {
4784
0
    EnsureOnloadBlocker();
4785
0
  }
4786
0
4787
0
  UpdateFrameRequestCallbackSchedulingState();
4788
0
4789
0
  if (aScriptGlobalObject) {
4790
0
    // Go back to using the docshell for the layout history state
4791
0
    mLayoutHistoryState = nullptr;
4792
0
    SetScopeObject(aScriptGlobalObject);
4793
0
    mHasHadDefaultView = true;
4794
#ifdef DEBUG
4795
    if (!mWillReparent) {
4796
      // We really shouldn't have a wrapper here but if we do we need to make sure
4797
      // it has the correct parent.
4798
      JSObject *obj = GetWrapperPreserveColor();
4799
      if (obj) {
4800
        JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
4801
        NS_ASSERTION(JS::GetNonCCWObjectGlobal(obj) == newScope,
4802
                     "Wrong scope, this is really bad!");
4803
      }
4804
    }
4805
#endif
4806
4807
0
    if (mAllowDNSPrefetch) {
4808
0
      nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4809
0
      if (docShell) {
4810
#ifdef DEBUG
4811
        nsCOMPtr<nsIWebNavigation> webNav =
4812
          do_GetInterface(aScriptGlobalObject);
4813
        NS_ASSERTION(SameCOMIdentity(webNav, docShell),
4814
                     "Unexpected container or script global?");
4815
#endif
4816
        bool allowDNSPrefetch;
4817
0
        docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
4818
0
        mAllowDNSPrefetch = allowDNSPrefetch;
4819
0
      }
4820
0
    }
4821
0
4822
0
    // If we are set in a window that is already focused we should remember this
4823
0
    // as the time the document gained focus.
4824
0
    IgnoredErrorResult ignored;
4825
0
    bool focused = HasFocus(ignored);
4826
0
    if (focused) {
4827
0
      SetLastFocusTime(TimeStamp::Now());
4828
0
    }
4829
0
  }
4830
0
4831
0
  // Remember the pointer to our window (or lack there of), to avoid
4832
0
  // having to QI every time it's asked for.
4833
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
4834
0
  mWindow = window;
4835
0
4836
0
  // Now that we know what our window is, we can flush the CSP errors to the
4837
0
  // Web Console. We are flushing all messages that occured and were stored
4838
0
  // in the queue prior to this point.
4839
0
  nsCOMPtr<nsIContentSecurityPolicy> csp;
4840
0
  NodePrincipal()->GetCsp(getter_AddRefs(csp));
4841
0
  if (csp) {
4842
0
    static_cast<nsCSPContext*>(csp.get())->flushConsoleMessages();
4843
0
  }
4844
0
4845
0
  nsCOMPtr<nsIHttpChannelInternal> internalChannel =
4846
0
    do_QueryInterface(GetChannel());
4847
0
  if (internalChannel) {
4848
0
    nsCOMArray<nsISecurityConsoleMessage> messages;
4849
0
    DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages);
4850
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
4851
0
    SendToConsole(messages);
4852
0
  }
4853
0
4854
0
  // Set our visibility state, but do not fire the event.  This is correct
4855
0
  // because either we're coming out of bfcache (in which case IsVisible() will
4856
0
  // still test false at this point and no state change will happen) or we're
4857
0
  // doing the initial document load and don't want to fire the event for this
4858
0
  // change.
4859
0
  dom::VisibilityState oldState = mVisibilityState;
4860
0
  mVisibilityState = ComputeVisibilityState();
4861
0
  // When the visibility is changed, notify it to observers.
4862
0
  // Some observers need the notification, for example HTMLMediaElement uses
4863
0
  // it to update internal media resource allocation.
4864
0
  // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
4865
0
  // creation are already done before nsDocument::SetScriptGlobalObject() call.
4866
0
  // MediaDecoder decides whether starting decoding is decided based on
4867
0
  // document's visibility. When the MediaDecoder is created,
4868
0
  // nsDocument::SetScriptGlobalObject() is not yet called and document is
4869
0
  // hidden state. Therefore the MediaDecoder decides that decoding is
4870
0
  // not yet necessary. But soon after nsDocument::SetScriptGlobalObject()
4871
0
  // call, the document becomes not hidden. At the time, MediaDecoder needs
4872
0
  // to know it and needs to start updating decoding.
4873
0
  if (oldState != mVisibilityState) {
4874
0
    EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4875
0
  }
4876
0
4877
0
  // The global in the template contents owner document should be the same.
4878
0
  if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
4879
0
    mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
4880
0
  }
4881
0
4882
0
  if (!mMaybeServiceWorkerControlled && mDocumentContainer && mScriptGlobalObject && GetChannel()) {
4883
0
4884
0
    // If we are shift-reloaded, don't associate with a ServiceWorker.
4885
0
    if (mDocumentContainer->IsForceReloading()) {
4886
0
      NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
4887
0
      return;
4888
0
    }
4889
0
4890
0
    mMaybeServiceWorkerControlled = true;
4891
0
  }
4892
0
}
4893
4894
nsIScriptGlobalObject*
4895
nsIDocument::GetScriptHandlingObjectInternal() const
4896
0
{
4897
0
  MOZ_ASSERT(!mScriptGlobalObject,
4898
0
             "Do not call this when mScriptGlobalObject is set!");
4899
0
  if (mHasHadDefaultView) {
4900
0
    return nullptr;
4901
0
  }
4902
0
4903
0
  nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
4904
0
    do_QueryReferent(mScopeObject);
4905
0
  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(scriptHandlingObject);
4906
0
  if (win) {
4907
0
    nsPIDOMWindowOuter* outer = win->GetOuterWindow();
4908
0
    if (!outer || outer->GetCurrentInnerWindow() != win) {
4909
0
      NS_WARNING("Wrong inner/outer window combination!");
4910
0
      return nullptr;
4911
0
    }
4912
0
  }
4913
0
  return scriptHandlingObject;
4914
0
}
4915
void
4916
nsIDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
4917
0
{
4918
0
  NS_ASSERTION(!mScriptGlobalObject ||
4919
0
               mScriptGlobalObject == aScriptObject,
4920
0
               "Wrong script object!");
4921
0
  if (aScriptObject) {
4922
0
    SetScopeObject(aScriptObject);
4923
0
    mHasHadDefaultView = false;
4924
0
  }
4925
0
}
4926
4927
nsPIDOMWindowOuter*
4928
nsIDocument::GetWindowInternal() const
4929
0
{
4930
0
  MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
4931
0
  // Let's use mScriptGlobalObject. Even if the document is already removed from
4932
0
  // the docshell, the outer window might be still obtainable from the it.
4933
0
  nsCOMPtr<nsPIDOMWindowOuter> win;
4934
0
  if (mRemovedFromDocShell) {
4935
0
    // The docshell returns the outer window we are done.
4936
0
    nsCOMPtr<nsIDocShell> kungFuDeathGrip(mDocumentContainer);
4937
0
    if (kungFuDeathGrip) {
4938
0
      win = kungFuDeathGrip->GetWindow();
4939
0
    }
4940
0
  } else {
4941
0
    if (nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(mScriptGlobalObject)) {
4942
0
      // mScriptGlobalObject is always the inner window, let's get the outer.
4943
0
      win = inner->GetOuterWindow();
4944
0
    }
4945
0
  }
4946
0
4947
0
  return win;
4948
0
}
4949
4950
bool
4951
nsIDocument::InternalAllowXULXBL()
4952
0
{
4953
0
  if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
4954
0
    mAllowXULXBL = eTriTrue;
4955
0
    return true;
4956
0
  }
4957
0
4958
0
  mAllowXULXBL = eTriFalse;
4959
0
  return false;
4960
0
}
4961
4962
// Note: We don't hold a reference to the document observer; we assume
4963
// that it has a live reference to the document.
4964
void
4965
nsIDocument::AddObserver(nsIDocumentObserver* aObserver)
4966
0
{
4967
0
  NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
4968
0
               "Observer already in the list");
4969
0
  mObservers.AppendElement(aObserver);
4970
0
  AddMutationObserver(aObserver);
4971
0
}
4972
4973
bool
4974
nsIDocument::RemoveObserver(nsIDocumentObserver* aObserver)
4975
0
{
4976
0
  // If we're in the process of destroying the document (and we're
4977
0
  // informing the observers of the destruction), don't remove the
4978
0
  // observers from the list. This is not a big deal, since we
4979
0
  // don't hold a live reference to the observers.
4980
0
  if (!mInDestructor) {
4981
0
    RemoveMutationObserver(aObserver);
4982
0
    return mObservers.RemoveElement(aObserver);
4983
0
  }
4984
0
4985
0
  return mObservers.Contains(aObserver);
4986
0
}
4987
4988
void
4989
nsIDocument::MaybeEndOutermostXBLUpdate()
4990
0
{
4991
0
  // Only call BindingManager()->EndOutermostUpdate() when
4992
0
  // we're not in an update and it is safe to run scripts.
4993
0
  if (mUpdateNestLevel == 0 && mInXBLUpdate) {
4994
0
    if (nsContentUtils::IsSafeToRunScript()) {
4995
0
      mInXBLUpdate = false;
4996
0
      BindingManager()->EndOutermostUpdate();
4997
0
    } else if (!mInDestructor) {
4998
0
      if (!mMaybeEndOutermostXBLUpdateRunner) {
4999
0
        mMaybeEndOutermostXBLUpdateRunner =
5000
0
          NewRunnableMethod("nsDocument::MaybeEndOutermostXBLUpdate",
5001
0
                            this,
5002
0
                            &nsDocument::MaybeEndOutermostXBLUpdate);
5003
0
      }
5004
0
      nsContentUtils::AddScriptRunner(mMaybeEndOutermostXBLUpdateRunner);
5005
0
    }
5006
0
  }
5007
0
}
5008
5009
void
5010
nsIDocument::BeginUpdate()
5011
0
{
5012
0
  // If the document is going away, then it's probably okay to do things to it
5013
0
  // in the wrong DocGroup. We're unlikely to run JS or do anything else
5014
0
  // observable at this point. We reach this point when cycle collecting a
5015
0
  // <link> element and the unlink code removes a style sheet.
5016
0
  //
5017
0
  // TODO(emilio): Style updates are gone, can this happen now?
5018
0
  if (mDocGroup && !mIsGoingAway && !mInUnlinkOrDeletion && !mIgnoreDocGroupMismatches) {
5019
0
    mDocGroup->ValidateAccess();
5020
0
  }
5021
0
5022
0
  if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
5023
0
    mInXBLUpdate = true;
5024
0
    BindingManager()->BeginOutermostUpdate();
5025
0
  }
5026
0
5027
0
  ++mUpdateNestLevel;
5028
0
  nsContentUtils::AddScriptBlocker();
5029
0
  NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this));
5030
0
}
5031
5032
void
5033
nsDocument::EndUpdate()
5034
0
{
5035
0
  NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this));
5036
0
5037
0
  nsContentUtils::RemoveScriptBlocker();
5038
0
5039
0
  --mUpdateNestLevel;
5040
0
5041
0
  // This set of updates may have created XBL bindings.  Let the
5042
0
  // binding manager know we're done.
5043
0
  MaybeEndOutermostXBLUpdate();
5044
0
5045
0
  MaybeInitializeFinalizeFrameLoaders();
5046
0
}
5047
5048
void
5049
nsDocument::BeginLoad()
5050
0
{
5051
0
  MOZ_ASSERT(!mDidCallBeginLoad);
5052
0
  mDidCallBeginLoad = true;
5053
0
5054
0
  // Block onload here to prevent having to deal with blocking and
5055
0
  // unblocking it while we know the document is loading.
5056
0
  BlockOnload();
5057
0
  mDidFireDOMContentLoaded = false;
5058
0
  BlockDOMContentLoaded();
5059
0
5060
0
  if (mScriptLoader) {
5061
0
    mScriptLoader->BeginDeferringScripts();
5062
0
  }
5063
0
5064
0
  NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
5065
0
}
5066
5067
void
5068
nsIDocument::MozSetImageElement(const nsAString& aImageElementId,
5069
                               Element* aElement)
5070
0
{
5071
0
  if (aImageElementId.IsEmpty())
5072
0
    return;
5073
0
5074
0
  // Hold a script blocker while calling SetImageElement since that can call
5075
0
  // out to id-observers
5076
0
  nsAutoScriptBlocker scriptBlocker;
5077
0
5078
0
  nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aImageElementId);
5079
0
  if (entry) {
5080
0
    entry->SetImageElement(aElement);
5081
0
    if (entry->IsEmpty()) {
5082
0
      mIdentifierMap.RemoveEntry(entry);
5083
0
    }
5084
0
  }
5085
0
}
5086
5087
void
5088
nsIDocument::DispatchContentLoadedEvents()
5089
0
{
5090
0
  // If you add early returns from this method, make sure you're
5091
0
  // calling UnblockOnload properly.
5092
0
5093
0
  // Unpin references to preloaded images
5094
0
  mPreloadingImages.Clear();
5095
0
5096
0
  // DOM manipulation after content loaded should not care if the element
5097
0
  // came from the preloader.
5098
0
  mPreloadedPreconnects.Clear();
5099
0
5100
0
  if (mTiming) {
5101
0
    mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
5102
0
  }
5103
0
5104
0
  // Dispatch observer notification to notify observers document is interactive.
5105
0
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
5106
0
  if (os) {
5107
0
    nsIPrincipal* principal = NodePrincipal();
5108
0
    os->NotifyObservers(this,
5109
0
                        nsContentUtils::IsSystemPrincipal(principal) ?
5110
0
                          "chrome-document-interactive" :
5111
0
                          "content-document-interactive",
5112
0
                        nullptr);
5113
0
  }
5114
0
5115
0
  // Fire a DOM event notifying listeners that this document has been
5116
0
  // loaded (excluding images and other loads initiated by this
5117
0
  // document).
5118
0
  nsContentUtils::DispatchTrustedEvent(this, this,
5119
0
                                       NS_LITERAL_STRING("DOMContentLoaded"),
5120
0
                                       CanBubble::eYes, Cancelable::eNo);
5121
0
5122
0
  if (MayStartLayout()) {
5123
0
    MaybeResolveReadyForIdle();
5124
0
  }
5125
0
5126
0
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
5127
0
  nsIDocShell* docShell = this->GetDocShell();
5128
0
5129
0
  if (timelines && timelines->HasConsumer(docShell)) {
5130
0
    timelines->AddMarkerForDocShell(docShell,
5131
0
      MakeUnique<DocLoadingTimelineMarker>("document::DOMContentLoaded"));
5132
0
  }
5133
0
5134
0
  if (mTiming) {
5135
0
    mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
5136
0
  }
5137
0
5138
0
  // If this document is a [i]frame, fire a DOMFrameContentLoaded
5139
0
  // event on all parent documents notifying that the HTML (excluding
5140
0
  // other external files such as images and stylesheets) in a frame
5141
0
  // has finished loading.
5142
0
5143
0
  // target_frame is the [i]frame element that will be used as the
5144
0
  // target for the event. It's the [i]frame whose content is done
5145
0
  // loading.
5146
0
  nsCOMPtr<EventTarget> target_frame;
5147
0
5148
0
  if (mParentDocument) {
5149
0
    target_frame = mParentDocument->FindContentForSubDocument(this);
5150
0
  }
5151
0
5152
0
  if (target_frame) {
5153
0
    nsCOMPtr<nsIDocument> parent = mParentDocument;
5154
0
    do {
5155
0
      RefPtr<Event> event;
5156
0
      if (parent) {
5157
0
        IgnoredErrorResult ignored;
5158
0
        event = parent->CreateEvent(NS_LITERAL_STRING("Events"),
5159
0
                                    CallerType::System, ignored);
5160
0
5161
0
      }
5162
0
5163
0
      if (event) {
5164
0
        event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
5165
0
                         true);
5166
0
5167
0
        event->SetTarget(target_frame);
5168
0
        event->SetTrusted(true);
5169
0
5170
0
        // To dispatch this event we must manually call
5171
0
        // EventDispatcher::Dispatch() on the ancestor document since the
5172
0
        // target is not in the same document, so the event would never reach
5173
0
        // the ancestor document if we used the normal event
5174
0
        // dispatching code.
5175
0
5176
0
        WidgetEvent* innerEvent = event->WidgetEventPtr();
5177
0
        if (innerEvent) {
5178
0
          nsEventStatus status = nsEventStatus_eIgnore;
5179
0
5180
0
          RefPtr<nsPresContext> context = parent->GetPresContext();
5181
0
5182
0
          if (context) {
5183
0
            EventDispatcher::Dispatch(parent, context, innerEvent, event,
5184
0
                                      &status);
5185
0
          }
5186
0
        }
5187
0
      }
5188
0
5189
0
      parent = parent->GetParentDocument();
5190
0
    } while (parent);
5191
0
  }
5192
0
5193
0
  // If the document has a manifest attribute, fire a MozApplicationManifest
5194
0
  // event.
5195
0
  Element* root = GetRootElement();
5196
0
  if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
5197
0
    nsContentUtils::DispatchChromeEvent(this, this,
5198
0
                                        NS_LITERAL_STRING("MozApplicationManifest"),
5199
0
                                        CanBubble::eYes, Cancelable::eYes);
5200
0
  }
5201
0
5202
0
  nsPIDOMWindowInner* inner = GetInnerWindow();
5203
0
  if (inner) {
5204
0
    inner->NoteDOMContentLoaded();
5205
0
  }
5206
0
5207
0
  // TODO
5208
0
  if (mMaybeServiceWorkerControlled) {
5209
0
    using mozilla::dom::ServiceWorkerManager;
5210
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
5211
0
    if (swm) {
5212
0
      Maybe<ClientInfo> clientInfo = GetClientInfo();
5213
0
      if (clientInfo.isSome()) {
5214
0
        swm->MaybeCheckNavigationUpdate(clientInfo.ref());
5215
0
      }
5216
0
    }
5217
0
  }
5218
0
5219
0
  UnblockOnload(true);
5220
0
}
5221
5222
#if defined(DEBUG) && !defined(ANDROID)
5223
// We want to get to a point where all about: pages ship with a CSP. This
5224
// assertion ensures that we can not deploy new about: pages without a CSP.
5225
// Initially we will whitelist legacy about: pages which not yet have a CSP
5226
// attached, but ultimately that whitelist should disappear.
5227
// Please note that any about: page should not use inline JS or inline CSS,
5228
// and instead should load JS and CSS from an external file (*.js, *.css)
5229
// which allows us to apply a strong CSP omitting 'unsafe-inline'. Ideally,
5230
// the CSP allows precisely the resources that need to be loaded; but it
5231
// should at least be as strong as:
5232
// <meta http-equiv="Content-Security-Policy" content="default-src chrome:"/>
5233
static void
5234
AssertContentPrivilegedAboutPageHasCSP(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal)
5235
{
5236
  // Curently we can't serialize the CSP, hence we only assert if
5237
  // running in the content process.
5238
  if (!XRE_IsContentProcess()) {
5239
    return;
5240
  }
5241
5242
  // Check if we are loading an about: URI at all
5243
  bool isAboutURI =
5244
    (NS_SUCCEEDED(aDocumentURI->SchemeIs("about", &isAboutURI)) && isAboutURI);
5245
5246
  if (!isAboutURI) {
5247
    return;
5248
  }
5249
5250
  // Check if we are loading a content-privileged about: URI
5251
  nsCOMPtr<nsIAboutModule> aboutModule;
5252
  nsresult rv = NS_GetAboutModule(aDocumentURI, getter_AddRefs(aboutModule));
5253
  NS_ENSURE_SUCCESS_VOID(rv);
5254
5255
  uint32_t aboutModuleFlags = 0;
5256
  rv = aboutModule->GetURIFlags(aDocumentURI, &aboutModuleFlags);
5257
  NS_ENSURE_SUCCESS_VOID(rv);
5258
5259
  if (!(aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT)) {
5260
    return;
5261
  }
5262
5263
  // Potentially init the legacy whitelist of about URIs without a CSP.
5264
  static StaticAutoPtr<nsTArray<nsCString>> sLegacyAboutPagesWithNoCSP;
5265
  if (!sLegacyAboutPagesWithNoCSP ||
5266
      Preferences::GetBool("csp.overrule_content_privileged_about_uris_without_csp_whitelist")) {
5267
    sLegacyAboutPagesWithNoCSP = new nsTArray<nsCString>();
5268
    nsAutoCString legacyAboutPages;
5269
    Preferences::GetCString("csp.content_privileged_about_uris_without_csp",
5270
      legacyAboutPages);
5271
    for (const nsACString& hostString : legacyAboutPages.Split(',')) {
5272
      // please note that for the actual whitelist we only store the path of
5273
      // about: URI. Let's reassemble the full about URI here so we don't
5274
      // have to remove query arguments later.
5275
      nsCString aboutURI;
5276
      aboutURI.AppendLiteral("about:");
5277
      aboutURI.Append(hostString);
5278
      sLegacyAboutPagesWithNoCSP->AppendElement(aboutURI);
5279
    }
5280
    ClearOnShutdown(&sLegacyAboutPagesWithNoCSP);
5281
  }
5282
5283
  // Check if the about URI is whitelisted
5284
  nsAutoCString aboutSpec;
5285
  aDocumentURI->GetSpec(aboutSpec);
5286
  for (auto& legacyPageEntry : *sLegacyAboutPagesWithNoCSP) {
5287
    // please note that we perform a substring match here on purpose,
5288
    // so we don't have to deal and parse out all the query arguments
5289
    // the various about pages rely on.
5290
    if (aboutSpec.Find(legacyPageEntry) == 0) {
5291
      return;
5292
    }
5293
  }
5294
5295
  nsCOMPtr<nsIContentSecurityPolicy> csp;
5296
  aPrincipal->GetCsp(getter_AddRefs(csp));
5297
  nsAutoString parsedPolicyStr;
5298
  if (csp) {
5299
    uint32_t policyCount = 0;
5300
     csp->GetPolicyCount(&policyCount);
5301
     if (policyCount > 0) {
5302
       csp->GetPolicyString(0, parsedPolicyStr);
5303
     }
5304
  }
5305
  if (Preferences::GetBool("csp.overrule_content_privileged_about_uris_without_csp_whitelist")) {
5306
    NS_ASSERTION(parsedPolicyStr.Find("default-src") >= 0, "about: page must have a CSP");
5307
    return;
5308
  }
5309
  MOZ_ASSERT(parsedPolicyStr.Find("default-src") >= 0,
5310
    "about: page must contain a CSP including default-src");
5311
}
5312
#endif
5313
5314
void
5315
nsDocument::EndLoad()
5316
0
{
5317
#if defined(DEBUG) && !defined(ANDROID)
5318
  // only assert if nothing stopped the load on purpose
5319
  if (!mParserAborted) {
5320
    AssertContentPrivilegedAboutPageHasCSP(mDocumentURI, NodePrincipal());
5321
  }
5322
#endif
5323
5324
0
  // EndLoad may have been called without a matching call to BeginLoad, in the
5325
0
  // case of a failed parse (for example, due to timeout). In such a case, we
5326
0
  // still want to execute part of this code to do appropriate cleanup, but we
5327
0
  // gate part of it because it is intended to match 1-for-1 with calls to
5328
0
  // BeginLoad. We have an explicit flag bit for this purpose, since it's
5329
0
  // complicated and error prone to derive this condition from other related
5330
0
  // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
5331
0
5332
0
  // Part 1: Code that always executes to cleanup end of parsing, whether
5333
0
  // that parsing was successful or not.
5334
0
5335
0
  // Drop the ref to our parser, if any, but keep hold of the sink so that we
5336
0
  // can flush it from FlushPendingNotifications as needed.  We might have to
5337
0
  // do that to get a StartLayout() to happen.
5338
0
  if (mParser) {
5339
0
    mWeakSink = do_GetWeakReference(mParser->GetContentSink());
5340
0
    mParser = nullptr;
5341
0
  }
5342
0
5343
0
  NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
5344
0
5345
0
  // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
5346
0
5347
0
  if (!mDidCallBeginLoad) {
5348
0
    return;
5349
0
  }
5350
0
  mDidCallBeginLoad = false;
5351
0
5352
0
  UnblockDOMContentLoaded();
5353
0
}
5354
5355
void
5356
nsIDocument::UnblockDOMContentLoaded()
5357
0
{
5358
0
  MOZ_ASSERT(mBlockDOMContentLoaded);
5359
0
  if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
5360
0
    return;
5361
0
  }
5362
0
5363
0
  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p UnblockDOMContentLoaded", this));
5364
0
5365
0
  mDidFireDOMContentLoaded = true;
5366
0
  if (nsIPresShell* shell = GetShell()) {
5367
0
    shell->GetRefreshDriver()->NotifyDOMContentLoaded();
5368
0
  }
5369
0
5370
0
5371
0
  MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
5372
0
  if (!mSynchronousDOMContentLoaded) {
5373
0
    MOZ_RELEASE_ASSERT(NS_IsMainThread());
5374
0
    nsCOMPtr<nsIRunnable> ev =
5375
0
      NewRunnableMethod("nsIDocument::DispatchContentLoadedEvents",
5376
0
                        this,
5377
0
                        &nsIDocument::DispatchContentLoadedEvents);
5378
0
    Dispatch(TaskCategory::Other, ev.forget());
5379
0
  } else {
5380
0
    DispatchContentLoadedEvents();
5381
0
  }
5382
0
}
5383
5384
void
5385
nsIDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
5386
0
{
5387
0
  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
5388
0
             "Someone forgot a scriptblocker");
5389
0
  NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
5390
0
                               (this, aContent, aStateMask));
5391
0
}
5392
5393
void
5394
nsIDocument::DocumentStatesChanged(EventStates aStateMask)
5395
0
{
5396
0
  UpdateDocumentStates(aStateMask);
5397
0
  NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
5398
0
}
5399
5400
void
5401
nsIDocument::StyleRuleChanged(StyleSheet* aSheet, css::Rule* aStyleRule)
5402
0
{
5403
0
  if (nsIPresShell* shell = GetShell()) {
5404
0
    shell->ApplicableStylesChanged();
5405
0
  }
5406
0
5407
0
  if (!StyleSheetChangeEventsEnabled()) {
5408
0
    return;
5409
0
  }
5410
0
5411
0
  DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5412
0
                             "StyleRuleChanged",
5413
0
                             mRule,
5414
0
                             aStyleRule);
5415
0
}
5416
5417
void
5418
nsIDocument::StyleRuleAdded(StyleSheet* aSheet, css::Rule* aStyleRule)
5419
0
{
5420
0
  if (nsIPresShell* shell = GetShell()) {
5421
0
    shell->ApplicableStylesChanged();
5422
0
  }
5423
0
5424
0
  if (!StyleSheetChangeEventsEnabled()) {
5425
0
    return;
5426
0
  }
5427
0
5428
0
  DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5429
0
                             "StyleRuleAdded",
5430
0
                             mRule,
5431
0
                             aStyleRule);
5432
0
}
5433
5434
void
5435
nsIDocument::StyleRuleRemoved(StyleSheet* aSheet, css::Rule* aStyleRule)
5436
0
{
5437
0
  if (nsIPresShell* shell = GetShell()) {
5438
0
    shell->ApplicableStylesChanged();
5439
0
  }
5440
0
5441
0
  if (!StyleSheetChangeEventsEnabled()) {
5442
0
    return;
5443
0
  }
5444
0
5445
0
  DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5446
0
                             "StyleRuleRemoved",
5447
0
                             mRule,
5448
0
                             aStyleRule);
5449
0
}
5450
5451
#undef DO_STYLESHEET_NOTIFICATION
5452
5453
static Element*
5454
GetCustomContentContainer(nsIPresShell* aShell)
5455
0
{
5456
0
  if (!aShell || !aShell->GetCanvasFrame()) {
5457
0
    return nullptr;
5458
0
  }
5459
0
5460
0
  return aShell->GetCanvasFrame()->GetCustomContentContainer();
5461
0
}
5462
5463
static void
5464
InsertAnonContentIntoCanvas(AnonymousContent& aAnonContent,
5465
                            nsIPresShell* aShell)
5466
0
{
5467
0
  Element* container = GetCustomContentContainer(aShell);
5468
0
  if (!container) {
5469
0
    return;
5470
0
  }
5471
0
5472
0
  nsresult rv = container->AppendChildTo(&aAnonContent.ContentNode(), true);
5473
0
  if (NS_FAILED(rv)) {
5474
0
    return;
5475
0
  }
5476
0
5477
0
  aShell->GetCanvasFrame()->ShowCustomContentContainer();
5478
0
}
5479
5480
already_AddRefed<AnonymousContent>
5481
nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
5482
0
{
5483
0
  nsAutoScriptBlocker scriptBlocker;
5484
0
5485
0
  // Clone the node to avoid returning a direct reference.
5486
0
  nsCOMPtr<nsINode> clone = aElement.CloneNode(true, aRv);
5487
0
  if (aRv.Failed()) {
5488
0
    return nullptr;
5489
0
  }
5490
0
5491
0
  auto anonContent =
5492
0
    MakeRefPtr<AnonymousContent>(clone.forget().downcast<Element>());
5493
0
  mAnonymousContents.AppendElement(anonContent);
5494
0
5495
0
  InsertAnonContentIntoCanvas(*anonContent, GetShell());
5496
0
5497
0
  return anonContent.forget();
5498
0
}
5499
5500
static void
5501
RemoveAnonContentFromCanvas(AnonymousContent& aAnonContent,
5502
                            nsIPresShell* aShell)
5503
0
{
5504
0
  RefPtr<Element> container = GetCustomContentContainer(aShell);
5505
0
  if (!container) {
5506
0
    return;
5507
0
  }
5508
0
  container->RemoveChild(aAnonContent.ContentNode(), IgnoreErrors());
5509
0
}
5510
5511
void
5512
nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
5513
                                    ErrorResult& aRv)
5514
0
{
5515
0
  nsAutoScriptBlocker scriptBlocker;
5516
0
5517
0
  auto index = mAnonymousContents.IndexOf(&aContent);
5518
0
  if (index == mAnonymousContents.NoIndex) {
5519
0
    return;
5520
0
  }
5521
0
5522
0
  mAnonymousContents.RemoveElementAt(index);
5523
0
  RemoveAnonContentFromCanvas(aContent, GetShell());
5524
0
5525
0
  if (mAnonymousContents.IsEmpty() && GetCustomContentContainer(GetShell())) {
5526
0
    GetShell()->GetCanvasFrame()->HideCustomContentContainer();
5527
0
  }
5528
0
}
5529
5530
Element*
5531
nsIDocument::GetAnonRootIfInAnonymousContentContainer(nsINode* aNode) const
5532
0
{
5533
0
  if (!aNode->IsInNativeAnonymousSubtree()) {
5534
0
    return nullptr;
5535
0
  }
5536
0
5537
0
  nsIPresShell* shell = GetShell();
5538
0
  if (!shell || !shell->GetCanvasFrame()) {
5539
0
    return nullptr;
5540
0
  }
5541
0
5542
0
  nsAutoScriptBlocker scriptBlocker;
5543
0
  nsCOMPtr<Element> customContainer = shell->GetCanvasFrame()
5544
0
                                           ->GetCustomContentContainer();
5545
0
  if (!customContainer) {
5546
0
    return nullptr;
5547
0
  }
5548
0
5549
0
  // An arbitrary number of elements can be inserted as children of the custom
5550
0
  // container frame.  We want the one that was added that contains aNode, so
5551
0
  // we need to keep track of the last child separately using |child| here.
5552
0
  nsINode* child = aNode;
5553
0
  nsINode* parent = aNode->GetParentNode();
5554
0
  while (parent && parent->IsInNativeAnonymousSubtree()) {
5555
0
    if (parent == customContainer) {
5556
0
      return Element::FromNode(child);
5557
0
    }
5558
0
    child = parent;
5559
0
    parent = child->GetParentNode();
5560
0
  }
5561
0
  return nullptr;
5562
0
}
5563
5564
Maybe<ClientInfo>
5565
nsIDocument::GetClientInfo() const
5566
0
{
5567
0
  nsPIDOMWindowInner* inner = GetInnerWindow();
5568
0
  if (inner) {
5569
0
    return inner->GetClientInfo();
5570
0
  }
5571
0
  return Maybe<ClientInfo>();
5572
0
}
5573
5574
Maybe<ClientState>
5575
nsIDocument::GetClientState() const
5576
0
{
5577
0
  nsPIDOMWindowInner* inner = GetInnerWindow();
5578
0
  if (inner) {
5579
0
    return inner->GetClientState();
5580
0
  }
5581
0
  return Maybe<ClientState>();
5582
0
}
5583
5584
Maybe<ServiceWorkerDescriptor>
5585
nsIDocument::GetController() const
5586
0
{
5587
0
  nsPIDOMWindowInner* inner = GetInnerWindow();
5588
0
  if (inner) {
5589
0
    return inner->GetController();
5590
0
  }
5591
0
  return Maybe<ServiceWorkerDescriptor>();
5592
0
}
5593
5594
//
5595
// nsIDocument interface
5596
//
5597
DocumentType*
5598
nsIDocument::GetDoctype() const
5599
0
{
5600
0
  for (nsIContent* child = GetFirstChild();
5601
0
       child;
5602
0
       child = child->GetNextSibling()) {
5603
0
    if (child->NodeType() == DOCUMENT_TYPE_NODE) {
5604
0
      return static_cast<DocumentType*>(child);
5605
0
    }
5606
0
  }
5607
0
  return nullptr;
5608
0
}
5609
5610
DOMImplementation*
5611
nsIDocument::GetImplementation(ErrorResult& rv)
5612
0
{
5613
0
  if (!mDOMImplementation) {
5614
0
    nsCOMPtr<nsIURI> uri;
5615
0
    NS_NewURI(getter_AddRefs(uri), "about:blank");
5616
0
    if (!uri) {
5617
0
      rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5618
0
      return nullptr;
5619
0
    }
5620
0
    bool hasHadScriptObject = true;
5621
0
    nsIScriptGlobalObject* scriptObject =
5622
0
      GetScriptHandlingObject(hasHadScriptObject);
5623
0
    if (!scriptObject && hasHadScriptObject) {
5624
0
      rv.Throw(NS_ERROR_UNEXPECTED);
5625
0
      return nullptr;
5626
0
    }
5627
0
    mDOMImplementation = new DOMImplementation(this,
5628
0
      scriptObject ? scriptObject : GetScopeObject(), uri, uri);
5629
0
  }
5630
0
5631
0
  return mDOMImplementation;
5632
0
}
5633
5634
bool IsLowercaseASCII(const nsAString& aValue)
5635
0
{
5636
0
  int32_t len = aValue.Length();
5637
0
  for (int32_t i = 0; i < len; ++i) {
5638
0
    char16_t c = aValue[i];
5639
0
    if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
5640
0
      return false;
5641
0
    }
5642
0
  }
5643
0
  return true;
5644
0
}
5645
5646
// We only support pseudo-elements with two colons in this function.
5647
static CSSPseudoElementType
5648
GetPseudoElementType(const nsString& aString, ErrorResult& aRv)
5649
0
{
5650
0
  MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null");
5651
0
  if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') {
5652
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5653
0
    return CSSPseudoElementType::NotPseudo;
5654
0
  }
5655
0
  RefPtr<nsAtom> pseudo = NS_Atomize(Substring(aString, 1));
5656
0
  return nsCSSPseudoElements::GetPseudoType(pseudo,
5657
0
      CSSEnabledState::eInUASheets);
5658
0
}
5659
5660
already_AddRefed<Element>
5661
nsIDocument::CreateElement(const nsAString& aTagName,
5662
                           const ElementCreationOptionsOrString& aOptions,
5663
                           ErrorResult& rv)
5664
0
{
5665
0
  rv = nsContentUtils::CheckQName(aTagName, false);
5666
0
  if (rv.Failed()) {
5667
0
    return nullptr;
5668
0
  }
5669
0
5670
0
  bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
5671
0
  nsAutoString lcTagName;
5672
0
  if (needsLowercase) {
5673
0
    nsContentUtils::ASCIIToLower(aTagName, lcTagName);
5674
0
  }
5675
0
5676
0
  const nsString* is = nullptr;
5677
0
  CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
5678
0
  if (aOptions.IsElementCreationOptions()) {
5679
0
    const ElementCreationOptions& options =
5680
0
      aOptions.GetAsElementCreationOptions();
5681
0
5682
0
    if (CustomElementRegistry::IsCustomElementEnabled(this) &&
5683
0
        options.mIs.WasPassed()) {
5684
0
      is = &options.mIs.Value();
5685
0
    }
5686
0
5687
0
    // Check 'pseudo' and throw an exception if it's not one allowed
5688
0
    // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
5689
0
    if (options.mPseudo.WasPassed()) {
5690
0
      pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv);
5691
0
      if (rv.Failed() ||
5692
0
          pseudoType == CSSPseudoElementType::NotPseudo ||
5693
0
          !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) {
5694
0
        rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5695
0
        return nullptr;
5696
0
      }
5697
0
    }
5698
0
  }
5699
0
5700
0
  RefPtr<Element> elem = CreateElem(
5701
0
    needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
5702
0
5703
0
  if (pseudoType != CSSPseudoElementType::NotPseudo) {
5704
0
    elem->SetPseudoElementType(pseudoType);
5705
0
  }
5706
0
5707
0
  return elem.forget();
5708
0
}
5709
5710
already_AddRefed<Element>
5711
nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
5712
                             const nsAString& aQualifiedName,
5713
                             const ElementCreationOptionsOrString& aOptions,
5714
                             ErrorResult& rv)
5715
0
{
5716
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5717
0
  rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5718
0
                                            aQualifiedName,
5719
0
                                            mNodeInfoManager,
5720
0
                                            ELEMENT_NODE,
5721
0
                                            getter_AddRefs(nodeInfo));
5722
0
  if (rv.Failed()) {
5723
0
    return nullptr;
5724
0
  }
5725
0
5726
0
  const nsString* is = nullptr;
5727
0
  if (CustomElementRegistry::IsCustomElementEnabled(this) &&
5728
0
      aOptions.IsElementCreationOptions()) {
5729
0
    const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions();
5730
0
    if (options.mIs.WasPassed()) {
5731
0
      is = &options.mIs.Value();
5732
0
    }
5733
0
  }
5734
0
5735
0
  nsCOMPtr<Element> element;
5736
0
  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
5737
0
                     NOT_FROM_PARSER, is);
5738
0
  if (rv.Failed()) {
5739
0
    return nullptr;
5740
0
  }
5741
0
5742
0
  return element.forget();
5743
0
}
5744
5745
already_AddRefed<Element>
5746
nsIDocument::CreateXULElement(const nsAString& aTagName,
5747
                              const ElementCreationOptionsOrString& aOptions,
5748
                              ErrorResult& aRv)
5749
0
{
5750
0
  aRv = nsContentUtils::CheckQName(aTagName, false);
5751
0
  if (aRv.Failed()) {
5752
0
    return nullptr;
5753
0
  }
5754
0
5755
0
  const nsString* is = nullptr;
5756
0
  if (CustomElementRegistry::IsCustomElementEnabled(this) &&
5757
0
      aOptions.IsElementCreationOptions()) {
5758
0
    const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions();
5759
0
    if (options.mIs.WasPassed()) {
5760
0
      is = &options.mIs.Value();
5761
0
    }
5762
0
  }
5763
0
5764
0
  RefPtr<Element> elem = CreateElem(aTagName, nullptr, kNameSpaceID_XUL, is);
5765
0
  if (!elem) {
5766
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
5767
0
    return nullptr;
5768
0
  }
5769
0
  return elem.forget();
5770
0
}
5771
5772
already_AddRefed<nsTextNode>
5773
nsIDocument::CreateEmptyTextNode() const
5774
0
{
5775
0
  RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5776
0
  return text.forget();
5777
0
}
5778
5779
already_AddRefed<nsTextNode>
5780
nsIDocument::CreateTextNode(const nsAString& aData) const
5781
0
{
5782
0
  RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5783
0
  // Don't notify; this node is still being created.
5784
0
  text->SetText(aData, false);
5785
0
  return text.forget();
5786
0
}
5787
5788
already_AddRefed<DocumentFragment>
5789
nsIDocument::CreateDocumentFragment() const
5790
0
{
5791
0
  RefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
5792
0
  return frag.forget();
5793
0
}
5794
5795
// Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
5796
already_AddRefed<dom::Comment>
5797
nsIDocument::CreateComment(const nsAString& aData) const
5798
0
{
5799
0
  RefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
5800
0
5801
0
  // Don't notify; this node is still being created.
5802
0
  comment->SetText(aData, false);
5803
0
  return comment.forget();
5804
0
}
5805
5806
already_AddRefed<CDATASection>
5807
nsIDocument::CreateCDATASection(const nsAString& aData,
5808
                                ErrorResult& rv)
5809
0
{
5810
0
  if (IsHTMLDocument()) {
5811
0
    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5812
0
    return nullptr;
5813
0
  }
5814
0
5815
0
  if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
5816
0
    rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5817
0
    return nullptr;
5818
0
  }
5819
0
5820
0
  RefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
5821
0
5822
0
  // Don't notify; this node is still being created.
5823
0
  cdata->SetText(aData, false);
5824
0
5825
0
  return cdata.forget();
5826
0
}
5827
5828
already_AddRefed<ProcessingInstruction>
5829
nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
5830
                                         const nsAString& aData,
5831
                                         ErrorResult& rv) const
5832
0
{
5833
0
  nsresult res = nsContentUtils::CheckQName(aTarget, false);
5834
0
  if (NS_FAILED(res)) {
5835
0
    rv.Throw(res);
5836
0
    return nullptr;
5837
0
  }
5838
0
5839
0
  if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
5840
0
    rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5841
0
    return nullptr;
5842
0
  }
5843
0
5844
0
  RefPtr<ProcessingInstruction> pi =
5845
0
    NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
5846
0
5847
0
  return pi.forget();
5848
0
}
5849
5850
already_AddRefed<Attr>
5851
nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv)
5852
0
{
5853
0
  if (!mNodeInfoManager) {
5854
0
    rv.Throw(NS_ERROR_NOT_INITIALIZED);
5855
0
    return nullptr;
5856
0
  }
5857
0
5858
0
  nsresult res = nsContentUtils::CheckQName(aName, false);
5859
0
  if (NS_FAILED(res)) {
5860
0
    rv.Throw(res);
5861
0
    return nullptr;
5862
0
  }
5863
0
5864
0
  nsAutoString name;
5865
0
  if (IsHTMLDocument()) {
5866
0
    nsContentUtils::ASCIIToLower(aName, name);
5867
0
  } else {
5868
0
    name = aName;
5869
0
  }
5870
0
5871
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5872
0
  res = mNodeInfoManager->GetNodeInfo(name, nullptr, kNameSpaceID_None,
5873
0
                                      ATTRIBUTE_NODE, getter_AddRefs(nodeInfo));
5874
0
  if (NS_FAILED(res)) {
5875
0
    rv.Throw(res);
5876
0
    return nullptr;
5877
0
  }
5878
0
5879
0
  RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5880
0
                                    EmptyString());
5881
0
  return attribute.forget();
5882
0
}
5883
5884
already_AddRefed<Attr>
5885
nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
5886
                               const nsAString& aQualifiedName,
5887
                               ErrorResult& rv)
5888
0
{
5889
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5890
0
  rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5891
0
                                            aQualifiedName,
5892
0
                                            mNodeInfoManager,
5893
0
                                            ATTRIBUTE_NODE,
5894
0
                                            getter_AddRefs(nodeInfo));
5895
0
  if (rv.Failed()) {
5896
0
    return nullptr;
5897
0
  }
5898
0
5899
0
  RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5900
0
                                    EmptyString());
5901
0
  return attribute.forget();
5902
0
}
5903
5904
void
5905
nsIDocument::ResolveScheduledSVGPresAttrs()
5906
0
{
5907
0
  for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) {
5908
0
    nsSVGElement* svg = iter.Get()->GetKey();
5909
0
    svg->UpdateContentDeclarationBlock();
5910
0
  }
5911
0
  mLazySVGPresElements.Clear();
5912
0
}
5913
5914
already_AddRefed<nsSimpleContentList>
5915
nsIDocument::BlockedTrackingNodes() const
5916
0
{
5917
0
  RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
5918
0
5919
0
  nsTArray<nsWeakPtr> blockedTrackingNodes;
5920
0
  blockedTrackingNodes = mBlockedTrackingNodes;
5921
0
5922
0
  for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) {
5923
0
    nsWeakPtr weakNode = blockedTrackingNodes[i];
5924
0
    nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
5925
0
    // Consider only nodes to which we have managed to get strong references.
5926
0
    // Coping with nullptrs since it's expected for nodes to disappear when
5927
0
    // nobody else is referring to them.
5928
0
    if (node) {
5929
0
      list->AppendElement(node);
5930
0
    }
5931
0
  }
5932
0
5933
0
  return list.forget();
5934
0
}
5935
5936
void
5937
nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
5938
0
{
5939
0
  aSheetSet.Truncate();
5940
0
5941
0
  // Look through our sheets, find the selected set title
5942
0
  size_t count = SheetCount();
5943
0
  nsAutoString title;
5944
0
  for (size_t index = 0; index < count; index++) {
5945
0
    StyleSheet* sheet = SheetAt(index);
5946
0
    NS_ASSERTION(sheet, "Null sheet in sheet list!");
5947
0
5948
0
    if (sheet->Disabled()) {
5949
0
      // Disabled sheets don't affect the currently selected set
5950
0
      continue;
5951
0
    }
5952
0
5953
0
    sheet->GetTitle(title);
5954
0
5955
0
    if (aSheetSet.IsEmpty()) {
5956
0
      aSheetSet = title;
5957
0
    } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
5958
0
      // Sheets from multiple sets enabled; return null string, per spec.
5959
0
      SetDOMStringToNull(aSheetSet);
5960
0
      return;
5961
0
    }
5962
0
  }
5963
0
}
5964
5965
void
5966
nsIDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
5967
0
{
5968
0
  if (DOMStringIsNull(aSheetSet)) {
5969
0
    return;
5970
0
  }
5971
0
5972
0
  // Must update mLastStyleSheetSet before doing anything else with stylesheets
5973
0
  // or CSSLoaders.
5974
0
  mLastStyleSheetSet = aSheetSet;
5975
0
  EnableStyleSheetsForSetInternal(aSheetSet, true);
5976
0
}
5977
5978
void
5979
nsIDocument::SetPreferredStyleSheetSet(const nsAString& aSheetSet)
5980
0
{
5981
0
  mPreferredStyleSheetSet = aSheetSet;
5982
0
  // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
5983
0
  // spec.
5984
0
  if (DOMStringIsNull(mLastStyleSheetSet)) {
5985
0
    // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
5986
0
    // per spec.  The idea here is that we're changing our preferred set and
5987
0
    // that shouldn't change the value of lastStyleSheetSet.  Also, we're
5988
0
    // using the Internal version so we can update the CSSLoader and not have
5989
0
    // to worry about null strings.
5990
0
    EnableStyleSheetsForSetInternal(aSheetSet, true);
5991
0
  }
5992
0
}
5993
5994
DOMStringList*
5995
nsIDocument::StyleSheetSets()
5996
0
{
5997
0
  if (!mStyleSheetSetList) {
5998
0
    mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
5999
0
  }
6000
0
  return mStyleSheetSetList;
6001
0
}
6002
6003
void
6004
nsIDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
6005
0
{
6006
0
  // Per spec, passing in null is a no-op.
6007
0
  if (!DOMStringIsNull(aSheetSet)) {
6008
0
    // Note: must make sure to not change the CSSLoader's preferred sheet --
6009
0
    // that value should be equal to either our lastStyleSheetSet (if that's
6010
0
    // non-null) or to our preferredStyleSheetSet.  And this method doesn't
6011
0
    // change either of those.
6012
0
    EnableStyleSheetsForSetInternal(aSheetSet, false);
6013
0
  }
6014
0
}
6015
6016
void
6017
nsIDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
6018
                                             bool aUpdateCSSLoader)
6019
0
{
6020
0
  size_t count = SheetCount();
6021
0
  nsAutoString title;
6022
0
  for (size_t index = 0; index < count; index++) {
6023
0
    StyleSheet* sheet = SheetAt(index);
6024
0
    NS_ASSERTION(sheet, "Null sheet in sheet list!");
6025
0
6026
0
    sheet->GetTitle(title);
6027
0
    if (!title.IsEmpty()) {
6028
0
      sheet->SetEnabled(title.Equals(aSheetSet));
6029
0
    }
6030
0
  }
6031
0
  if (aUpdateCSSLoader) {
6032
0
    CSSLoader()->DocumentStyleSheetSetChanged();
6033
0
  }
6034
0
  if (nsIPresShell* shell = GetShell()) {
6035
0
    if (shell->StyleSet()->StyleSheetsHaveChanged()) {
6036
0
      shell->ApplicableStylesChanged();
6037
0
    }
6038
0
  }
6039
0
}
6040
6041
void
6042
nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const
6043
0
{
6044
0
  nsAutoCString charset;
6045
0
  GetDocumentCharacterSet()->Name(charset);
6046
0
  CopyASCIItoUTF16(charset, aCharacterSet);
6047
0
}
6048
6049
already_AddRefed<nsINode>
6050
nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
6051
0
{
6052
0
  nsINode* imported = &aNode;
6053
0
6054
0
  switch (imported->NodeType()) {
6055
0
    case DOCUMENT_NODE:
6056
0
    {
6057
0
      break;
6058
0
    }
6059
0
    case DOCUMENT_FRAGMENT_NODE:
6060
0
    case ATTRIBUTE_NODE:
6061
0
    case ELEMENT_NODE:
6062
0
    case PROCESSING_INSTRUCTION_NODE:
6063
0
    case TEXT_NODE:
6064
0
    case CDATA_SECTION_NODE:
6065
0
    case COMMENT_NODE:
6066
0
    case DOCUMENT_TYPE_NODE:
6067
0
    {
6068
0
      return nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager, nullptr, rv);
6069
0
    }
6070
0
    default:
6071
0
    {
6072
0
      NS_WARNING("Don't know how to clone this nodetype for importNode.");
6073
0
    }
6074
0
  }
6075
0
6076
0
  rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6077
0
  return nullptr;
6078
0
}
6079
6080
void
6081
nsIDocument::LoadBindingDocument(const nsAString& aURI,
6082
                                 nsIPrincipal& aSubjectPrincipal,
6083
                                 ErrorResult& rv)
6084
0
{
6085
0
  nsCOMPtr<nsIURI> uri;
6086
0
  rv = NS_NewURI(getter_AddRefs(uri), aURI, mCharacterSet, GetDocBaseURI());
6087
0
  if (rv.Failed()) {
6088
0
    return;
6089
0
  }
6090
0
6091
0
  BindingManager()->LoadBindingDocument(this, uri, &aSubjectPrincipal);
6092
0
}
6093
6094
Element*
6095
nsIDocument::GetBindingParent(nsINode& aNode)
6096
0
{
6097
0
  nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
6098
0
  if (!content)
6099
0
    return nullptr;
6100
0
6101
0
  nsIContent* bindingParent = content->GetBindingParent();
6102
0
  return bindingParent ? bindingParent->AsElement() : nullptr;
6103
0
}
6104
6105
static Element*
6106
GetElementByAttribute(Element* aElement, nsAtom* aAttrName,
6107
                      const nsAString& aAttrValue, bool aUniversalMatch)
6108
0
{
6109
0
  if (aUniversalMatch ? aElement->HasAttr(kNameSpaceID_None, aAttrName) :
6110
0
                        aElement->AttrValueIs(kNameSpaceID_None, aAttrName,
6111
0
                                              aAttrValue, eCaseMatters)) {
6112
0
    return aElement;
6113
0
  }
6114
0
6115
0
  for (nsIContent* child = aElement->GetFirstChild();
6116
0
       child;
6117
0
       child = child->GetNextSibling()) {
6118
0
    if (!child->IsElement()) {
6119
0
      continue;
6120
0
    }
6121
0
6122
0
    Element* matchedElement =
6123
0
      GetElementByAttribute(child->AsElement(), aAttrName, aAttrValue,
6124
0
                            aUniversalMatch);
6125
0
    if (matchedElement)
6126
0
      return matchedElement;
6127
0
  }
6128
0
6129
0
  return nullptr;
6130
0
}
6131
6132
Element*
6133
nsIDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
6134
                                            nsAtom* aAttrName,
6135
                                            const nsAString& aAttrValue) const
6136
0
{
6137
0
  nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
6138
0
  if (!nodeList)
6139
0
    return nullptr;
6140
0
6141
0
  uint32_t length = nodeList->Length();
6142
0
6143
0
  bool universalMatch = aAttrValue.EqualsLiteral("*");
6144
0
6145
0
  for (uint32_t i = 0; i < length; ++i) {
6146
0
    Element* current = Element::FromNode(nodeList->Item(i));
6147
0
    if (!current) {
6148
0
      continue;
6149
0
    }
6150
0
6151
0
    Element* matchedElm =
6152
0
      GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
6153
0
    if (matchedElm)
6154
0
      return matchedElm;
6155
0
  }
6156
0
6157
0
  return nullptr;
6158
0
}
6159
6160
Element*
6161
nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
6162
                                            const nsAString& aAttrName,
6163
                                            const nsAString& aAttrValue)
6164
0
{
6165
0
  RefPtr<nsAtom> attribute = NS_Atomize(aAttrName);
6166
0
6167
0
  return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
6168
0
}
6169
6170
nsINodeList*
6171
nsIDocument::GetAnonymousNodes(Element& aElement)
6172
0
{
6173
0
  return BindingManager()->GetAnonymousNodesFor(&aElement);
6174
0
}
6175
6176
already_AddRefed<nsRange>
6177
nsIDocument::CreateRange(ErrorResult& rv)
6178
0
{
6179
0
  RefPtr<nsRange> range = new nsRange(this);
6180
0
  nsresult res = range->CollapseTo(this, 0);
6181
0
  if (NS_FAILED(res)) {
6182
0
    rv.Throw(res);
6183
0
    return nullptr;
6184
0
  }
6185
0
6186
0
  return range.forget();
6187
0
}
6188
6189
already_AddRefed<NodeIterator>
6190
nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6191
                                NodeFilter* aFilter,
6192
                                ErrorResult& rv) const
6193
0
{
6194
0
  RefPtr<NodeIterator> iterator = new NodeIterator(&aRoot, aWhatToShow,
6195
0
                                                   aFilter);
6196
0
  return iterator.forget();
6197
0
}
6198
6199
already_AddRefed<TreeWalker>
6200
nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6201
                              NodeFilter* aFilter,
6202
                              ErrorResult& rv) const
6203
0
{
6204
0
  RefPtr<TreeWalker> walker = new TreeWalker(&aRoot, aWhatToShow, aFilter);
6205
0
  return walker.forget();
6206
0
}
6207
6208
6209
already_AddRefed<Location>
6210
nsIDocument::GetLocation() const
6211
0
{
6212
0
  nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject);
6213
0
6214
0
  if (!w) {
6215
0
    return nullptr;
6216
0
  }
6217
0
6218
0
  nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(w);
6219
0
  RefPtr<Location> loc = window->GetLocation();
6220
0
  return loc.forget();
6221
0
}
6222
6223
Element*
6224
nsIDocument::GetHtmlElement() const
6225
0
{
6226
0
  Element* rootElement = GetRootElement();
6227
0
  if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html))
6228
0
    return rootElement;
6229
0
  return nullptr;
6230
0
}
6231
6232
Element*
6233
nsIDocument::GetHtmlChildElement(nsAtom* aTag)
6234
0
{
6235
0
  Element* html = GetHtmlElement();
6236
0
  if (!html)
6237
0
    return nullptr;
6238
0
6239
0
  // Look for the element with aTag inside html. This needs to run
6240
0
  // forwards to find the first such element.
6241
0
  for (nsIContent* child = html->GetFirstChild();
6242
0
       child;
6243
0
       child = child->GetNextSibling()) {
6244
0
    if (child->IsHTMLElement(aTag))
6245
0
      return child->AsElement();
6246
0
  }
6247
0
  return nullptr;
6248
0
}
6249
6250
nsGenericHTMLElement*
6251
nsIDocument::GetBody()
6252
0
{
6253
0
  Element* html = GetHtmlElement();
6254
0
  if (!html) {
6255
0
    return nullptr;
6256
0
  }
6257
0
6258
0
  for (nsIContent* child = html->GetFirstChild();
6259
0
       child;
6260
0
       child = child->GetNextSibling()) {
6261
0
    if (child->IsHTMLElement(nsGkAtoms::body) ||
6262
0
        child->IsHTMLElement(nsGkAtoms::frameset)) {
6263
0
      return static_cast<nsGenericHTMLElement*>(child);
6264
0
    }
6265
0
  }
6266
0
6267
0
  return nullptr;
6268
0
}
6269
6270
void
6271
nsIDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv)
6272
0
{
6273
0
  nsCOMPtr<Element> root = GetRootElement();
6274
0
6275
0
  // The body element must be either a body tag or a frameset tag. And we must
6276
0
  // have a root element to be able to add kids to it.
6277
0
  if (!newBody ||
6278
0
      !newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset) ||
6279
0
      !root) {
6280
0
    rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
6281
0
    return;
6282
0
  }
6283
0
6284
0
  // Use DOM methods so that we pass through the appropriate security checks.
6285
0
  nsCOMPtr<Element> currentBody = GetBody();
6286
0
  if (currentBody) {
6287
0
    root->ReplaceChild(*newBody, *currentBody, rv);
6288
0
  } else {
6289
0
    root->AppendChild(*newBody, rv);
6290
0
  }
6291
0
}
6292
6293
HTMLSharedElement*
6294
nsIDocument::GetHead()
6295
0
{
6296
0
  return static_cast<HTMLSharedElement*>(GetHeadElement());
6297
0
}
6298
6299
Element*
6300
nsIDocument::GetTitleElement()
6301
0
{
6302
0
  // mMayHaveTitleElement will have been set to true if any HTML or SVG
6303
0
  // <title> element has been bound to this document. So if it's false,
6304
0
  // we know there is nothing to do here. This avoids us having to search
6305
0
  // the whole DOM if someone calls document.title on a large document
6306
0
  // without a title.
6307
0
  if (!mMayHaveTitleElement)
6308
0
    return nullptr;
6309
0
6310
0
  Element* root = GetRootElement();
6311
0
  if (root && root->IsSVGElement(nsGkAtoms::svg)) {
6312
0
    // In SVG, the document's title must be a child
6313
0
    for (nsIContent* child = root->GetFirstChild();
6314
0
         child; child = child->GetNextSibling()) {
6315
0
      if (child->IsSVGElement(nsGkAtoms::title)) {
6316
0
        return child->AsElement();
6317
0
      }
6318
0
    }
6319
0
    return nullptr;
6320
0
  }
6321
0
6322
0
  // We check the HTML namespace even for non-HTML documents, except SVG.  This
6323
0
  // matches the spec and the behavior of all tested browsers.
6324
0
  // We avoid creating a live nsContentList since we don't need to watch for DOM
6325
0
  // tree mutations.
6326
0
  RefPtr<nsContentList> list = new nsContentList(this, kNameSpaceID_XHTML,
6327
0
                                                 nsGkAtoms::title, nsGkAtoms::title,
6328
0
                                                 /* aDeep = */ true,
6329
0
                                                 /* aLiveList = */ false);
6330
0
6331
0
  nsIContent* first = list->Item(0, false);
6332
0
6333
0
  return first ? first->AsElement() : nullptr;
6334
0
}
6335
6336
void
6337
nsIDocument::GetTitle(nsAString& aTitle)
6338
0
{
6339
0
  aTitle.Truncate();
6340
0
6341
0
  Element* rootElement = GetRootElement();
6342
0
  if (!rootElement) {
6343
0
    return;
6344
0
  }
6345
0
6346
0
  nsAutoString tmp;
6347
0
6348
0
#ifdef MOZ_XUL
6349
0
  if (rootElement->IsXULElement()) {
6350
0
    rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
6351
0
  } else
6352
0
#endif
6353
0
  {
6354
0
    Element* title = GetTitleElement();
6355
0
    if (!title) {
6356
0
      return;
6357
0
    }
6358
0
    nsContentUtils::GetNodeTextContent(title, false, tmp);
6359
0
  }
6360
0
6361
0
  tmp.CompressWhitespace();
6362
0
  aTitle = tmp;
6363
0
}
6364
6365
void
6366
nsIDocument::SetTitle(const nsAString& aTitle, ErrorResult& aRv)
6367
0
{
6368
0
  Element* rootElement = GetRootElement();
6369
0
  if (!rootElement) {
6370
0
    return;
6371
0
  }
6372
0
6373
0
#ifdef MOZ_XUL
6374
0
  if (rootElement->IsXULElement()) {
6375
0
    aRv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
6376
0
                               aTitle, true);
6377
0
    return;
6378
0
  }
6379
0
#endif
6380
0
6381
0
  Maybe<mozAutoDocUpdate> updateBatch;
6382
0
  nsCOMPtr<Element> title = GetTitleElement();
6383
0
  if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
6384
0
    if (!title) {
6385
0
      // Batch updates so that mutation events don't change "the title
6386
0
      // element" under us
6387
0
      updateBatch.emplace(this, true);
6388
0
      RefPtr<mozilla::dom::NodeInfo> titleInfo =
6389
0
        mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
6390
0
                                      kNameSpaceID_SVG,
6391
0
                                      ELEMENT_NODE);
6392
0
      NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(),
6393
0
                       NOT_FROM_PARSER);
6394
0
      if (!title) {
6395
0
        return;
6396
0
      }
6397
0
      rootElement->InsertChildBefore(title, rootElement->GetFirstChild(), true);
6398
0
    }
6399
0
  } else if (rootElement->IsHTMLElement()) {
6400
0
    if (!title) {
6401
0
      // Batch updates so that mutation events don't change "the title
6402
0
      // element" under us
6403
0
      updateBatch.emplace(this, true);
6404
0
      Element* head = GetHeadElement();
6405
0
      if (!head) {
6406
0
        return;
6407
0
      }
6408
0
6409
0
      RefPtr<mozilla::dom::NodeInfo> titleInfo;
6410
0
      titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
6411
0
          kNameSpaceID_XHTML, ELEMENT_NODE);
6412
0
      title = NS_NewHTMLTitleElement(titleInfo.forget());
6413
0
      if (!title) {
6414
0
        return;
6415
0
      }
6416
0
6417
0
      head->AppendChildTo(title, true);
6418
0
    }
6419
0
  } else {
6420
0
    return;
6421
0
  }
6422
0
6423
0
  aRv = nsContentUtils::SetNodeTextContent(title, aTitle, false);
6424
0
}
6425
6426
void
6427
nsIDocument::NotifyPossibleTitleChange(bool aBoundTitleElement)
6428
0
{
6429
0
  NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
6430
0
               "Setting a title while unlinking or destroying the element?");
6431
0
  if (mInUnlinkOrDeletion) {
6432
0
    return;
6433
0
  }
6434
0
6435
0
  if (aBoundTitleElement) {
6436
0
    mMayHaveTitleElement = true;
6437
0
  }
6438
0
  if (mPendingTitleChangeEvent.IsPending())
6439
0
    return;
6440
0
6441
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
6442
0
  RefPtr<nsRunnableMethod<nsIDocument, void, false>> event =
6443
0
    NewNonOwningRunnableMethod("nsIDocument::DoNotifyPossibleTitleChange",
6444
0
                               this,
6445
0
                               &nsIDocument::DoNotifyPossibleTitleChange);
6446
0
  nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(event));
6447
0
  if (NS_SUCCEEDED(rv)) {
6448
0
    mPendingTitleChangeEvent = std::move(event);
6449
0
  }
6450
0
}
6451
6452
void
6453
nsIDocument::DoNotifyPossibleTitleChange()
6454
0
{
6455
0
  mPendingTitleChangeEvent.Forget();
6456
0
  mHaveFiredTitleChange = true;
6457
0
6458
0
  nsAutoString title;
6459
0
  GetTitle(title);
6460
0
6461
0
  nsCOMPtr<nsIPresShell> shell = GetShell();
6462
0
  if (shell) {
6463
0
    nsCOMPtr<nsISupports> container =
6464
0
      shell->GetPresContext()->GetContainerWeak();
6465
0
    if (container) {
6466
0
      nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
6467
0
      if (docShellWin) {
6468
0
        docShellWin->SetTitle(title);
6469
0
      }
6470
0
    }
6471
0
  }
6472
0
6473
0
  // Fire a DOM event for the title change.
6474
0
  nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
6475
0
                                      NS_LITERAL_STRING("DOMTitleChanged"),
6476
0
                                      CanBubble::eYes, Cancelable::eYes);
6477
0
}
6478
6479
already_AddRefed<BoxObject>
6480
nsIDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
6481
0
{
6482
0
  if (!aElement) {
6483
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
6484
0
    return nullptr;
6485
0
  }
6486
0
6487
0
  nsIDocument* doc = aElement->OwnerDoc();
6488
0
  if (doc != this) {
6489
0
    aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
6490
0
    return nullptr;
6491
0
  }
6492
0
6493
0
  if (!mHasWarnedAboutBoxObjects && !aElement->IsXULElement()) {
6494
0
    mHasWarnedAboutBoxObjects = true;
6495
0
    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
6496
0
                                    NS_LITERAL_CSTRING("BoxObjects"), this,
6497
0
                                    nsContentUtils::eDOM_PROPERTIES,
6498
0
                                    "UseOfGetBoxObjectForWarning");
6499
0
  }
6500
0
6501
0
  if (!mBoxObjectTable) {
6502
0
    mBoxObjectTable = new nsRefPtrHashtable<nsPtrHashKey<nsIContent>, BoxObject>(6);
6503
0
  }
6504
0
6505
0
  RefPtr<BoxObject> boxObject;
6506
0
  auto entry = mBoxObjectTable->LookupForAdd(aElement);
6507
0
  if (entry) {
6508
0
    boxObject = entry.Data();
6509
0
    return boxObject.forget();
6510
0
  }
6511
0
6512
0
  int32_t namespaceID;
6513
0
  RefPtr<nsAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
6514
0
#ifdef MOZ_XUL
6515
0
  if (namespaceID == kNameSpaceID_XUL) {
6516
0
    if (tag == nsGkAtoms::tree) {
6517
0
      boxObject = new TreeBoxObject();
6518
0
    } else {
6519
0
      boxObject = new BoxObject();
6520
0
    }
6521
0
  } else
6522
0
#endif // MOZ_XUL
6523
0
  {
6524
0
    boxObject = new BoxObject();
6525
0
  }
6526
0
6527
0
  boxObject->Init(aElement);
6528
0
  entry.OrInsert([&boxObject]() { return boxObject; });
6529
0
6530
0
  return boxObject.forget();
6531
0
}
6532
6533
void
6534
nsIDocument::ClearBoxObjectFor(nsIContent* aContent)
6535
0
{
6536
0
  if (mBoxObjectTable) {
6537
0
    if (auto entry = mBoxObjectTable->Lookup(aContent)) {
6538
0
      nsPIBoxObject* boxObject = entry.Data();
6539
0
      boxObject->Clear();
6540
0
      entry.Remove();
6541
0
    }
6542
0
  }
6543
0
}
6544
6545
already_AddRefed<MediaQueryList>
6546
nsIDocument::MatchMedia(const nsAString& aMediaQueryList,
6547
                        CallerType aCallerType)
6548
0
{
6549
0
  RefPtr<MediaQueryList> result =
6550
0
    new MediaQueryList(this, aMediaQueryList, aCallerType);
6551
0
6552
0
  mDOMMediaQueryLists.insertBack(result);
6553
0
6554
0
  return result.forget();
6555
0
}
6556
6557
void
6558
nsIDocument::FlushSkinBindings()
6559
0
{
6560
0
  BindingManager()->FlushSkinBindings();
6561
0
}
6562
6563
void
6564
nsIDocument::SetMayStartLayout(bool aMayStartLayout)
6565
0
{
6566
0
  mMayStartLayout = aMayStartLayout;
6567
0
  if (MayStartLayout()) {
6568
0
    // Before starting layout, check whether we're a toplevel chrome
6569
0
    // window.  If we are, setup some state so that we don't have to restyle
6570
0
    // the whole tree after StartLayout.
6571
0
    if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
6572
0
        // We're the chrome document!
6573
0
        win->BeforeStartLayout();
6574
0
    }
6575
0
    ReadyState state = GetReadyStateEnum();
6576
0
    if (state >= READYSTATE_INTERACTIVE) {
6577
0
      // DOMContentLoaded has fired already.
6578
0
      MaybeResolveReadyForIdle();
6579
0
    }
6580
0
  }
6581
0
}
6582
6583
nsresult
6584
nsIDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
6585
0
{
6586
0
  mInitializableFrameLoaders.RemoveElement(aLoader);
6587
0
  // Don't even try to initialize.
6588
0
  if (mInDestructor) {
6589
0
    NS_WARNING("Trying to initialize a frame loader while"
6590
0
               "document is being deleted");
6591
0
    return NS_ERROR_FAILURE;
6592
0
  }
6593
0
6594
0
  mInitializableFrameLoaders.AppendElement(aLoader);
6595
0
  if (!mFrameLoaderRunner) {
6596
0
    mFrameLoaderRunner =
6597
0
      NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders",
6598
0
                        this,
6599
0
                        &nsIDocument::MaybeInitializeFinalizeFrameLoaders);
6600
0
    NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6601
0
    nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6602
0
  }
6603
0
  return NS_OK;
6604
0
}
6605
6606
nsresult
6607
nsIDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer)
6608
0
{
6609
0
  mInitializableFrameLoaders.RemoveElement(aLoader);
6610
0
  if (mInDestructor) {
6611
0
    return NS_ERROR_FAILURE;
6612
0
  }
6613
0
6614
0
  mFrameLoaderFinalizers.AppendElement(aFinalizer);
6615
0
  if (!mFrameLoaderRunner) {
6616
0
    mFrameLoaderRunner =
6617
0
      NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders",
6618
0
                        this,
6619
0
                        &nsIDocument::MaybeInitializeFinalizeFrameLoaders);
6620
0
    NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6621
0
    nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6622
0
  }
6623
0
  return NS_OK;
6624
0
}
6625
6626
void
6627
nsIDocument::MaybeInitializeFinalizeFrameLoaders()
6628
0
{
6629
0
  if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
6630
0
    // This method will be recalled when mUpdateNestLevel drops to 0,
6631
0
    // or when !mDelayFrameLoaderInitialization.
6632
0
    mFrameLoaderRunner = nullptr;
6633
0
    return;
6634
0
  }
6635
0
6636
0
  // We're not in an update, but it is not safe to run scripts, so
6637
0
  // postpone frameloader initialization and finalization.
6638
0
  if (!nsContentUtils::IsSafeToRunScript()) {
6639
0
    if (!mInDestructor && !mFrameLoaderRunner &&
6640
0
        (mInitializableFrameLoaders.Length() ||
6641
0
         mFrameLoaderFinalizers.Length())) {
6642
0
      mFrameLoaderRunner =
6643
0
        NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders",
6644
0
                          this,
6645
0
                          &nsIDocument::MaybeInitializeFinalizeFrameLoaders);
6646
0
      nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6647
0
    }
6648
0
    return;
6649
0
  }
6650
0
  mFrameLoaderRunner = nullptr;
6651
0
6652
0
  // Don't use a temporary array for mInitializableFrameLoaders, because
6653
0
  // loading a frame may cause some other frameloader to be removed from the
6654
0
  // array. But be careful to keep the loader alive when starting the load!
6655
0
  while (mInitializableFrameLoaders.Length()) {
6656
0
    RefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
6657
0
    mInitializableFrameLoaders.RemoveElementAt(0);
6658
0
    NS_ASSERTION(loader, "null frameloader in the array?");
6659
0
    loader->ReallyStartLoading();
6660
0
  }
6661
0
6662
0
  uint32_t length = mFrameLoaderFinalizers.Length();
6663
0
  if (length > 0) {
6664
0
    nsTArray<nsCOMPtr<nsIRunnable> > finalizers;
6665
0
    mFrameLoaderFinalizers.SwapElements(finalizers);
6666
0
    for (uint32_t i = 0; i < length; ++i) {
6667
0
      finalizers[i]->Run();
6668
0
    }
6669
0
  }
6670
0
}
6671
6672
void
6673
nsIDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
6674
0
{
6675
0
  uint32_t length = mInitializableFrameLoaders.Length();
6676
0
  for (uint32_t i = 0; i < length; ++i) {
6677
0
    if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
6678
0
      mInitializableFrameLoaders.RemoveElementAt(i);
6679
0
      return;
6680
0
    }
6681
0
  }
6682
0
}
6683
6684
nsIDocument*
6685
nsIDocument::RequestExternalResource(nsIURI* aURI,
6686
                                     nsIURI* aReferrer,
6687
                                     uint32_t aReferrerPolicy,
6688
                                     nsINode* aRequestingNode,
6689
                                     ExternalResourceLoad** aPendingLoad)
6690
0
{
6691
0
  MOZ_ASSERT(aURI, "Must have a URI");
6692
0
  MOZ_ASSERT(aRequestingNode, "Must have a node");
6693
0
  if (mDisplayDocument) {
6694
0
    return mDisplayDocument->RequestExternalResource(aURI,
6695
0
                                                     aReferrer,
6696
0
                                                     aReferrerPolicy,
6697
0
                                                     aRequestingNode,
6698
0
                                                     aPendingLoad);
6699
0
  }
6700
0
6701
0
  return mExternalResourceMap.RequestResource(aURI, aReferrer, aReferrerPolicy,
6702
0
                                              aRequestingNode, this,
6703
0
                                              aPendingLoad);
6704
0
}
6705
6706
void
6707
nsIDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
6708
0
{
6709
0
  mExternalResourceMap.EnumerateResources(aCallback, aData);
6710
0
}
6711
6712
nsSMILAnimationController*
6713
nsIDocument::GetAnimationController()
6714
0
{
6715
0
  // We create the animation controller lazily because most documents won't want
6716
0
  // one and only SVG documents and the like will call this
6717
0
  if (mAnimationController)
6718
0
    return mAnimationController;
6719
0
  // Refuse to create an Animation Controller for data documents.
6720
0
  if (mLoadedAsData || mLoadedAsInteractiveData)
6721
0
    return nullptr;
6722
0
6723
0
  mAnimationController = new nsSMILAnimationController(this);
6724
0
6725
0
  // If there's a presContext then check the animation mode and pause if
6726
0
  // necessary.
6727
0
  nsPresContext* context = GetPresContext();
6728
0
  if (mAnimationController && context &&
6729
0
      context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
6730
0
    mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
6731
0
  }
6732
0
6733
0
  // If we're hidden (or being hidden), notify the newly-created animation
6734
0
  // controller. (Skip this check for SVG-as-an-image documents, though,
6735
0
  // because they don't get OnPageShow / OnPageHide calls).
6736
0
  if (!mIsShowing && !mIsBeingUsedAsImage) {
6737
0
    mAnimationController->OnPageHide();
6738
0
  }
6739
0
6740
0
  return mAnimationController;
6741
0
}
6742
6743
PendingAnimationTracker*
6744
nsIDocument::GetOrCreatePendingAnimationTracker()
6745
0
{
6746
0
  if (!mPendingAnimationTracker) {
6747
0
    mPendingAnimationTracker = new PendingAnimationTracker(this);
6748
0
  }
6749
0
6750
0
  return mPendingAnimationTracker;
6751
0
}
6752
6753
/**
6754
 * Retrieve the "direction" property of the document.
6755
 *
6756
 * @lina 01/09/2001
6757
 */
6758
void
6759
nsIDocument::GetDir(nsAString& aDirection) const
6760
0
{
6761
0
  aDirection.Truncate();
6762
0
  Element* rootElement = GetHtmlElement();
6763
0
  if (rootElement) {
6764
0
    static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
6765
0
  }
6766
0
}
6767
6768
/**
6769
 * Set the "direction" property of the document.
6770
 *
6771
 * @lina 01/09/2001
6772
 */
6773
void
6774
nsIDocument::SetDir(const nsAString& aDirection)
6775
0
{
6776
0
  Element* rootElement = GetHtmlElement();
6777
0
  if (rootElement) {
6778
0
    rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
6779
0
                         aDirection, true);
6780
0
  }
6781
0
}
6782
6783
nsIHTMLCollection*
6784
nsIDocument::Images()
6785
0
{
6786
0
  if (!mImages) {
6787
0
    mImages = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::img, nsGkAtoms::img);
6788
0
  }
6789
0
  return mImages;
6790
0
}
6791
6792
nsIHTMLCollection*
6793
nsIDocument::Embeds()
6794
0
{
6795
0
  if (!mEmbeds) {
6796
0
    mEmbeds = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::embed, nsGkAtoms::embed);
6797
0
  }
6798
0
  return mEmbeds;
6799
0
}
6800
6801
static bool
6802
MatchLinks(Element* aElement, int32_t aNamespaceID,
6803
           nsAtom* aAtom, void* aData)
6804
0
{
6805
0
  return aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
6806
0
         aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
6807
0
}
6808
6809
nsIHTMLCollection*
6810
nsIDocument::Links()
6811
0
{
6812
0
  if (!mLinks) {
6813
0
    mLinks = new nsContentList(this, MatchLinks, nullptr, nullptr);
6814
0
  }
6815
0
  return mLinks;
6816
0
}
6817
6818
nsIHTMLCollection*
6819
nsIDocument::Forms()
6820
0
{
6821
0
  if (!mForms) {
6822
0
    // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
6823
0
    mForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form, nsGkAtoms::form);
6824
0
  }
6825
0
6826
0
  return mForms;
6827
0
}
6828
6829
nsIHTMLCollection*
6830
nsIDocument::Scripts()
6831
0
{
6832
0
  if (!mScripts) {
6833
0
    mScripts = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::script, nsGkAtoms::script);
6834
0
  }
6835
0
  return mScripts;
6836
0
}
6837
6838
nsIHTMLCollection*
6839
nsIDocument::Applets()
6840
0
{
6841
0
  if (!mApplets) {
6842
0
    mApplets = new nsEmptyContentList(this);
6843
0
  }
6844
0
  return mApplets;
6845
0
}
6846
6847
static bool
6848
MatchAnchors(Element* aElement, int32_t aNamespaceID,
6849
             nsAtom* aAtom, void* aData)
6850
0
{
6851
0
  return aElement->IsHTMLElement(nsGkAtoms::a) &&
6852
0
         aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::name);
6853
0
}
6854
6855
nsIHTMLCollection*
6856
nsIDocument::Anchors()
6857
0
{
6858
0
  if (!mAnchors) {
6859
0
    mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr);
6860
0
  }
6861
0
  return mAnchors;
6862
0
}
6863
6864
/* static */
6865
bool
6866
nsIDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
6867
                                nsAtom* aAtom, void* aData)
6868
0
{
6869
0
  MOZ_ASSERT(aElement, "Must have element to work with!");
6870
0
6871
0
  if (!aElement->HasName()) {
6872
0
    return false;
6873
0
  }
6874
0
6875
0
  nsString* elementName = static_cast<nsString*>(aData);
6876
0
  return
6877
0
    aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
6878
0
    aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
6879
0
                          *elementName, eCaseMatters);
6880
0
}
6881
6882
/* static */
6883
void*
6884
nsIDocument::UseExistingNameString(nsINode* aRootNode, const nsString* aName)
6885
0
{
6886
0
  return const_cast<nsString*>(aName);
6887
0
}
6888
6889
nsresult
6890
nsIDocument::GetDocumentURI(nsString& aDocumentURI) const
6891
0
{
6892
0
  if (mDocumentURI) {
6893
0
    nsAutoCString uri;
6894
0
    nsresult rv = mDocumentURI->GetSpec(uri);
6895
0
    NS_ENSURE_SUCCESS(rv, rv);
6896
0
6897
0
    CopyUTF8toUTF16(uri, aDocumentURI);
6898
0
  } else {
6899
0
    aDocumentURI.Truncate();
6900
0
  }
6901
0
6902
0
  return NS_OK;
6903
0
}
6904
6905
// Alias of above
6906
nsresult
6907
nsIDocument::GetURL(nsString& aURL) const
6908
0
{
6909
0
  return GetDocumentURI(aURL);
6910
0
}
6911
6912
void
6913
nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI, CallerType aCallerType,
6914
                                  ErrorResult& aRv) const
6915
0
{
6916
0
  if (!mChromeXHRDocURI || aCallerType != CallerType::System) {
6917
0
    aRv = GetDocumentURI(aDocumentURI);
6918
0
    return;
6919
0
  }
6920
0
6921
0
  nsAutoCString uri;
6922
0
  nsresult res = mChromeXHRDocURI->GetSpec(uri);
6923
0
  if (NS_FAILED(res)) {
6924
0
    aRv.Throw(res);
6925
0
    return;
6926
0
  }
6927
0
  CopyUTF8toUTF16(uri, aDocumentURI);
6928
0
}
6929
6930
nsIURI*
6931
nsIDocument::GetDocumentURIObject() const
6932
0
{
6933
0
  if (!mChromeXHRDocURI) {
6934
0
    return GetDocumentURI();
6935
0
  }
6936
0
6937
0
  return mChromeXHRDocURI;
6938
0
}
6939
6940
void
6941
nsIDocument::GetCompatMode(nsString& aCompatMode) const
6942
0
{
6943
0
  NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
6944
0
               mCompatMode == eCompatibility_AlmostStandards ||
6945
0
               mCompatMode == eCompatibility_FullStandards,
6946
0
               "mCompatMode is neither quirks nor strict for this document");
6947
0
6948
0
  if (mCompatMode == eCompatibility_NavQuirks) {
6949
0
    aCompatMode.AssignLiteral("BackCompat");
6950
0
  } else {
6951
0
    aCompatMode.AssignLiteral("CSS1Compat");
6952
0
  }
6953
0
}
6954
6955
void
6956
nsDOMAttributeMap::BlastSubtreeToPieces(nsINode* aNode)
6957
0
{
6958
0
  if (Element* element = Element::FromNode(aNode)) {
6959
0
    if (const nsDOMAttributeMap* map = element->GetAttributeMap()) {
6960
0
      while (true) {
6961
0
        RefPtr<Attr> attr;
6962
0
        {
6963
0
          // Use an iterator to get an arbitrary attribute from the
6964
0
          // cache. The iterator must be destroyed before any other
6965
0
          // operations on mAttributeCache, to avoid hash table
6966
0
          // assertions.
6967
0
          auto iter = map->mAttributeCache.ConstIter();
6968
0
          if (iter.Done()) {
6969
0
            break;
6970
0
          }
6971
0
          attr = iter.UserData();
6972
0
        }
6973
0
6974
0
        BlastSubtreeToPieces(attr);
6975
0
6976
0
        DebugOnly<nsresult> rv =
6977
0
          element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
6978
0
                             attr->NodeInfo()->NameAtom(),
6979
0
                             false);
6980
0
6981
0
        // XXX Should we abort here?
6982
0
        NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
6983
0
      }
6984
0
    }
6985
0
  }
6986
0
6987
0
  while (aNode->HasChildren()) {
6988
0
    nsIContent* node = aNode->GetFirstChild();
6989
0
    BlastSubtreeToPieces(node);
6990
0
    aNode->RemoveChildNode(node, false);
6991
0
  }
6992
0
}
6993
6994
nsINode*
6995
nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
6996
0
{
6997
0
  nsINode* adoptedNode = &aAdoptedNode;
6998
0
6999
0
  // Scope firing mutation events so that we don't carry any state that
7000
0
  // might be stale
7001
0
  {
7002
0
    nsINode* parent = adoptedNode->GetParentNode();
7003
0
    if (parent) {
7004
0
      nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent);
7005
0
    }
7006
0
  }
7007
0
7008
0
  nsAutoScriptBlocker scriptBlocker;
7009
0
7010
0
  switch (adoptedNode->NodeType()) {
7011
0
    case ATTRIBUTE_NODE:
7012
0
    {
7013
0
      // Remove from ownerElement.
7014
0
      RefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
7015
0
7016
0
      nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
7017
0
      if (rv.Failed()) {
7018
0
        return nullptr;
7019
0
      }
7020
0
7021
0
      if (ownerElement) {
7022
0
        RefPtr<Attr> newAttr =
7023
0
          ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
7024
0
        if (rv.Failed()) {
7025
0
          return nullptr;
7026
0
        }
7027
0
7028
0
        newAttr.swap(adoptedAttr);
7029
0
      }
7030
0
7031
0
      break;
7032
0
    }
7033
0
    case DOCUMENT_FRAGMENT_NODE:
7034
0
    {
7035
0
      if (adoptedNode->IsShadowRoot()) {
7036
0
        rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
7037
0
        return nullptr;
7038
0
      }
7039
0
      MOZ_FALLTHROUGH;
7040
0
    }
7041
0
    case ELEMENT_NODE:
7042
0
    case PROCESSING_INSTRUCTION_NODE:
7043
0
    case TEXT_NODE:
7044
0
    case CDATA_SECTION_NODE:
7045
0
    case COMMENT_NODE:
7046
0
    case DOCUMENT_TYPE_NODE:
7047
0
    {
7048
0
      // Don't allow adopting a node's anonymous subtree out from under it.
7049
0
      if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
7050
0
        rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7051
0
        return nullptr;
7052
0
      }
7053
0
7054
0
      // We don't want to adopt an element into its own contentDocument or into
7055
0
      // a descendant contentDocument, so we check if the frameElement of this
7056
0
      // document or any of its parents is the adopted node or one of its
7057
0
      // descendants.
7058
0
      nsIDocument *doc = this;
7059
0
      do {
7060
0
        if (nsPIDOMWindowOuter *win = doc->GetWindow()) {
7061
0
          nsCOMPtr<nsINode> node = win->GetFrameElementInternal();
7062
0
          if (node &&
7063
0
              nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
7064
0
            rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
7065
0
            return nullptr;
7066
0
          }
7067
0
        }
7068
0
      } while ((doc = doc->GetParentDocument()));
7069
0
7070
0
      // Remove from parent.
7071
0
      nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
7072
0
      if (parent) {
7073
0
        parent->RemoveChildNode(adoptedNode->AsContent(), true);
7074
0
      } else {
7075
0
        MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc());
7076
0
7077
0
        // If we're adopting a node that's not in a document, it might still
7078
0
        // have a binding applied. Remove the binding from the element now
7079
0
        // that it's getting adopted into a new document.
7080
0
        // TODO Fully tear down the binding.
7081
0
        if (Element* element = Element::FromNode(adoptedNode)) {
7082
0
          element->SetXBLBinding(nullptr);
7083
0
        }
7084
0
      }
7085
0
7086
0
      break;
7087
0
    }
7088
0
    case DOCUMENT_NODE:
7089
0
    {
7090
0
      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7091
0
      return nullptr;
7092
0
    }
7093
0
    default:
7094
0
    {
7095
0
      NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
7096
0
7097
0
      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7098
0
      return nullptr;
7099
0
    }
7100
0
  }
7101
0
7102
0
  nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
7103
0
  bool sameDocument = oldDocument == this;
7104
0
7105
0
  AutoJSContext cx;
7106
0
  JS::Rooted<JSObject*> newScope(cx, nullptr);
7107
0
  if (!sameDocument) {
7108
0
    newScope = GetWrapper();
7109
0
    if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
7110
0
      // Make sure cx is in a semi-sane compartment before we call WrapNative.
7111
0
      // It's kind of irrelevant, given that we're passing aAllowWrapping =
7112
0
      // false, and documents should always insist on being wrapped in an
7113
0
      // canonical scope. But we try to pass something sane anyway.
7114
0
      JSAutoRealm ar(cx, GetScopeObject()->GetGlobalJSObject());
7115
0
      JS::Rooted<JS::Value> v(cx);
7116
0
      rv = nsContentUtils::WrapNative(cx, this, this, &v,
7117
0
                                      /* aAllowWrapping = */ false);
7118
0
      if (rv.Failed())
7119
0
        return nullptr;
7120
0
      newScope = &v.toObject();
7121
0
    }
7122
0
  }
7123
0
7124
0
  nsCOMArray<nsINode> nodesWithProperties;
7125
0
  nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
7126
0
                     newScope, nodesWithProperties, rv);
7127
0
  if (rv.Failed()) {
7128
0
    // Disconnect all nodes from their parents, since some have the old document
7129
0
    // as their ownerDocument and some have this as their ownerDocument.
7130
0
    nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
7131
0
7132
0
    if (!sameDocument && oldDocument) {
7133
0
      for (nsINode* node : nodesWithProperties) {
7134
0
        // Remove all properties.
7135
0
        oldDocument->PropertyTable().DeleteAllPropertiesFor(node);
7136
0
      }
7137
0
    }
7138
0
7139
0
    return nullptr;
7140
0
  }
7141
0
7142
0
  if (!sameDocument && oldDocument) {
7143
0
    nsPropertyTable& oldTable = oldDocument->PropertyTable();
7144
0
    nsPropertyTable& newTable = PropertyTable();
7145
0
    for (nsINode* node : nodesWithProperties) {
7146
0
      rv = oldTable.TransferOrDeleteAllPropertiesFor(node, newTable);
7147
0
    }
7148
0
7149
0
    if (rv.Failed()) {
7150
0
      // Disconnect all nodes from their parents.
7151
0
      nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
7152
0
7153
0
      return nullptr;
7154
0
    }
7155
0
  }
7156
0
7157
0
  NS_ASSERTION(adoptedNode->OwnerDoc() == this,
7158
0
               "Should still be in the document we just got adopted into");
7159
0
7160
0
  return adoptedNode;
7161
0
}
7162
7163
nsViewportInfo
7164
nsIDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
7165
0
{
7166
0
  MOZ_ASSERT(mPresShell);
7167
0
7168
0
  // Compute the CSS-to-LayoutDevice pixel scale as the product of the
7169
0
  // widget scale and the full zoom.
7170
0
  nsPresContext* context = mPresShell->GetPresContext();
7171
0
  // When querying the full zoom, get it from the device context rather than
7172
0
  // directly from the pres context, because the device context's value can
7173
0
  // include an adjustment necessay to keep the number of app units per device
7174
0
  // pixel an integer, and we want the adjusted value.
7175
0
  float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
7176
0
  fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
7177
0
  CSSToLayoutDeviceScale layoutDeviceScale = context ? context->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
7178
0
7179
0
  CSSToScreenScale defaultScale = layoutDeviceScale
7180
0
                                * LayoutDeviceToScreenScale(1.0);
7181
0
7182
0
  // Special behaviour for desktop mode, provided we are not on an about: page
7183
0
  nsPIDOMWindowOuter* win = GetWindow();
7184
0
  if (win && win->IsDesktopModeViewport() && !IsAboutPage()) {
7185
0
    CSSCoord viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
7186
0
    CSSToScreenScale scaleToFit(aDisplaySize.width / viewportWidth);
7187
0
    float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
7188
0
    CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
7189
0
    ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit);
7190
0
    return nsViewportInfo(fakeDesktopSize,
7191
0
                          scaleToFit,
7192
0
                          /*allowZoom*/ true);
7193
0
  }
7194
0
7195
0
  if (!gfxPrefs::MetaViewportEnabled()) {
7196
0
    return nsViewportInfo(aDisplaySize,
7197
0
                          defaultScale,
7198
0
                          /*allowZoom*/ false);
7199
0
  }
7200
0
7201
0
  // In cases where the width of the CSS viewport is less than or equal to the width
7202
0
  // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
7203
0
  // behaviour. See bug 941995 for details.
7204
0
7205
0
  switch (mViewportType) {
7206
0
  case DisplayWidthHeight:
7207
0
    return nsViewportInfo(aDisplaySize,
7208
0
                          defaultScale,
7209
0
                          /*allowZoom*/ true);
7210
0
  case Unknown:
7211
0
  {
7212
0
    nsAutoString viewport;
7213
0
    GetHeaderData(nsGkAtoms::viewport, viewport);
7214
0
    if (viewport.IsEmpty()) {
7215
0
      // If the docType specifies that we are on a site optimized for mobile,
7216
0
      // then we want to return specially crafted defaults for the viewport info.
7217
0
      RefPtr<DocumentType> docType = GetDoctype();
7218
0
      if (docType) {
7219
0
        nsAutoString docId;
7220
0
        docType->GetPublicId(docId);
7221
0
        if ((docId.Find("WAP") != -1) ||
7222
0
            (docId.Find("Mobile") != -1) ||
7223
0
            (docId.Find("WML") != -1))
7224
0
        {
7225
0
          // We're making an assumption that the docType can't change here
7226
0
          mViewportType = DisplayWidthHeight;
7227
0
          return nsViewportInfo(aDisplaySize,
7228
0
                                defaultScale,
7229
0
                                /*allowZoom*/true);
7230
0
        }
7231
0
      }
7232
0
7233
0
      nsAutoString handheldFriendly;
7234
0
      GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
7235
0
      if (handheldFriendly.EqualsLiteral("true")) {
7236
0
        mViewportType = DisplayWidthHeight;
7237
0
        return nsViewportInfo(aDisplaySize, defaultScale,
7238
0
                              /*allowZoom*/true);
7239
0
      }
7240
0
    }
7241
0
7242
0
    nsAutoString minScaleStr;
7243
0
    GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
7244
0
7245
0
    nsresult errorCode;
7246
0
    mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
7247
0
7248
0
    if (NS_FAILED(errorCode)) {
7249
0
      mScaleMinFloat = kViewportMinScale;
7250
0
    }
7251
0
7252
0
    mScaleMinFloat = mozilla::clamped(
7253
0
        mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
7254
0
7255
0
    nsAutoString maxScaleStr;
7256
0
    GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
7257
0
7258
0
    // We define a special error code variable for the scale and max scale,
7259
0
    // because they are used later (see the width calculations).
7260
0
    nsresult scaleMaxErrorCode;
7261
0
    mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
7262
0
7263
0
    if (NS_FAILED(scaleMaxErrorCode)) {
7264
0
      mScaleMaxFloat = kViewportMaxScale;
7265
0
    }
7266
0
7267
0
    mScaleMaxFloat = mozilla::clamped(
7268
0
        mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
7269
0
7270
0
    nsAutoString scaleStr;
7271
0
    GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
7272
0
7273
0
    nsresult scaleErrorCode;
7274
0
    mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
7275
0
7276
0
    nsAutoString widthStr, heightStr;
7277
0
7278
0
    GetHeaderData(nsGkAtoms::viewport_height, heightStr);
7279
0
    GetHeaderData(nsGkAtoms::viewport_width, widthStr);
7280
0
7281
0
    mAutoSize = false;
7282
0
7283
0
    if (widthStr.EqualsLiteral("device-width")) {
7284
0
      mAutoSize = true;
7285
0
    }
7286
0
7287
0
    if (widthStr.IsEmpty() &&
7288
0
        (heightStr.EqualsLiteral("device-height") ||
7289
0
         (mScaleFloat.scale == 1.0)))
7290
0
    {
7291
0
      mAutoSize = true;
7292
0
    }
7293
0
7294
0
    nsresult widthErrorCode, heightErrorCode;
7295
0
    mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
7296
0
    mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
7297
0
7298
0
    // If width or height has not been set to a valid number by this point,
7299
0
    // fall back to a default value.
7300
0
    mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
7301
0
    mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
7302
0
7303
0
    // If the width is set to some unrecognized value, and there is no
7304
0
    // height set, treat it as if device-width were specified.
7305
0
    if ((!mValidWidth && !widthStr.IsEmpty()) && !mValidHeight) {
7306
0
      mAutoSize = true;
7307
0
    }
7308
0
7309
0
    mAllowZoom = true;
7310
0
    nsAutoString userScalable;
7311
0
    GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
7312
0
7313
0
    if ((userScalable.EqualsLiteral("0")) ||
7314
0
        (userScalable.EqualsLiteral("no")) ||
7315
0
        (userScalable.EqualsLiteral("false"))) {
7316
0
      mAllowZoom = false;
7317
0
    }
7318
0
7319
0
    mScaleStrEmpty = scaleStr.IsEmpty();
7320
0
    mWidthStrEmpty = widthStr.IsEmpty();
7321
0
    mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
7322
0
    mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
7323
0
7324
0
    mViewportType = Specified;
7325
0
    mViewportOverflowType = ViewportOverflowType::NoOverflow;
7326
0
    MOZ_FALLTHROUGH;
7327
0
  }
7328
0
  case Specified:
7329
0
  default:
7330
0
    LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat;
7331
0
    LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat;
7332
0
    bool effectiveValidMaxScale = mValidMaxScale;
7333
0
    bool effectiveAllowZoom = mAllowZoom;
7334
0
    if (gfxPrefs::ForceUserScalable()) {
7335
0
      // If the pref to force user-scalable is enabled, we ignore the values
7336
0
      // from the meta-viewport tag for these properties and just assume they
7337
0
      // allow the page to be scalable. Note in particular that this code is
7338
0
      // in the "Specified" branch of the enclosing switch statement, so that
7339
0
      // calls to GetViewportInfo always use the latest value of the
7340
0
      // ForceUserScalable pref. Other codepaths that return nsViewportInfo
7341
0
      // instances are all consistent with ForceUserScalable() already.
7342
0
      effectiveMinScale = kViewportMinScale;
7343
0
      effectiveMaxScale = kViewportMaxScale;
7344
0
      effectiveValidMaxScale = true;
7345
0
      effectiveAllowZoom = true;
7346
0
    }
7347
0
7348
0
    CSSSize size = mViewportSize;
7349
0
7350
0
    if (!mValidWidth) {
7351
0
      if (mValidHeight && !aDisplaySize.IsEmpty()) {
7352
0
        size.width = size.height * aDisplaySize.width / aDisplaySize.height;
7353
0
      } else {
7354
0
        // Stretch CSS pixel size of viewport to keep device pixel size
7355
0
        // unchanged after full zoom applied.
7356
0
        // See bug 974242.
7357
0
        size.width = gfxPrefs::DesktopViewportWidth() / fullZoom;
7358
0
      }
7359
0
    }
7360
0
7361
0
    if (!mValidHeight) {
7362
0
      if (!aDisplaySize.IsEmpty()) {
7363
0
        size.height = size.width * aDisplaySize.height / aDisplaySize.width;
7364
0
      } else {
7365
0
        size.height = size.width;
7366
0
      }
7367
0
    }
7368
0
7369
0
    CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
7370
0
    CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale;
7371
0
    CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale;
7372
0
7373
0
    if (mAutoSize) {
7374
0
      // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
7375
0
      CSSToScreenScale defaultPixelScale = layoutDeviceScale * LayoutDeviceToScreenScale(1.0f);
7376
0
      size = ScreenSize(aDisplaySize) / defaultPixelScale;
7377
0
    }
7378
0
7379
0
    size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width));
7380
0
7381
0
    // Also recalculate the default zoom, if it wasn't specified in the metadata,
7382
0
    // and the width is specified.
7383
0
    if (mScaleStrEmpty && !mWidthStrEmpty) {
7384
0
      CSSToScreenScale defaultScale(float(aDisplaySize.width) / size.width);
7385
0
      scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
7386
0
    }
7387
0
7388
0
    size.height = clamped(size.height, float(kViewportMinSize.height), float(kViewportMaxSize.height));
7389
0
7390
0
    // We need to perform a conversion, but only if the initial or maximum
7391
0
    // scale were set explicitly by the user.
7392
0
    if (mValidScaleFloat && scaleFloat >= scaleMinFloat && scaleFloat <= scaleMaxFloat) {
7393
0
      CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
7394
0
      size.width = std::max(size.width, displaySize.width);
7395
0
      size.height = std::max(size.height, displaySize.height);
7396
0
    } else if (effectiveValidMaxScale) {
7397
0
      CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
7398
0
      size.width = std::max(size.width, displaySize.width);
7399
0
      size.height = std::max(size.height, displaySize.height);
7400
0
    }
7401
0
7402
0
    return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
7403
0
                          mAutoSize, effectiveAllowZoom);
7404
0
  }
7405
0
}
7406
7407
void
7408
nsIDocument::UpdateViewportOverflowType(nscoord aScrolledWidth,
7409
                                        nscoord aScrollportWidth)
7410
0
{
7411
#ifdef DEBUG
7412
  MOZ_ASSERT(mPresShell);
7413
  nsPresContext* pc = GetPresContext();
7414
  MOZ_ASSERT(pc->GetViewportScrollStylesOverride().mHorizontal ==
7415
             NS_STYLE_OVERFLOW_HIDDEN,
7416
             "Should only be called when viewport has overflow-x: hidden");
7417
  MOZ_ASSERT(aScrolledWidth > aScrollportWidth,
7418
             "Should only be called when viewport is overflowed");
7419
  MOZ_ASSERT(IsTopLevelContentDocument(),
7420
             "Should only be called for top-level content document");
7421
#endif // DEBUG
7422
7423
0
  if (!gfxPrefs::MetaViewportEnabled() ||
7424
0
      (GetWindow() && GetWindow()->IsDesktopModeViewport())) {
7425
0
    mViewportOverflowType = ViewportOverflowType::Desktop;
7426
0
    return;
7427
0
  }
7428
0
7429
0
  if (mViewportType == Unknown) {
7430
0
    // The viewport info hasn't been initialized yet. Suppose we would
7431
0
    // get here again at some point after it's initialized.
7432
0
    return;
7433
0
  }
7434
0
7435
0
  static const LayoutDeviceToScreenScale
7436
0
    kBlinkDefaultMinScale = LayoutDeviceToScreenScale(0.25f);
7437
0
  LayoutDeviceToScreenScale minScale;
7438
0
  if (mViewportType == DisplayWidthHeight) {
7439
0
    minScale = kBlinkDefaultMinScale;
7440
0
  } else {
7441
0
    if (mScaleMinFloat == kViewportMinScale) {
7442
0
      minScale = kBlinkDefaultMinScale;
7443
0
    } else {
7444
0
      minScale = mScaleMinFloat;
7445
0
    }
7446
0
  }
7447
0
7448
0
  // If the content has overflowed with minimum scale applied, don't
7449
0
  // change it, otherwise update the overflow type.
7450
0
  if (mViewportOverflowType != ViewportOverflowType::MinScaleSize) {
7451
0
    if (aScrolledWidth * minScale.scale < aScrollportWidth) {
7452
0
      mViewportOverflowType = ViewportOverflowType::ButNotMinScaleSize;
7453
0
    } else {
7454
0
      mViewportOverflowType = ViewportOverflowType::MinScaleSize;
7455
0
    }
7456
0
  }
7457
0
}
7458
7459
EventListenerManager*
7460
nsDocument::GetOrCreateListenerManager()
7461
0
{
7462
0
  if (!mListenerManager) {
7463
0
    mListenerManager =
7464
0
      new EventListenerManager(static_cast<EventTarget*>(this));
7465
0
    SetFlags(NODE_HAS_LISTENERMANAGER);
7466
0
  }
7467
0
7468
0
  return mListenerManager;
7469
0
}
7470
7471
EventListenerManager*
7472
nsDocument::GetExistingListenerManager() const
7473
0
{
7474
0
  return mListenerManager;
7475
0
}
7476
7477
void
7478
nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor)
7479
0
{
7480
0
  if (mDocGroup && aVisitor.mEvent->mMessage != eVoidEvent &&
7481
0
      !mIgnoreDocGroupMismatches) {
7482
0
    mDocGroup->ValidateAccess();
7483
0
  }
7484
0
7485
0
  aVisitor.mCanHandle = true;
7486
0
   // FIXME! This is a hack to make middle mouse paste working also in Editor.
7487
0
   // Bug 329119
7488
0
  aVisitor.mForceContentDispatch = true;
7489
0
7490
0
  // Load events must not propagate to |window| object, see bug 335251.
7491
0
  if (aVisitor.mEvent->mMessage != eLoad) {
7492
0
    nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetWindow());
7493
0
    aVisitor.SetParentTarget(
7494
0
      window ? window->GetTargetForEventTargetChain() : nullptr, false);
7495
0
  }
7496
0
}
7497
7498
already_AddRefed<Event>
7499
nsIDocument::CreateEvent(const nsAString& aEventType, CallerType aCallerType,
7500
                         ErrorResult& rv) const
7501
0
{
7502
0
  nsPresContext* presContext = GetPresContext();
7503
0
7504
0
  // Create event even without presContext.
7505
0
  RefPtr<Event> ev =
7506
0
    EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this), presContext,
7507
0
                                 nullptr, aEventType, aCallerType);
7508
0
  if (!ev) {
7509
0
    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7510
0
    return nullptr;
7511
0
  }
7512
0
  WidgetEvent* e = ev->WidgetEventPtr();
7513
0
  e->mFlags.mBubbles = false;
7514
0
  e->mFlags.mCancelable = false;
7515
0
  return ev.forget();
7516
0
}
7517
7518
void
7519
nsIDocument::FlushPendingNotifications(FlushType aType)
7520
0
{
7521
0
 mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style);
7522
0
  FlushPendingNotifications(flush);
7523
0
}
7524
7525
void
7526
nsIDocument::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
7527
0
{
7528
0
  FlushType flushType = aFlush.mFlushType;
7529
0
7530
0
  nsDocumentOnStack dos(this);
7531
0
7532
0
  // We need to flush the sink for non-HTML documents (because the XML
7533
0
  // parser still does insertion with deferred notifications).  We
7534
0
  // also need to flush the sink if this is a layout-related flush, to
7535
0
  // make sure that layout is started as needed.  But we can skip that
7536
0
  // part if we have no presshell or if it's already done an initial
7537
0
  // reflow.
7538
0
  if ((!IsHTMLDocument() ||
7539
0
       (flushType > FlushType::ContentAndNotify && mPresShell &&
7540
0
        !mPresShell->DidInitialize())) &&
7541
0
      (mParser || mWeakSink)) {
7542
0
    nsCOMPtr<nsIContentSink> sink;
7543
0
    if (mParser) {
7544
0
      sink = mParser->GetContentSink();
7545
0
    } else {
7546
0
      sink = do_QueryReferent(mWeakSink);
7547
0
      if (!sink) {
7548
0
        mWeakSink = nullptr;
7549
0
      }
7550
0
    }
7551
0
    // Determine if it is safe to flush the sink notifications
7552
0
    // by determining if it safe to flush all the presshells.
7553
0
    if (sink && (flushType == FlushType::Content || IsSafeToFlush())) {
7554
0
      sink->FlushPendingNotifications(flushType);
7555
0
    }
7556
0
  }
7557
0
7558
0
  // Should we be flushing pending binding constructors in here?
7559
0
7560
0
  if (flushType <= FlushType::ContentAndNotify) {
7561
0
    // Nothing to do here
7562
0
    return;
7563
0
  }
7564
0
7565
0
  // If we have a parent we must flush the parent too to ensure that our
7566
0
  // container is reflowed if its size was changed.  But if it's not safe to
7567
0
  // flush ourselves, then don't flush the parent, since that can cause things
7568
0
  // like resizes of our frame's widget, which we can't handle while flushing
7569
0
  // is unsafe.
7570
0
  // Since media queries mean that a size change of our container can
7571
0
  // affect style, we need to promote a style flush on ourself to a
7572
0
  // layout flush on our parent, since we need our container to be the
7573
0
  // correct size to determine the correct style.
7574
0
  if (mParentDocument && IsSafeToFlush()) {
7575
0
    mozilla::ChangesToFlush parentFlush = aFlush;
7576
0
    if (flushType >= FlushType::Style) {
7577
0
      parentFlush.mFlushType = std::max(FlushType::Layout, flushType);
7578
0
    }
7579
0
    mParentDocument->FlushPendingNotifications(parentFlush);
7580
0
  }
7581
0
7582
0
  if (nsIPresShell* shell = GetShell()) {
7583
0
    shell->FlushPendingNotifications(aFlush);
7584
0
  }
7585
0
}
7586
7587
static bool
7588
Copy(nsIDocument* aDocument, void* aData)
7589
0
{
7590
0
  auto* resources = static_cast<nsTArray<nsCOMPtr<nsIDocument>>*>(aData);
7591
0
  resources->AppendElement(aDocument);
7592
0
  return true;
7593
0
}
7594
7595
void
7596
nsIDocument::FlushExternalResources(FlushType aType)
7597
0
{
7598
0
  NS_ASSERTION(aType >= FlushType::Style,
7599
0
    "should only need to flush for style or higher in external resources");
7600
0
  if (GetDisplayDocument()) {
7601
0
    return;
7602
0
  }
7603
0
7604
0
  nsTArray<nsCOMPtr<nsIDocument>> resources;
7605
0
  EnumerateExternalResources(Copy, &resources);
7606
0
7607
0
  for (uint32_t i = 0; i < resources.Length(); i++) {
7608
0
    resources[i]->FlushPendingNotifications(aType);
7609
0
  }
7610
0
}
7611
7612
void
7613
nsIDocument::SetXMLDeclaration(const char16_t* aVersion,
7614
                               const char16_t* aEncoding,
7615
                               const int32_t aStandalone)
7616
0
{
7617
0
  if (!aVersion || *aVersion == '\0') {
7618
0
    mXMLDeclarationBits = 0;
7619
0
    return;
7620
0
  }
7621
0
7622
0
  mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
7623
0
7624
0
  if (aEncoding && *aEncoding != '\0') {
7625
0
    mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
7626
0
  }
7627
0
7628
0
  if (aStandalone == 1) {
7629
0
    mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
7630
0
                           XML_DECLARATION_BITS_STANDALONE_YES;
7631
0
  }
7632
0
  else if (aStandalone == 0) {
7633
0
    mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
7634
0
  }
7635
0
}
7636
7637
void
7638
nsIDocument::GetXMLDeclaration(nsAString& aVersion,
7639
                               nsAString& aEncoding,
7640
                               nsAString& aStandalone)
7641
0
{
7642
0
  aVersion.Truncate();
7643
0
  aEncoding.Truncate();
7644
0
  aStandalone.Truncate();
7645
0
7646
0
  if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
7647
0
    return;
7648
0
  }
7649
0
7650
0
  // always until we start supporting 1.1 etc.
7651
0
  aVersion.AssignLiteral("1.0");
7652
0
7653
0
  if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
7654
0
    // This is what we have stored, not necessarily what was written
7655
0
    // in the original
7656
0
    GetCharacterSet(aEncoding);
7657
0
  }
7658
0
7659
0
  if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
7660
0
    if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
7661
0
      aStandalone.AssignLiteral("yes");
7662
0
    } else {
7663
0
      aStandalone.AssignLiteral("no");
7664
0
    }
7665
0
  }
7666
0
}
7667
7668
bool
7669
nsIDocument::IsScriptEnabled()
7670
0
{
7671
0
  // If this document is sandboxed without 'allow-scripts'
7672
0
  // script is not enabled
7673
0
  if (HasScriptsBlockedBySandbox()) {
7674
0
    return false;
7675
0
  }
7676
0
7677
0
  nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
7678
0
  if (!globalObject || !globalObject->GetGlobalJSObject()) {
7679
0
    return false;
7680
0
  }
7681
0
7682
0
  return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed();
7683
0
}
7684
7685
void
7686
nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
7687
0
{
7688
0
  PRTime modDate = 0;
7689
0
  nsresult rv;
7690
0
7691
0
  nsCOMPtr<nsIHttpChannel> httpChannel;
7692
0
  rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
7693
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
7694
0
    return;
7695
0
  }
7696
0
7697
0
  if (httpChannel) {
7698
0
    nsAutoCString tmp;
7699
0
    rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
7700
0
                                        tmp);
7701
0
7702
0
    if (NS_SUCCEEDED(rv)) {
7703
0
      PRTime time;
7704
0
      PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
7705
0
      if (st == PR_SUCCESS) {
7706
0
        modDate = time;
7707
0
      }
7708
0
    }
7709
0
7710
0
    // The misspelled key 'referer' is as per the HTTP spec
7711
0
    rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
7712
0
                                       mReferrer);
7713
0
7714
0
    static const char *const headers[] = {
7715
0
      "default-style",
7716
0
      "content-style-type",
7717
0
      "content-language",
7718
0
      "content-disposition",
7719
0
      "refresh",
7720
0
      "x-dns-prefetch-control",
7721
0
      "x-frame-options",
7722
0
      "referrer-policy",
7723
0
      // add more http headers if you need
7724
0
      // XXXbz don't add content-location support without reading bug
7725
0
      // 238654 and its dependencies/dups first.
7726
0
      0
7727
0
    };
7728
0
7729
0
    nsAutoCString headerVal;
7730
0
    const char *const *name = headers;
7731
0
    while (*name) {
7732
0
      rv =
7733
0
        httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
7734
0
      if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
7735
0
        RefPtr<nsAtom> key = NS_Atomize(*name);
7736
0
        SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
7737
0
      }
7738
0
      ++name;
7739
0
    }
7740
0
  } else {
7741
0
    nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
7742
0
    if (fileChannel) {
7743
0
      nsCOMPtr<nsIFile> file;
7744
0
      fileChannel->GetFile(getter_AddRefs(file));
7745
0
      if (file) {
7746
0
        PRTime msecs;
7747
0
        rv = file->GetLastModifiedTime(&msecs);
7748
0
7749
0
        if (NS_SUCCEEDED(rv)) {
7750
0
          modDate = msecs * int64_t(PR_USEC_PER_MSEC);
7751
0
        }
7752
0
      }
7753
0
    } else {
7754
0
      nsAutoCString contentDisp;
7755
0
      rv = aChannel->GetContentDispositionHeader(contentDisp);
7756
0
      if (NS_SUCCEEDED(rv)) {
7757
0
        SetHeaderData(nsGkAtoms::headerContentDisposition,
7758
0
                      NS_ConvertASCIItoUTF16(contentDisp));
7759
0
      }
7760
0
    }
7761
0
  }
7762
0
7763
0
  mLastModified.Truncate();
7764
0
  if (modDate != 0) {
7765
0
    GetFormattedTimeString(modDate, mLastModified);
7766
0
  }
7767
0
}
7768
7769
already_AddRefed<Element>
7770
nsIDocument::CreateElem(const nsAString& aName, nsAtom *aPrefix,
7771
                        int32_t aNamespaceID, const nsAString* aIs)
7772
0
{
7773
#ifdef DEBUG
7774
  nsAutoString qName;
7775
  if (aPrefix) {
7776
    aPrefix->ToString(qName);
7777
    qName.Append(':');
7778
  }
7779
  qName.Append(aName);
7780
7781
  // Note: "a:b:c" is a valid name in non-namespaces XML, and
7782
  // nsIDocument::CreateElement can call us with such a name and no prefix,
7783
  // which would cause an error if we just used true here.
7784
  bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
7785
  NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
7786
               "Don't pass invalid prefixes to nsDocument::CreateElem, "
7787
               "check caller.");
7788
#endif
7789
7790
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
7791
0
  mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
7792
0
                                ELEMENT_NODE, getter_AddRefs(nodeInfo));
7793
0
  NS_ENSURE_TRUE(nodeInfo, nullptr);
7794
0
7795
0
  nsCOMPtr<Element> element;
7796
0
  nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
7797
0
                              NOT_FROM_PARSER, aIs);
7798
0
  return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
7799
0
}
7800
7801
bool
7802
nsIDocument::IsSafeToFlush() const
7803
0
{
7804
0
  nsIPresShell* shell = GetShell();
7805
0
  if (!shell)
7806
0
    return true;
7807
0
7808
0
  return shell->IsSafeToFlush();
7809
0
}
7810
7811
void
7812
nsIDocument::Sanitize()
7813
0
{
7814
0
  // Sanitize the document by resetting all password fields and any form
7815
0
  // fields with autocomplete=off to their default values.  We do this now,
7816
0
  // instead of when the presentation is restored, to offer some protection
7817
0
  // in case there is ever an exploit that allows a cached document to be
7818
0
  // accessed from a different document.
7819
0
7820
0
  // First locate all input elements, regardless of whether they are
7821
0
  // in a form, and reset the password and autocomplete=off elements.
7822
0
7823
0
  RefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
7824
0
7825
0
  nsAutoString value;
7826
0
7827
0
  uint32_t length = nodes->Length(true);
7828
0
  for (uint32_t i = 0; i < length; ++i) {
7829
0
    NS_ASSERTION(nodes->Item(i), "null item in node list!");
7830
0
7831
0
    RefPtr<HTMLInputElement> input = HTMLInputElement::FromNodeOrNull(nodes->Item(i));
7832
0
    if (!input)
7833
0
      continue;
7834
0
7835
0
    bool resetValue = false;
7836
0
7837
0
    input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
7838
0
    if (value.LowerCaseEqualsLiteral("off")) {
7839
0
      resetValue = true;
7840
0
    } else {
7841
0
      input->GetType(value);
7842
0
      if (value.LowerCaseEqualsLiteral("password"))
7843
0
        resetValue = true;
7844
0
    }
7845
0
7846
0
    if (resetValue) {
7847
0
      input->Reset();
7848
0
    }
7849
0
  }
7850
0
7851
0
  // Now locate all _form_ elements that have autocomplete=off and reset them
7852
0
  nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
7853
0
7854
0
  length = nodes->Length(true);
7855
0
  for (uint32_t i = 0; i < length; ++i) {
7856
0
    NS_ASSERTION(nodes->Item(i), "null item in nodelist");
7857
0
7858
0
    HTMLFormElement* form = HTMLFormElement::FromNode(nodes->Item(i));
7859
0
    if (!form)
7860
0
      continue;
7861
0
7862
0
    form->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, value);
7863
0
    if (value.LowerCaseEqualsLiteral("off"))
7864
0
      form->Reset();
7865
0
  }
7866
0
}
7867
7868
void
7869
nsIDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
7870
0
{
7871
0
  if (!mSubDocuments) {
7872
0
    return;
7873
0
  }
7874
0
7875
0
  // PLDHashTable::Iterator can't handle modifications while iterating so we
7876
0
  // copy all entries to an array first before calling any callbacks.
7877
0
  AutoTArray<nsCOMPtr<nsIDocument>, 8> subdocs;
7878
0
  for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
7879
0
    auto entry = static_cast<SubDocMapEntry*>(iter.Get());
7880
0
    nsIDocument* subdoc = entry->mSubDocument;
7881
0
    if (subdoc) {
7882
0
      subdocs.AppendElement(subdoc);
7883
0
    }
7884
0
  }
7885
0
  for (auto subdoc : subdocs) {
7886
0
    if (!aCallback(subdoc, aData)) {
7887
0
      break;
7888
0
    }
7889
0
  }
7890
0
}
7891
7892
void
7893
nsIDocument::CollectDescendantDocuments(
7894
  nsTArray<nsCOMPtr<nsIDocument>>& aDescendants,
7895
  nsDocTestFunc aCallback) const
7896
0
{
7897
0
  if (!mSubDocuments) {
7898
0
    return;
7899
0
  }
7900
0
7901
0
  for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
7902
0
    auto entry = static_cast<SubDocMapEntry*>(iter.Get());
7903
0
    const nsIDocument* subdoc = entry->mSubDocument;
7904
0
    if (subdoc) {
7905
0
      if (aCallback(subdoc)) {
7906
0
        aDescendants.AppendElement(entry->mSubDocument);
7907
0
      }
7908
0
      subdoc->CollectDescendantDocuments(aDescendants, aCallback);
7909
0
    }
7910
0
  }
7911
0
}
7912
7913
#ifdef DEBUG_bryner
7914
#define DEBUG_PAGE_CACHE
7915
#endif
7916
7917
bool
7918
nsIDocument::CanSavePresentation(nsIRequest *aNewRequest)
7919
0
{
7920
0
  if (!IsBFCachingAllowed()) {
7921
0
    return false;
7922
0
  }
7923
0
7924
0
  if (EventHandlingSuppressed()) {
7925
0
    return false;
7926
0
  }
7927
0
7928
0
  // Do not allow suspended windows to be placed in the
7929
0
  // bfcache.  This method is also used to verify a document
7930
0
  // coming out of the bfcache is ok to restore, though.  So
7931
0
  // we only want to block suspend windows that aren't also
7932
0
  // frozen.
7933
0
  nsPIDOMWindowInner* win = GetInnerWindow();
7934
0
  if (win && win->IsSuspended() && !win->IsFrozen()) {
7935
0
    return false;
7936
0
  }
7937
0
7938
0
  // Check our event listener manager for unload/beforeunload listeners.
7939
0
  nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
7940
0
  if (piTarget) {
7941
0
    EventListenerManager* manager = piTarget->GetExistingListenerManager();
7942
0
    if (manager && manager->HasUnloadListeners()) {
7943
0
      return false;
7944
0
    }
7945
0
  }
7946
0
7947
0
  // Check if we have pending network requests
7948
0
  nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
7949
0
  if (loadGroup) {
7950
0
    nsCOMPtr<nsISimpleEnumerator> requests;
7951
0
    loadGroup->GetRequests(getter_AddRefs(requests));
7952
0
7953
0
    bool hasMore = false;
7954
0
7955
0
    // We want to bail out if we have any requests other than aNewRequest (or
7956
0
    // in the case when aNewRequest is a part of a multipart response the base
7957
0
    // channel the multipart response is coming in on).
7958
0
    nsCOMPtr<nsIChannel> baseChannel;
7959
0
    nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
7960
0
    if (part) {
7961
0
      part->GetBaseChannel(getter_AddRefs(baseChannel));
7962
0
    }
7963
0
7964
0
    while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
7965
0
      nsCOMPtr<nsISupports> elem;
7966
0
      requests->GetNext(getter_AddRefs(elem));
7967
0
7968
0
      nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
7969
0
      if (request && request != aNewRequest && request != baseChannel) {
7970
0
        // Favicon loads don't need to block caching.
7971
0
        nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
7972
0
        if (channel) {
7973
0
          nsCOMPtr<nsILoadInfo> li;
7974
0
          channel->GetLoadInfo(getter_AddRefs(li));
7975
0
          if (li) {
7976
0
            if (li->InternalContentPolicyType() == nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) {
7977
0
              continue;
7978
0
            }
7979
0
          }
7980
0
        }
7981
#ifdef DEBUG_PAGE_CACHE
7982
        nsAutoCString requestName, docSpec;
7983
        request->GetName(requestName);
7984
        if (mDocumentURI)
7985
          mDocumentURI->GetSpec(docSpec);
7986
7987
        printf("document %s has request %s\n",
7988
               docSpec.get(), requestName.get());
7989
#endif
7990
0
        return false;
7991
0
      }
7992
0
    }
7993
0
  }
7994
0
7995
0
  // Check if we have active GetUserMedia use
7996
0
  if (MediaManager::Exists() && win &&
7997
0
      MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
7998
0
    return false;
7999
0
  }
8000
0
8001
0
#ifdef MOZ_WEBRTC
8002
0
  // Check if we have active PeerConnections
8003
0
  if (win && win->HasActivePeerConnections()) {
8004
0
    return false;
8005
0
  }
8006
0
#endif // MOZ_WEBRTC
8007
0
8008
0
  // Don't save presentations for documents containing EME content, so that
8009
0
  // CDMs reliably shutdown upon user navigation.
8010
0
  if (ContainsEMEContent()) {
8011
0
    return false;
8012
0
  }
8013
0
8014
0
  // Don't save presentations for documents containing MSE content, to
8015
0
  // reduce memory usage.
8016
0
  if (ContainsMSEContent()) {
8017
0
    return false;
8018
0
  }
8019
0
8020
0
  if (mSubDocuments) {
8021
0
    for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
8022
0
      auto entry = static_cast<SubDocMapEntry*>(iter.Get());
8023
0
      nsIDocument* subdoc = entry->mSubDocument;
8024
0
8025
0
      // The aIgnoreRequest we were passed is only for us, so don't pass it on.
8026
0
      bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
8027
0
      if (!canCache) {
8028
0
        return false;
8029
0
      }
8030
0
    }
8031
0
  }
8032
0
8033
0
8034
0
  if (win) {
8035
0
    auto* globalWindow = nsGlobalWindowInner::Cast(win);
8036
0
#ifdef MOZ_WEBSPEECH
8037
0
    if (globalWindow->HasActiveSpeechSynthesis()) {
8038
0
      return false;
8039
0
    }
8040
0
#endif
8041
0
    if (globalWindow->HasUsedVR()) {
8042
0
      return false;
8043
0
    }
8044
0
  }
8045
0
8046
0
  return true;
8047
0
}
8048
8049
void
8050
nsDocument::Destroy()
8051
0
{
8052
0
  // The ContentViewer wants to release the document now.  So, tell our content
8053
0
  // to drop any references to the document so that it can be destroyed.
8054
0
  if (mIsGoingAway)
8055
0
    return;
8056
0
8057
0
  mIsGoingAway = true;
8058
0
8059
0
  ScriptLoader()->Destroy();
8060
0
  SetScriptGlobalObject(nullptr);
8061
0
  RemovedFromDocShell();
8062
0
8063
0
  bool oldVal = mInUnlinkOrDeletion;
8064
0
  mInUnlinkOrDeletion = true;
8065
0
8066
#ifdef DEBUG
8067
  uint32_t oldChildCount = GetChildCount();
8068
#endif
8069
8070
0
  for (nsIContent* child = GetFirstChild(); child;
8071
0
       child = child->GetNextSibling()) {
8072
0
    child->DestroyContent();
8073
0
    MOZ_ASSERT(child->GetParentNode() == this);
8074
0
  }
8075
0
  MOZ_ASSERT(oldChildCount == GetChildCount());
8076
0
8077
0
  mInUnlinkOrDeletion = oldVal;
8078
0
8079
0
  mLayoutHistoryState = nullptr;
8080
0
8081
0
  // Shut down our external resource map.  We might not need this for
8082
0
  // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
8083
0
  // tearing down all those frame trees right now is the right thing to do.
8084
0
  mExternalResourceMap.Shutdown();
8085
0
}
8086
8087
void
8088
nsDocument::RemovedFromDocShell()
8089
0
{
8090
0
  if (mRemovedFromDocShell)
8091
0
    return;
8092
0
8093
0
  mRemovedFromDocShell = true;
8094
0
  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8095
0
8096
0
  for (nsIContent* child = GetFirstChild(); child;
8097
0
       child = child->GetNextSibling()) {
8098
0
    child->SaveSubtreeState();
8099
0
  }
8100
0
}
8101
8102
already_AddRefed<nsILayoutHistoryState>
8103
nsIDocument::GetLayoutHistoryState() const
8104
0
{
8105
0
  nsCOMPtr<nsILayoutHistoryState> state;
8106
0
  if (!mScriptGlobalObject) {
8107
0
    state = mLayoutHistoryState;
8108
0
  } else {
8109
0
    nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
8110
0
    if (docShell) {
8111
0
      docShell->GetLayoutHistoryState(getter_AddRefs(state));
8112
0
    }
8113
0
  }
8114
0
8115
0
  return state.forget();
8116
0
}
8117
8118
void
8119
nsIDocument::EnsureOnloadBlocker()
8120
0
{
8121
0
  // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8122
0
  // -- it's not ours.
8123
0
  if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
8124
0
    nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8125
0
    if (loadGroup) {
8126
0
      // Check first to see if mOnloadBlocker is in the loadgroup.
8127
0
      nsCOMPtr<nsISimpleEnumerator> requests;
8128
0
      loadGroup->GetRequests(getter_AddRefs(requests));
8129
0
8130
0
      bool hasMore = false;
8131
0
      while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8132
0
        nsCOMPtr<nsISupports> elem;
8133
0
        requests->GetNext(getter_AddRefs(elem));
8134
0
        nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8135
0
        if (request && request == mOnloadBlocker) {
8136
0
          return;
8137
0
        }
8138
0
      }
8139
0
8140
0
      // Not in the loadgroup, so add it.
8141
0
      loadGroup->AddRequest(mOnloadBlocker, nullptr);
8142
0
    }
8143
0
  }
8144
0
}
8145
8146
void
8147
nsDocument::AsyncBlockOnload()
8148
0
{
8149
0
  while (mAsyncOnloadBlockCount) {
8150
0
    --mAsyncOnloadBlockCount;
8151
0
    BlockOnload();
8152
0
  }
8153
0
}
8154
8155
void
8156
nsDocument::BlockOnload()
8157
0
{
8158
0
  if (mDisplayDocument) {
8159
0
    mDisplayDocument->BlockOnload();
8160
0
    return;
8161
0
  }
8162
0
8163
0
  // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8164
0
  // -- it's not ours.
8165
0
  if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
8166
0
    if (!nsContentUtils::IsSafeToRunScript()) {
8167
0
      // Because AddRequest may lead to OnStateChange calls in chrome,
8168
0
      // block onload only when there are no script blockers.
8169
0
      ++mAsyncOnloadBlockCount;
8170
0
      if (mAsyncOnloadBlockCount == 1) {
8171
0
        nsContentUtils::AddScriptRunner(NewRunnableMethod(
8172
0
          "nsDocument::AsyncBlockOnload", this, &nsDocument::AsyncBlockOnload));
8173
0
      }
8174
0
      return;
8175
0
    }
8176
0
    nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8177
0
    if (loadGroup) {
8178
0
      loadGroup->AddRequest(mOnloadBlocker, nullptr);
8179
0
    }
8180
0
  }
8181
0
  ++mOnloadBlockCount;
8182
0
}
8183
8184
void
8185
nsDocument::UnblockOnload(bool aFireSync)
8186
0
{
8187
0
  if (mDisplayDocument) {
8188
0
    mDisplayDocument->UnblockOnload(aFireSync);
8189
0
    return;
8190
0
  }
8191
0
8192
0
  if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
8193
0
    MOZ_ASSERT_UNREACHABLE("More UnblockOnload() calls than BlockOnload() "
8194
0
                           "calls; dropping call");
8195
0
    return;
8196
0
  }
8197
0
8198
0
  --mOnloadBlockCount;
8199
0
8200
0
  if (mOnloadBlockCount == 0) {
8201
0
    if (mScriptGlobalObject) {
8202
0
      // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
8203
0
      // is null, it's not ours.
8204
0
      if (aFireSync && mAsyncOnloadBlockCount == 0) {
8205
0
        // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
8206
0
        ++mOnloadBlockCount;
8207
0
        DoUnblockOnload();
8208
0
      } else {
8209
0
        PostUnblockOnloadEvent();
8210
0
      }
8211
0
    } else if (mIsBeingUsedAsImage) {
8212
0
      // To correctly unblock onload for a document that contains an SVG
8213
0
      // image, we need to know when all of the SVG document's resources are
8214
0
      // done loading, in a way comparable to |window.onload|. We fire this
8215
0
      // event to indicate that the SVG should be considered fully loaded.
8216
0
      // Because scripting is disabled on SVG-as-image documents, this event
8217
0
      // is not accessible to content authors. (See bug 837315.)
8218
0
      RefPtr<AsyncEventDispatcher> asyncDispatcher =
8219
0
        new AsyncEventDispatcher(this,
8220
0
                                 NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
8221
0
                                 CanBubble::eNo,
8222
0
                                 ChromeOnlyDispatch::eNo);
8223
0
      asyncDispatcher->PostDOMEvent();
8224
0
    }
8225
0
  }
8226
0
}
8227
8228
class nsUnblockOnloadEvent : public Runnable {
8229
public:
8230
  explicit nsUnblockOnloadEvent(nsIDocument* aDoc)
8231
    : mozilla::Runnable("nsUnblockOnloadEvent")
8232
    , mDoc(aDoc)
8233
0
  {
8234
0
  }
8235
0
  NS_IMETHOD Run() override {
8236
0
    mDoc->DoUnblockOnload();
8237
0
    return NS_OK;
8238
0
  }
8239
private:
8240
  RefPtr<nsIDocument> mDoc;
8241
};
8242
8243
void
8244
nsIDocument::PostUnblockOnloadEvent()
8245
0
{
8246
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
8247
0
  nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
8248
0
  nsresult rv =
8249
0
    Dispatch(TaskCategory::Other, evt.forget());
8250
0
  if (NS_SUCCEEDED(rv)) {
8251
0
    // Stabilize block count so we don't post more events while this one is up
8252
0
    ++mOnloadBlockCount;
8253
0
  } else {
8254
0
    NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
8255
0
  }
8256
0
}
8257
8258
void
8259
nsIDocument::DoUnblockOnload()
8260
0
{
8261
0
  MOZ_ASSERT(!mDisplayDocument,
8262
0
                  "Shouldn't get here for resource document");
8263
0
  MOZ_ASSERT(mOnloadBlockCount != 0,
8264
0
             "Shouldn't have a count of zero here, since we stabilized in "
8265
0
             "PostUnblockOnloadEvent");
8266
0
8267
0
  --mOnloadBlockCount;
8268
0
8269
0
  if (mOnloadBlockCount != 0) {
8270
0
    // We blocked again after the last unblock.  Nothing to do here.  We'll
8271
0
    // post a new event when we unblock again.
8272
0
    return;
8273
0
  }
8274
0
8275
0
  if (mAsyncOnloadBlockCount != 0) {
8276
0
    // We need to wait until the async onload block has been handled.
8277
0
    PostUnblockOnloadEvent();
8278
0
  }
8279
0
8280
0
  // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8281
0
  // -- it's not ours.
8282
0
  if (mScriptGlobalObject) {
8283
0
    nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8284
0
    if (loadGroup) {
8285
0
      loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
8286
0
    }
8287
0
  }
8288
0
}
8289
8290
nsIContent*
8291
nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const
8292
0
{
8293
0
  for (nsIFrame* f = aFrame; f;
8294
0
       f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
8295
0
    nsIContent* content = f->GetContent();
8296
0
    if (!content || content->IsInAnonymousSubtree())
8297
0
      continue;
8298
0
8299
0
    if (content->OwnerDoc() == this) {
8300
0
      return content;
8301
0
    }
8302
0
    // We must be in a subdocument so jump directly to the root frame.
8303
0
    // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
8304
0
    // the containing document.
8305
0
    f = f->PresContext()->GetPresShell()->GetRootFrame();
8306
0
  }
8307
0
8308
0
  return nullptr;
8309
0
}
8310
8311
void
8312
nsIDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
8313
                                    const nsAString& aType,
8314
                                    bool aPersisted,
8315
                                    bool aOnlySystemGroup)
8316
0
{
8317
0
  if (!aDispatchTarget) {
8318
0
    return;
8319
0
  }
8320
0
8321
0
  PageTransitionEventInit init;
8322
0
  init.mBubbles = true;
8323
0
  init.mCancelable = true;
8324
0
  init.mPersisted = aPersisted;
8325
0
8326
0
  nsDocShell* docShell = mDocumentContainer.get();
8327
0
  init.mInFrameSwap = docShell && docShell->InFrameSwap();
8328
0
8329
0
  RefPtr<PageTransitionEvent> event =
8330
0
    PageTransitionEvent::Constructor(this, aType, init);
8331
0
8332
0
  event->SetTrusted(true);
8333
0
  event->SetTarget(this);
8334
0
  if (aOnlySystemGroup) {
8335
0
    event->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatchInContent = true;
8336
0
  }
8337
0
  EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event,
8338
0
                                    nullptr, nullptr);
8339
0
}
8340
8341
static bool
8342
NotifyPageShow(nsIDocument* aDocument, void* aData)
8343
0
{
8344
0
  const bool* aPersistedPtr = static_cast<const bool*>(aData);
8345
0
  aDocument->OnPageShow(*aPersistedPtr, nullptr);
8346
0
  return true;
8347
0
}
8348
8349
void
8350
nsIDocument::OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget,
8351
                        bool aOnlySystemGroup)
8352
0
{
8353
0
  mVisible = true;
8354
0
8355
0
  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8356
0
  EnumerateExternalResources(NotifyPageShow, &aPersisted);
8357
0
8358
0
  Element* root = GetRootElement();
8359
0
  if (aPersisted && root) {
8360
0
    // Send out notifications that our <link> elements are attached.
8361
0
    RefPtr<nsContentList> links = NS_GetContentList(root,
8362
0
                                                      kNameSpaceID_XHTML,
8363
0
                                                      NS_LITERAL_STRING("link"));
8364
0
8365
0
    uint32_t linkCount = links->Length(true);
8366
0
    for (uint32_t i = 0; i < linkCount; ++i) {
8367
0
      static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
8368
0
    }
8369
0
  }
8370
0
8371
0
  // See nsIDocument
8372
0
  if (!aDispatchStartTarget) {
8373
0
    // Set mIsShowing before firing events, in case those event handlers
8374
0
    // move us around.
8375
0
    mIsShowing = true;
8376
0
  }
8377
0
8378
0
  if (mAnimationController) {
8379
0
    mAnimationController->OnPageShow();
8380
0
  }
8381
0
8382
0
  if (aPersisted) {
8383
0
    ImageTracker()->SetAnimatingState(true);
8384
0
  }
8385
0
8386
0
  UpdateVisibilityState();
8387
0
8388
0
  if (!mIsBeingUsedAsImage) {
8389
0
    // Dispatch observer notification to notify observers page is shown.
8390
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
8391
0
    if (os) {
8392
0
      nsIPrincipal* principal = NodePrincipal();
8393
0
      os->NotifyObservers(this,
8394
0
                          nsContentUtils::IsSystemPrincipal(principal) ?
8395
0
                            "chrome-page-shown" :
8396
0
                            "content-page-shown",
8397
0
                          nullptr);
8398
0
    }
8399
0
8400
0
    nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8401
0
    if (!target) {
8402
0
      target = do_QueryInterface(GetWindow());
8403
0
    }
8404
0
    DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted,
8405
0
                           aOnlySystemGroup);
8406
0
  }
8407
0
}
8408
8409
static bool
8410
NotifyPageHide(nsIDocument* aDocument, void* aData)
8411
0
{
8412
0
  const bool* aPersistedPtr = static_cast<const bool*>(aData);
8413
0
  aDocument->OnPageHide(*aPersistedPtr, nullptr);
8414
0
  return true;
8415
0
}
8416
8417
static void
8418
DispatchFullscreenChange(nsIDocument* aDocument, nsINode* aTarget)
8419
0
{
8420
0
  if (nsPresContext* presContext = aDocument->GetPresContext()) {
8421
0
    auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
8422
0
      FullscreenEventType::Change, aDocument, aTarget);
8423
0
    presContext->RefreshDriver()->
8424
0
      ScheduleFullscreenEvent(std::move(pendingEvent));
8425
0
  }
8426
0
}
8427
8428
static void ClearPendingFullscreenRequests(nsIDocument* aDoc);
8429
8430
static bool
8431
HasHttpScheme(nsIURI* aURI)
8432
0
{
8433
0
  bool isHttpish = false;
8434
0
  return aURI &&
8435
0
         ((NS_SUCCEEDED(aURI->SchemeIs("http", &isHttpish)) && isHttpish) ||
8436
0
          (NS_SUCCEEDED(aURI->SchemeIs("https", &isHttpish)) && isHttpish));
8437
0
}
8438
8439
void
8440
nsIDocument::OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget,
8441
                        bool aOnlySystemGroup)
8442
0
{
8443
0
  if (IsTopLevelContentDocument() && GetDocGroup() &&
8444
0
      Telemetry::CanRecordExtended()) {
8445
0
    TabGroup* tabGroup = mDocGroup->GetTabGroup();
8446
0
8447
0
    if (tabGroup) {
8448
0
      uint32_t active = tabGroup->Count(true /* aActiveOnly */);
8449
0
      uint32_t total = tabGroup->Count();
8450
0
8451
0
      if (HasHttpScheme(GetDocumentURI())) {
8452
0
        Telemetry::Accumulate(Telemetry::ACTIVE_HTTP_DOCGROUPS_PER_TABGROUP,
8453
0
                              active);
8454
0
        Telemetry::Accumulate(Telemetry::TOTAL_HTTP_DOCGROUPS_PER_TABGROUP,
8455
0
                              total);
8456
0
      }
8457
0
    }
8458
0
  }
8459
0
8460
0
  // Send out notifications that our <link> elements are detached,
8461
0
  // but only if this is not a full unload.
8462
0
  Element* root = GetRootElement();
8463
0
  if (aPersisted && root) {
8464
0
    RefPtr<nsContentList> links = NS_GetContentList(root,
8465
0
                                                    kNameSpaceID_XHTML,
8466
0
                                                    NS_LITERAL_STRING("link"));
8467
0
8468
0
    uint32_t linkCount = links->Length(true);
8469
0
    for (uint32_t i = 0; i < linkCount; ++i) {
8470
0
      static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
8471
0
    }
8472
0
  }
8473
0
8474
0
  // See nsIDocument
8475
0
  if (!aDispatchStartTarget) {
8476
0
    // Set mIsShowing before firing events, in case those event handlers
8477
0
    // move us around.
8478
0
    mIsShowing = false;
8479
0
  }
8480
0
8481
0
  if (mAnimationController) {
8482
0
    mAnimationController->OnPageHide();
8483
0
  }
8484
0
8485
0
  // We do not stop the animations (bug 1024343)
8486
0
  // when the page is refreshing while being dragged out
8487
0
  nsDocShell* docShell = mDocumentContainer.get();
8488
0
  if (aPersisted && !(docShell && docShell->InFrameSwap())) {
8489
0
    ImageTracker()->SetAnimatingState(false);
8490
0
  }
8491
0
8492
0
  ExitPointerLock();
8493
0
8494
0
  if (!mIsBeingUsedAsImage) {
8495
0
    // Dispatch observer notification to notify observers page is hidden.
8496
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
8497
0
    if (os) {
8498
0
      nsIPrincipal* principal = NodePrincipal();
8499
0
      os->NotifyObservers(this,
8500
0
                          nsContentUtils::IsSystemPrincipal(principal) ?
8501
0
                            "chrome-page-hidden" :
8502
0
                            "content-page-hidden",
8503
0
                          nullptr);
8504
0
    }
8505
0
8506
0
    // Now send out a PageHide event.
8507
0
    nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8508
0
    if (!target) {
8509
0
      target = do_QueryInterface(GetWindow());
8510
0
    }
8511
0
    {
8512
0
      PageUnloadingEventTimeStamp timeStamp(this);
8513
0
      DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted,
8514
0
                             aOnlySystemGroup);
8515
0
    }
8516
0
  }
8517
0
8518
0
  mVisible = false;
8519
0
8520
0
  UpdateVisibilityState();
8521
0
8522
0
  EnumerateExternalResources(NotifyPageHide, &aPersisted);
8523
0
  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8524
0
8525
0
  ClearPendingFullscreenRequests(this);
8526
0
  if (FullscreenStackTop()) {
8527
0
    // If this document was fullscreen, we should exit fullscreen in this
8528
0
    // doctree branch. This ensures that if the user navigates while in
8529
0
    // fullscreen mode we don't leave its still visible ancestor documents
8530
0
    // in fullscreen mode. So exit fullscreen in the document's fullscreen
8531
0
    // root document, as this will exit fullscreen in all the root's
8532
0
    // descendant documents. Note that documents are removed from the
8533
0
    // doctree by the time OnPageHide() is called, so we must store a
8534
0
    // reference to the root (in nsDocument::mFullscreenRoot) since we can't
8535
0
    // just traverse the doctree to get the root.
8536
0
    nsIDocument::ExitFullscreenInDocTree(this);
8537
0
8538
0
    // Since the document is removed from the doctree before OnPageHide() is
8539
0
    // called, ExitFullscreen() can't traverse from the root down to *this*
8540
0
    // document, so we must manually call CleanupFullscreenState() below too.
8541
0
    // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
8542
0
    // so we *must* call it after ExitFullscreen(), not before.
8543
0
    // OnPageHide() is called in every hidden (i.e. descendant) document,
8544
0
    // so calling CleanupFullscreenState() here will ensure all hidden
8545
0
    // documents have their fullscreen state reset.
8546
0
    CleanupFullscreenState();
8547
0
8548
0
    // The fullscreenchange event is to be queued in the refresh driver,
8549
0
    // however a hidden page wouldn't trigger that again, so it makes no
8550
0
    // sense to dispatch such event here.
8551
0
  }
8552
0
}
8553
8554
void
8555
nsIDocument::WillDispatchMutationEvent(nsINode* aTarget)
8556
0
{
8557
0
  NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
8558
0
               mSubtreeModifiedTargets.Count() == 0,
8559
0
               "mSubtreeModifiedTargets not cleared after dispatching?");
8560
0
  ++mSubtreeModifiedDepth;
8561
0
  if (aTarget) {
8562
0
    // MayDispatchMutationEvent is often called just before this method,
8563
0
    // so it has already appended the node to mSubtreeModifiedTargets.
8564
0
    int32_t count = mSubtreeModifiedTargets.Count();
8565
0
    if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
8566
0
      mSubtreeModifiedTargets.AppendObject(aTarget);
8567
0
    }
8568
0
  }
8569
0
}
8570
8571
void
8572
nsIDocument::MutationEventDispatched(nsINode* aTarget)
8573
0
{
8574
0
  --mSubtreeModifiedDepth;
8575
0
  if (mSubtreeModifiedDepth == 0) {
8576
0
    int32_t count = mSubtreeModifiedTargets.Count();
8577
0
    if (!count) {
8578
0
      return;
8579
0
    }
8580
0
8581
0
    nsPIDOMWindowInner* window = GetInnerWindow();
8582
0
    if (window &&
8583
0
        !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
8584
0
      mSubtreeModifiedTargets.Clear();
8585
0
      return;
8586
0
    }
8587
0
8588
0
    nsCOMArray<nsINode> realTargets;
8589
0
    for (int32_t i = 0; i < count; ++i) {
8590
0
      nsINode* possibleTarget = mSubtreeModifiedTargets[i];
8591
0
      nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
8592
0
      if (content && content->ChromeOnlyAccess()) {
8593
0
        continue;
8594
0
      }
8595
0
8596
0
      nsINode* commonAncestor = nullptr;
8597
0
      int32_t realTargetCount = realTargets.Count();
8598
0
      for (int32_t j = 0; j < realTargetCount; ++j) {
8599
0
        commonAncestor =
8600
0
          nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
8601
0
        if (commonAncestor) {
8602
0
          realTargets.ReplaceObjectAt(commonAncestor, j);
8603
0
          break;
8604
0
        }
8605
0
      }
8606
0
      if (!commonAncestor) {
8607
0
        realTargets.AppendObject(possibleTarget);
8608
0
      }
8609
0
    }
8610
0
8611
0
    mSubtreeModifiedTargets.Clear();
8612
0
8613
0
    int32_t realTargetCount = realTargets.Count();
8614
0
    for (int32_t k = 0; k < realTargetCount; ++k) {
8615
0
      InternalMutationEvent mutation(true, eLegacySubtreeModified);
8616
0
      (new AsyncEventDispatcher(realTargets[k], mutation))->
8617
0
        RunDOMEventWhenSafe();
8618
0
    }
8619
0
  }
8620
0
}
8621
8622
void
8623
nsIDocument::DestroyElementMaps()
8624
0
{
8625
#ifdef DEBUG
8626
  mStyledLinksCleared = true;
8627
#endif
8628
  mStyledLinks.Clear();
8629
0
  mIdentifierMap.Clear();
8630
0
  mComposedShadowRoots.Clear();
8631
0
  mResponsiveContent.Clear();
8632
0
  IncrementExpandoGeneration(*this);
8633
0
}
8634
8635
void
8636
nsIDocument::RefreshLinkHrefs()
8637
0
{
8638
0
  // Get a list of all links we know about.  We will reset them, which will
8639
0
  // remove them from the document, so we need a copy of what is in the
8640
0
  // hashtable.
8641
0
  LinkArray linksToNotify(mStyledLinks.Count());
8642
0
  for (auto iter = mStyledLinks.ConstIter(); !iter.Done(); iter.Next()) {
8643
0
    linksToNotify.AppendElement(iter.Get()->GetKey());
8644
0
  }
8645
0
8646
0
  // Reset all of our styled links.
8647
0
  nsAutoScriptBlocker scriptBlocker;
8648
0
  for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
8649
0
    linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
8650
0
  }
8651
0
}
8652
8653
nsresult
8654
nsDocument::CloneDocHelper(nsDocument* clone) const
8655
0
{
8656
0
  clone->mIsStaticDocument = mCreatingStaticClone;
8657
0
8658
0
  // Init document
8659
0
  nsresult rv = clone->Init();
8660
0
  NS_ENSURE_SUCCESS(rv, rv);
8661
0
8662
0
  if (mCreatingStaticClone) {
8663
0
    nsCOMPtr<nsILoadGroup> loadGroup;
8664
0
8665
0
    // |mDocumentContainer| is the container of the document that is being
8666
0
    // created and not the original container. See CreateStaticClone function().
8667
0
    nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
8668
0
    if (docLoader) {
8669
0
      docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
8670
0
    }
8671
0
    nsCOMPtr<nsIChannel> channel = GetChannel();
8672
0
    nsCOMPtr<nsIURI> uri;
8673
0
    if (channel) {
8674
0
      NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
8675
0
    } else {
8676
0
      uri = nsIDocument::GetDocumentURI();
8677
0
    }
8678
0
    clone->mChannel = channel;
8679
0
    if (uri) {
8680
0
      clone->ResetToURI(uri, loadGroup, NodePrincipal());
8681
0
    }
8682
0
8683
0
    clone->SetContainer(mDocumentContainer);
8684
0
  }
8685
0
8686
0
  // Now ensure that our clone has the same URI, base URI, and principal as us.
8687
0
  // We do this after the mCreatingStaticClone block above, because that block
8688
0
  // can set the base URI to an incorrect value in cases when base URI
8689
0
  // information came from the channel.  So we override explicitly, and do it
8690
0
  // for all these properties, in case ResetToURI messes with any of the rest of
8691
0
  // them.
8692
0
  clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
8693
0
  clone->SetChromeXHRDocURI(mChromeXHRDocURI);
8694
0
  clone->SetPrincipal(NodePrincipal());
8695
0
  clone->mDocumentBaseURI = mDocumentBaseURI;
8696
0
  clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
8697
0
8698
0
  bool hasHadScriptObject = true;
8699
0
  nsIScriptGlobalObject* scriptObject =
8700
0
    GetScriptHandlingObject(hasHadScriptObject);
8701
0
  NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
8702
0
  if (mCreatingStaticClone) {
8703
0
    // If we're doing a static clone (print, print preview), then we're going to
8704
0
    // be setting a scope object after the clone. It's better to set it only
8705
0
    // once, so we don't do that here. However, we do want to act as if there is
8706
0
    // a script handling object. So we set mHasHadScriptHandlingObject.
8707
0
    clone->mHasHadScriptHandlingObject = true;
8708
0
  } else if (scriptObject) {
8709
0
    clone->SetScriptHandlingObject(scriptObject);
8710
0
  } else {
8711
0
    clone->SetScopeObject(GetScopeObject());
8712
0
  }
8713
0
  // Make the clone a data document
8714
0
  clone->SetLoadedAsData(true);
8715
0
8716
0
  // Misc state
8717
0
8718
0
  // State from nsIDocument
8719
0
  clone->mCharacterSet = mCharacterSet;
8720
0
  clone->mCharacterSetSource = mCharacterSetSource;
8721
0
  clone->mCompatMode = mCompatMode;
8722
0
  clone->mBidiOptions = mBidiOptions;
8723
0
  clone->mContentLanguage = mContentLanguage;
8724
0
  clone->SetContentTypeInternal(GetContentTypeInternal());
8725
0
  clone->mSecurityInfo = mSecurityInfo;
8726
0
8727
0
  // State from nsDocument
8728
0
  clone->mType = mType;
8729
0
  clone->mXMLDeclarationBits = mXMLDeclarationBits;
8730
0
  clone->mBaseTarget = mBaseTarget;
8731
0
8732
0
  return NS_OK;
8733
0
}
8734
8735
void
8736
nsIDocument::SetReadyStateInternal(ReadyState rs)
8737
0
{
8738
0
  mReadyState = rs;
8739
0
  if (rs == READYSTATE_UNINITIALIZED) {
8740
0
    // Transition back to uninitialized happens only to keep assertions happy
8741
0
    // right before readyState transitions to something else. Make this
8742
0
    // transition undetectable by Web content.
8743
0
    return;
8744
0
  }
8745
0
  if (mTiming) {
8746
0
    switch (rs) {
8747
0
      case READYSTATE_LOADING:
8748
0
        mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
8749
0
        break;
8750
0
      case READYSTATE_INTERACTIVE:
8751
0
        mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
8752
0
        break;
8753
0
      case READYSTATE_COMPLETE:
8754
0
        mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
8755
0
        break;
8756
0
      default:
8757
0
        NS_WARNING("Unexpected ReadyState value");
8758
0
        break;
8759
0
    }
8760
0
  }
8761
0
  // At the time of loading start, we don't have timing object, record time.
8762
0
  if (READYSTATE_LOADING == rs) {
8763
0
    mLoadingTimeStamp = mozilla::TimeStamp::Now();
8764
0
  }
8765
0
8766
0
  if (READYSTATE_INTERACTIVE == rs) {
8767
0
    TriggerInitialDocumentTranslation();
8768
0
  }
8769
0
8770
0
  RecordNavigationTiming(rs);
8771
0
8772
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
8773
0
    new AsyncEventDispatcher(this,
8774
0
                             NS_LITERAL_STRING("readystatechange"),
8775
0
                             CanBubble::eNo,
8776
0
                             ChromeOnlyDispatch::eNo);
8777
0
  asyncDispatcher->RunDOMEventWhenSafe();
8778
0
}
8779
8780
void
8781
nsIDocument::GetReadyState(nsAString& aReadyState) const
8782
{
8783
  switch(mReadyState) {
8784
  case READYSTATE_LOADING :
8785
    aReadyState.AssignLiteral(u"loading");
8786
    break;
8787
  case READYSTATE_INTERACTIVE :
8788
    aReadyState.AssignLiteral(u"interactive");
8789
    break;
8790
  case READYSTATE_COMPLETE :
8791
    aReadyState.AssignLiteral(u"complete");
8792
    break;
8793
  default:
8794
    aReadyState.AssignLiteral(u"uninitialized");
8795
  }
8796
}
8797
8798
static bool
8799
SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
8800
0
{
8801
0
  aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData));
8802
0
8803
0
  return true;
8804
0
}
8805
8806
void
8807
nsIDocument::SuppressEventHandling(uint32_t aIncrease)
8808
0
{
8809
0
  mEventsSuppressed += aIncrease;
8810
0
  UpdateFrameRequestCallbackSchedulingState();
8811
0
  for (uint32_t i = 0; i < aIncrease; ++i) {
8812
0
    ScriptLoader()->AddExecuteBlocker();
8813
0
  }
8814
0
8815
0
  EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
8816
0
}
8817
8818
static void
8819
FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments,
8820
                         bool aFireEvents)
8821
0
{
8822
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
8823
0
  if (!fm)
8824
0
    return;
8825
0
8826
0
  for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
8827
0
    // NB: Don't bother trying to fire delayed events on documents that were
8828
0
    // closed before this event ran.
8829
0
    if (!aDocuments[i]->EventHandlingSuppressed()) {
8830
0
      fm->FireDelayedEvents(aDocuments[i]);
8831
0
      nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
8832
0
      if (shell) {
8833
0
        // Only fire events for active documents.
8834
0
        bool fire = aFireEvents &&
8835
0
                    aDocuments[i]->GetInnerWindow() &&
8836
0
                    aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
8837
0
        shell->FireOrClearDelayedEvents(fire);
8838
0
      }
8839
0
    }
8840
0
  }
8841
0
}
8842
8843
void
8844
nsIDocument::PreloadPictureClosed()
8845
0
{
8846
0
  MOZ_ASSERT(mPreloadPictureDepth > 0);
8847
0
  mPreloadPictureDepth--;
8848
0
  if (mPreloadPictureDepth == 0) {
8849
0
    mPreloadPictureFoundSource.SetIsVoid(true);
8850
0
  }
8851
0
}
8852
8853
void
8854
nsIDocument::PreloadPictureImageSource(const nsAString& aSrcsetAttr,
8855
                                       const nsAString& aSizesAttr,
8856
                                       const nsAString& aTypeAttr,
8857
                                       const nsAString& aMediaAttr)
8858
0
{
8859
0
  // Nested pictures are not valid syntax, so while we'll eventually load them,
8860
0
  // it's not worth tracking sources mixed between nesting levels to preload
8861
0
  // them effectively.
8862
0
  if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) {
8863
0
    // <picture> selects the first matching source, so if this returns a URI we
8864
0
    // needn't consider new sources until a new <picture> is encountered.
8865
0
    bool found =
8866
0
      HTMLImageElement::SelectSourceForTagWithAttrs(this, true, VoidString(),
8867
0
                                                    aSrcsetAttr, aSizesAttr,
8868
0
                                                    aTypeAttr, aMediaAttr,
8869
0
                                                    mPreloadPictureFoundSource);
8870
0
    if (found && mPreloadPictureFoundSource.IsVoid()) {
8871
0
      // Found an empty source, which counts
8872
0
      mPreloadPictureFoundSource.SetIsVoid(false);
8873
0
    }
8874
0
  }
8875
0
}
8876
8877
already_AddRefed<nsIURI>
8878
nsIDocument::ResolvePreloadImage(nsIURI *aBaseURI,
8879
                                 const nsAString& aSrcAttr,
8880
                                 const nsAString& aSrcsetAttr,
8881
                                 const nsAString& aSizesAttr,
8882
                                 bool *aIsImgSet)
8883
0
{
8884
0
  nsString sourceURL;
8885
0
  bool isImgSet;
8886
0
  if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
8887
0
    // We're in a <picture> element and found a URI from a source previous to
8888
0
    // this image, use it.
8889
0
    sourceURL = mPreloadPictureFoundSource;
8890
0
    isImgSet = true;
8891
0
  } else {
8892
0
    // Otherwise try to use this <img> as a source
8893
0
    HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr,
8894
0
                                                  aSrcsetAttr, aSizesAttr,
8895
0
                                                  VoidString(), VoidString(),
8896
0
                                                  sourceURL);
8897
0
    isImgSet = !aSrcsetAttr.IsEmpty();
8898
0
  }
8899
0
8900
0
  // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
8901
0
  if (sourceURL.IsEmpty()) {
8902
0
    return nullptr;
8903
0
  }
8904
0
8905
0
  // Construct into URI using passed baseURI (the parser may know of base URI
8906
0
  // changes that have not reached us)
8907
0
  nsresult rv;
8908
0
  nsCOMPtr<nsIURI> uri;
8909
0
  rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL,
8910
0
                                                 this, aBaseURI);
8911
0
  if (NS_FAILED(rv)) {
8912
0
    return nullptr;
8913
0
  }
8914
0
8915
0
  *aIsImgSet = isImgSet;
8916
0
8917
0
  // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
8918
0
  // this this <picture> share the same <sources> (though this is not valid per
8919
0
  // spec)
8920
0
  return uri.forget();
8921
0
}
8922
8923
void
8924
nsIDocument::MaybePreLoadImage(nsIURI* uri,
8925
                               const nsAString &aCrossOriginAttr,
8926
                               enum mozilla::net::ReferrerPolicy aReferrerPolicy,
8927
                               bool aIsImgSet)
8928
0
{
8929
0
  // Early exit if the img is already present in the img-cache
8930
0
  // which indicates that the "real" load has already started and
8931
0
  // that we shouldn't preload it.
8932
0
  if (nsContentUtils::IsImageInCache(uri, this)) {
8933
0
    return;
8934
0
  }
8935
0
8936
0
  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
8937
0
                          nsContentUtils::CORSModeToLoadImageFlags(
8938
0
                            Element::StringToCORSMode(aCrossOriginAttr));
8939
0
8940
0
  nsContentPolicyType policyType =
8941
0
    aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET :
8942
0
                nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD;
8943
0
8944
0
  // Image not in cache - trigger preload
8945
0
  RefPtr<imgRequestProxy> request;
8946
0
  nsresult rv =
8947
0
    nsContentUtils::LoadImage(uri,
8948
0
                              static_cast<nsINode*>(this),
8949
0
                              this,
8950
0
                              NodePrincipal(),
8951
0
                              0,
8952
0
                              mDocumentURI, // uri of document used as referrer
8953
0
                              aReferrerPolicy,
8954
0
                              nullptr,       // no observer
8955
0
                              loadFlags,
8956
0
                              NS_LITERAL_STRING("img"),
8957
0
                              getter_AddRefs(request),
8958
0
                              policyType);
8959
0
8960
0
  // Pin image-reference to avoid evicting it from the img-cache before
8961
0
  // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
8962
0
  // unlink
8963
0
  if (NS_SUCCEEDED(rv)) {
8964
0
    mPreloadingImages.Put(uri, request.forget());
8965
0
  }
8966
0
}
8967
8968
void
8969
nsIDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode)
8970
0
{
8971
0
  NS_MutateURI mutator(aOrigURI);
8972
0
  if (NS_FAILED(mutator.GetStatus())) {
8973
0
      return;
8974
0
  }
8975
0
8976
0
  // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
8977
0
  // which ignores the path and uses only the origin. The other is for the
8978
0
  // document mPreloadedPreconnects de-duplication hash. Anonymous vs
8979
0
  // non-Anonymous preconnects create different connections on the wire and
8980
0
  // therefore should not be considred duplicates of each other and we
8981
0
  // normalize the path before putting it in the hash to accomplish that.
8982
0
8983
0
  if (aCORSMode == CORS_ANONYMOUS) {
8984
0
    mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/anonymous"));
8985
0
  } else {
8986
0
    mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/"));
8987
0
  }
8988
0
8989
0
  nsCOMPtr<nsIURI> uri;
8990
0
  nsresult rv = mutator.Finalize(uri);
8991
0
  if (NS_FAILED(rv)) {
8992
0
    return;
8993
0
  }
8994
0
8995
0
  auto entry = mPreloadedPreconnects.LookupForAdd(uri);
8996
0
  if (entry) {
8997
0
    return; // we found an existing entry
8998
0
  }
8999
0
  entry.OrInsert([] () { return true; });
9000
0
9001
0
  nsCOMPtr<nsISpeculativeConnect>
9002
0
    speculator(do_QueryInterface(nsContentUtils::GetIOService()));
9003
0
  if (!speculator) {
9004
0
    return;
9005
0
  }
9006
0
9007
0
  if (aCORSMode == CORS_ANONYMOUS) {
9008
0
    speculator->SpeculativeAnonymousConnect2(uri, NodePrincipal(), nullptr);
9009
0
  } else {
9010
0
    speculator->SpeculativeConnect2(uri, NodePrincipal(), nullptr);
9011
0
  }
9012
0
}
9013
9014
void
9015
nsIDocument::ForgetImagePreload(nsIURI* aURI)
9016
0
{
9017
0
  // Checking count is faster than hashing the URI in the common
9018
0
  // case of empty table.
9019
0
  if (mPreloadingImages.Count() != 0) {
9020
0
    nsCOMPtr<imgIRequest> req;
9021
0
    mPreloadingImages.Remove(aURI, getter_AddRefs(req));
9022
0
    if (req) {
9023
0
      // Make sure to cancel the request so imagelib knows it's gone.
9024
0
      req->CancelAndForgetObserver(NS_BINDING_ABORTED);
9025
0
    }
9026
0
  }
9027
0
}
9028
9029
void
9030
nsIDocument::UpdateDocumentStates(EventStates aChangedStates)
9031
0
{
9032
0
  if (aChangedStates.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
9033
0
    if (IsDocumentRightToLeft()) {
9034
0
      mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9035
0
    } else {
9036
0
      mDocumentState &= ~NS_DOCUMENT_STATE_RTL_LOCALE;
9037
0
    }
9038
0
  }
9039
0
9040
0
  if (aChangedStates.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
9041
0
    if (IsTopLevelWindowInactive()) {
9042
0
      mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9043
0
    } else {
9044
0
      mDocumentState &= ~NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9045
0
    }
9046
0
  }
9047
0
}
9048
9049
namespace {
9050
9051
/**
9052
 * Stub for LoadSheet(), since all we want is to get the sheet into
9053
 * the CSSLoader's style cache
9054
 */
9055
class StubCSSLoaderObserver final : public nsICSSLoaderObserver {
9056
0
  ~StubCSSLoaderObserver() {}
9057
public:
9058
  NS_IMETHOD
9059
  StyleSheetLoaded(StyleSheet*, bool, nsresult) override
9060
0
  {
9061
0
    return NS_OK;
9062
0
  }
9063
  NS_DECL_ISUPPORTS
9064
};
9065
NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
9066
9067
} // namespace
9068
9069
void
9070
nsIDocument::PreloadStyle(nsIURI* uri,
9071
                          const Encoding* aEncoding,
9072
                          const nsAString& aCrossOriginAttr,
9073
                          const enum mozilla::net::ReferrerPolicy aReferrerPolicy,
9074
                          const nsAString& aIntegrity)
9075
0
{
9076
0
  // The CSSLoader will retain this object after we return.
9077
0
  nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
9078
0
9079
0
  // Charset names are always ASCII.
9080
0
  CSSLoader()->LoadSheet(uri,
9081
0
                         true,
9082
0
                         NodePrincipal(),
9083
0
                         aEncoding,
9084
0
                         obs,
9085
0
                         Element::StringToCORSMode(aCrossOriginAttr),
9086
0
                         aReferrerPolicy,
9087
0
                         aIntegrity);
9088
0
}
9089
9090
nsresult
9091
nsIDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
9092
                                RefPtr<mozilla::StyleSheet>* aSheet)
9093
0
{
9094
0
  css::SheetParsingMode mode =
9095
0
    isAgentSheet ? css::eAgentSheetFeatures
9096
0
                 : css::eAuthorSheetFeatures;
9097
0
  return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, aSheet);
9098
0
}
9099
9100
class nsDelayedEventDispatcher : public Runnable
9101
{
9102
public:
9103
  explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments)
9104
    : mozilla::Runnable("nsDelayedEventDispatcher")
9105
0
  {
9106
0
    mDocuments.SwapElements(aDocuments);
9107
0
  }
9108
0
  virtual ~nsDelayedEventDispatcher() {}
9109
9110
  NS_IMETHOD Run() override
9111
0
  {
9112
0
    FireOrClearDelayedEvents(mDocuments, true);
9113
0
    return NS_OK;
9114
0
  }
9115
9116
private:
9117
  nsTArray<nsCOMPtr<nsIDocument>> mDocuments;
9118
};
9119
9120
static bool
9121
GetAndUnsuppressSubDocuments(nsIDocument* aDocument, void* aData)
9122
0
{
9123
0
  if (aDocument->EventHandlingSuppressed() > 0) {
9124
0
    aDocument->DecreaseEventSuppression();
9125
0
    aDocument->ScriptLoader()->RemoveExecuteBlocker();
9126
0
  }
9127
0
9128
0
  auto* docs = static_cast<nsTArray<nsCOMPtr<nsIDocument>>*>(aData);
9129
0
9130
0
  docs->AppendElement(aDocument);
9131
0
  aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
9132
0
  return true;
9133
0
}
9134
9135
void
9136
nsIDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents)
9137
0
{
9138
0
  nsTArray<nsCOMPtr<nsIDocument>> documents;
9139
0
  GetAndUnsuppressSubDocuments(this, &documents);
9140
0
9141
0
  if (aFireEvents) {
9142
0
    MOZ_RELEASE_ASSERT(NS_IsMainThread());
9143
0
    nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
9144
0
    Dispatch(TaskCategory::Other, ded.forget());
9145
0
  } else {
9146
0
    FireOrClearDelayedEvents(documents, false);
9147
0
  }
9148
0
}
9149
9150
nsISupports*
9151
nsIDocument::GetCurrentContentSink()
9152
0
{
9153
0
  return mParser ? mParser->GetContentSink() : nullptr;
9154
0
}
9155
9156
nsIDocument*
9157
nsIDocument::GetTemplateContentsOwner()
9158
0
{
9159
0
  if (!mTemplateContentsOwner) {
9160
0
    bool hasHadScriptObject = true;
9161
0
    nsIScriptGlobalObject* scriptObject =
9162
0
      GetScriptHandlingObject(hasHadScriptObject);
9163
0
9164
0
    nsCOMPtr<nsIDocument> document;
9165
0
    nsresult rv = NS_NewDOMDocument(getter_AddRefs(document),
9166
0
                                    EmptyString(), // aNamespaceURI
9167
0
                                    EmptyString(), // aQualifiedName
9168
0
                                    nullptr, // aDoctype
9169
0
                                    nsIDocument::GetDocumentURI(),
9170
0
                                    nsIDocument::GetDocBaseURI(),
9171
0
                                    NodePrincipal(),
9172
0
                                    true, // aLoadedAsData
9173
0
                                    scriptObject, // aEventObject
9174
0
                                    DocumentFlavorHTML);
9175
0
    NS_ENSURE_SUCCESS(rv, nullptr);
9176
0
9177
0
    mTemplateContentsOwner = document;
9178
0
    NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
9179
0
9180
0
    nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
9181
0
9182
0
    if (!scriptObject) {
9183
0
      mTemplateContentsOwner->SetScopeObject(GetScopeObject());
9184
0
    }
9185
0
9186
0
    doc->mHasHadScriptHandlingObject = hasHadScriptObject;
9187
0
9188
0
    // Set |doc| as the template contents owner of itself so that
9189
0
    // |doc| is the template contents owner of template elements created
9190
0
    // by |doc|.
9191
0
    doc->mTemplateContentsOwner = doc;
9192
0
  }
9193
0
9194
0
  return mTemplateContentsOwner;
9195
0
}
9196
9197
static already_AddRefed<nsPIDOMWindowOuter>
9198
FindTopWindowForElement(Element* element)
9199
0
{
9200
0
  nsIDocument* document = element->OwnerDoc();
9201
0
  if (!document) {
9202
0
    return nullptr;
9203
0
  }
9204
0
9205
0
  nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow();
9206
0
  if (!window) {
9207
0
    return nullptr;
9208
0
  }
9209
0
9210
0
  // Trying to find the top window (equivalent to window.top).
9211
0
  if (nsCOMPtr<nsPIDOMWindowOuter> top = window->GetTop()) {
9212
0
    window = top.forget();
9213
0
  }
9214
0
  return window.forget();
9215
0
}
9216
9217
/**
9218
 * nsAutoFocusEvent is used to dispatch a focus event for an
9219
 * nsGenericHTMLFormElement with the autofocus attribute enabled.
9220
 */
9221
class nsAutoFocusEvent : public Runnable
9222
{
9223
public:
9224
  explicit nsAutoFocusEvent(already_AddRefed<Element>&& aElement,
9225
                            already_AddRefed<nsPIDOMWindowOuter>&& aTopWindow)
9226
    : mozilla::Runnable("nsAutoFocusEvent")
9227
    , mElement(aElement)
9228
    , mTopWindow(aTopWindow)
9229
0
  {
9230
0
  }
9231
9232
  NS_IMETHOD Run() override
9233
0
  {
9234
0
    nsCOMPtr<nsPIDOMWindowOuter> currentTopWindow =
9235
0
      FindTopWindowForElement(mElement);
9236
0
    if (currentTopWindow != mTopWindow) {
9237
0
      // The element's top window changed from when the event was queued.
9238
0
      // Don't take away focus from an unrelated window.
9239
0
      return NS_OK;
9240
0
    }
9241
0
9242
0
    // Don't steal focus from the user.
9243
0
    if (mTopWindow->GetFocusedElement()) {
9244
0
      return NS_OK;
9245
0
    }
9246
0
9247
0
    mozilla::ErrorResult rv;
9248
0
    mElement->Focus(rv);
9249
0
    return rv.StealNSResult();
9250
0
  }
9251
private:
9252
  nsCOMPtr<Element> mElement;
9253
  nsCOMPtr<nsPIDOMWindowOuter> mTopWindow;
9254
};
9255
9256
void
9257
nsIDocument::SetAutoFocusElement(Element* aAutoFocusElement)
9258
0
{
9259
0
  if (mAutoFocusFired) {
9260
0
    // Too late.
9261
0
    return;
9262
0
  }
9263
0
9264
0
  if (mAutoFocusElement) {
9265
0
    // The spec disallows multiple autofocus elements, so we consider only the
9266
0
    // first one to preserve the old behavior.
9267
0
    return;
9268
0
  }
9269
0
9270
0
  mAutoFocusElement = do_GetWeakReference(aAutoFocusElement);
9271
0
  TriggerAutoFocus();
9272
0
}
9273
9274
void
9275
nsIDocument::TriggerAutoFocus()
9276
0
{
9277
0
  if (mAutoFocusFired) {
9278
0
    return;
9279
0
  }
9280
0
9281
0
  if (!mPresShell || !mPresShell->DidInitialize()) {
9282
0
    // Delay autofocus until frames are constructed so that we don't thrash
9283
0
    // style and layout calculations.
9284
0
    return;
9285
0
  }
9286
0
9287
0
  nsCOMPtr<Element> autoFocusElement = do_QueryReferent(mAutoFocusElement);
9288
0
  if (autoFocusElement && autoFocusElement->OwnerDoc() == this) {
9289
0
    mAutoFocusFired = true;
9290
0
9291
0
    nsCOMPtr<nsPIDOMWindowOuter> topWindow =
9292
0
      FindTopWindowForElement(autoFocusElement);
9293
0
    if (!topWindow) {
9294
0
      return;
9295
0
    }
9296
0
9297
0
    // NOTE: This may be removed in the future since the spec technically
9298
0
    // allows autofocus after load.
9299
0
    nsCOMPtr<nsIDocument> topDoc = topWindow->GetExtantDoc();
9300
0
    if (topDoc && topDoc->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) {
9301
0
      return;
9302
0
    }
9303
0
9304
0
    nsCOMPtr<nsIRunnable> event =
9305
0
      new nsAutoFocusEvent(autoFocusElement.forget(), topWindow.forget());
9306
0
    nsresult rv = NS_DispatchToCurrentThread(event.forget());
9307
0
    NS_ENSURE_SUCCESS_VOID(rv);
9308
0
  }
9309
0
}
9310
9311
void
9312
nsIDocument::SetScrollToRef(nsIURI* aDocumentURI)
9313
0
{
9314
0
  if (!aDocumentURI) {
9315
0
    return;
9316
0
  }
9317
0
9318
0
  nsAutoCString ref;
9319
0
9320
0
  // Since all URI's that pass through here aren't URL's we can't
9321
0
  // rely on the nsIURI implementation for providing a way for
9322
0
  // finding the 'ref' part of the URI, we'll haveto revert to
9323
0
  // string routines for finding the data past '#'
9324
0
9325
0
  nsresult rv = aDocumentURI->GetSpec(ref);
9326
0
  if (NS_FAILED(rv)) {
9327
0
    Unused << aDocumentURI->GetRef(mScrollToRef);
9328
0
    return;
9329
0
  }
9330
0
9331
0
  nsReadingIterator<char> start, end;
9332
0
9333
0
  ref.BeginReading(start);
9334
0
  ref.EndReading(end);
9335
0
9336
0
  if (FindCharInReadable('#', start, end)) {
9337
0
    ++start; // Skip over the '#'
9338
0
9339
0
    mScrollToRef = Substring(start, end);
9340
0
  }
9341
0
}
9342
9343
void
9344
nsIDocument::ScrollToRef()
9345
0
{
9346
0
  if (mScrolledToRefAlready) {
9347
0
    nsCOMPtr<nsIPresShell> shell = GetShell();
9348
0
    if (shell) {
9349
0
      shell->ScrollToAnchor();
9350
0
    }
9351
0
    return;
9352
0
  }
9353
0
9354
0
  if (mScrollToRef.IsEmpty()) {
9355
0
    return;
9356
0
  }
9357
0
9358
0
  nsCOMPtr<nsIPresShell> shell = GetShell();
9359
0
  if (shell) {
9360
0
    nsresult rv = NS_ERROR_FAILURE;
9361
0
    // We assume that the bytes are in UTF-8, as it says in the spec:
9362
0
    // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9363
0
    NS_ConvertUTF8toUTF16 ref(mScrollToRef);
9364
0
    // Check an empty string which might be caused by the UTF-8 conversion
9365
0
    if (!ref.IsEmpty()) {
9366
0
      // Note that GoToAnchor will handle flushing layout as needed.
9367
0
      rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9368
0
    } else {
9369
0
      rv = NS_ERROR_FAILURE;
9370
0
    }
9371
0
9372
0
    if (NS_FAILED(rv)) {
9373
0
      char* tmpstr = ToNewCString(mScrollToRef);
9374
0
      if (!tmpstr) {
9375
0
        return;
9376
0
      }
9377
0
      nsUnescape(tmpstr);
9378
0
      nsAutoCString unescapedRef;
9379
0
      unescapedRef.Assign(tmpstr);
9380
0
      free(tmpstr);
9381
0
9382
0
      NS_ConvertUTF8toUTF16 utf16Str(unescapedRef);
9383
0
      if (!utf16Str.IsEmpty()) {
9384
0
        rv = shell->GoToAnchor(utf16Str, mChangeScrollPosWhenScrollingToRef);
9385
0
      }
9386
0
9387
0
      // If UTF-8 URI failed then try to assume the string as a
9388
0
      // document's charset.
9389
0
      if (NS_FAILED(rv)) {
9390
0
        const Encoding* encoding = GetDocumentCharacterSet();
9391
0
        rv = encoding->DecodeWithoutBOMHandling(unescapedRef, ref);
9392
0
        if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
9393
0
          rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9394
0
        }
9395
0
      }
9396
0
    }
9397
0
    if (NS_SUCCEEDED(rv)) {
9398
0
      mScrolledToRefAlready = true;
9399
0
    }
9400
0
  }
9401
0
}
9402
9403
void
9404
nsIDocument::RegisterActivityObserver(nsISupports* aSupports)
9405
0
{
9406
0
  if (!mActivityObservers) {
9407
0
    mActivityObservers = new nsTHashtable<nsPtrHashKey<nsISupports> >();
9408
0
  }
9409
0
  mActivityObservers->PutEntry(aSupports);
9410
0
}
9411
9412
bool
9413
nsIDocument::UnregisterActivityObserver(nsISupports* aSupports)
9414
0
{
9415
0
  if (!mActivityObservers) {
9416
0
    return false;
9417
0
  }
9418
0
  nsPtrHashKey<nsISupports>* entry = mActivityObservers->GetEntry(aSupports);
9419
0
  if (!entry) {
9420
0
    return false;
9421
0
  }
9422
0
  mActivityObservers->RemoveEntry(entry);
9423
0
  return true;
9424
0
}
9425
9426
void
9427
nsIDocument::EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
9428
                                        void* aData)
9429
0
{
9430
0
  if (!mActivityObservers)
9431
0
    return;
9432
0
9433
0
  for (auto iter = mActivityObservers->ConstIter(); !iter.Done();
9434
0
       iter.Next()) {
9435
0
    aEnumerator(iter.Get()->GetKey(), aData);
9436
0
  }
9437
0
}
9438
9439
void
9440
nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
9441
0
{
9442
0
  if (aLink->HasPendingLinkUpdate()) {
9443
0
    return;
9444
0
  }
9445
0
9446
0
  aLink->SetHasPendingLinkUpdate();
9447
0
9448
0
  if (!mHasLinksToUpdateRunnable && !mFlushingPendingLinkUpdates) {
9449
0
    nsCOMPtr<nsIRunnable> event =
9450
0
      NewRunnableMethod("nsIDocument::FlushPendingLinkUpdatesFromRunnable",
9451
0
                        this,
9452
0
                        &nsIDocument::FlushPendingLinkUpdatesFromRunnable);
9453
0
    // Do this work in a second in the worst case.
9454
0
    nsresult rv =
9455
0
      NS_IdleDispatchToCurrentThread(event.forget(), 1000);
9456
0
    if (NS_FAILED(rv)) {
9457
0
      // If during shutdown posting a runnable doesn't succeed, we probably
9458
0
      // don't need to update link states.
9459
0
      return;
9460
0
    }
9461
0
    mHasLinksToUpdateRunnable = true;
9462
0
  }
9463
0
9464
0
  mLinksToUpdate.InfallibleAppend(aLink);
9465
0
}
9466
9467
void
9468
nsIDocument::FlushPendingLinkUpdatesFromRunnable()
9469
0
{
9470
0
  MOZ_ASSERT(mHasLinksToUpdateRunnable);
9471
0
  mHasLinksToUpdateRunnable = false;
9472
0
  FlushPendingLinkUpdates();
9473
0
}
9474
9475
void
9476
nsIDocument::FlushPendingLinkUpdates()
9477
0
{
9478
0
  if (mFlushingPendingLinkUpdates) {
9479
0
    return;
9480
0
  }
9481
0
9482
0
  auto restore = MakeScopeExit([&] { mFlushingPendingLinkUpdates = false; });
9483
0
  mFlushingPendingLinkUpdates = true;
9484
0
9485
0
  while (!mLinksToUpdate.IsEmpty()) {
9486
0
    LinksToUpdateList links(std::move(mLinksToUpdate));
9487
0
    for (auto iter = links.Iter(); !iter.Done(); iter.Next()) {
9488
0
      Link* link = iter.Get();
9489
0
      Element* element = link->GetElement();
9490
0
      if (element->OwnerDoc() == this) {
9491
0
        link->ClearHasPendingLinkUpdate();
9492
0
        if (element->IsInComposedDoc()) {
9493
0
          element->UpdateLinkState(link->LinkState());
9494
0
        }
9495
0
      }
9496
0
    }
9497
0
  }
9498
0
}
9499
9500
already_AddRefed<nsIDocument>
9501
nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer)
9502
0
{
9503
0
  nsDocument* thisAsDoc = static_cast<nsDocument*>(this);
9504
0
  mCreatingStaticClone = true;
9505
0
9506
0
  // Make document use different container during cloning.
9507
0
  RefPtr<nsDocShell> originalShell = mDocumentContainer.get();
9508
0
  SetContainer(static_cast<nsDocShell*>(aCloneContainer));
9509
0
  ErrorResult rv;
9510
0
  nsCOMPtr<nsINode> clonedNode = thisAsDoc->CloneNode(true, rv);
9511
0
  SetContainer(originalShell);
9512
0
9513
0
  RefPtr<nsDocument> clonedDoc;
9514
0
  if (rv.Failed()) {
9515
0
    // Don't return yet; we need to reset mCreatingStaticClone
9516
0
    rv.SuppressException();
9517
0
  } else {
9518
0
    nsCOMPtr<nsIDocument> tmp = do_QueryInterface(clonedNode);
9519
0
    if (tmp) {
9520
0
      clonedDoc = static_cast<nsDocument*>(tmp.get());
9521
0
      if (IsStaticDocument()) {
9522
0
        clonedDoc->mOriginalDocument = mOriginalDocument;
9523
0
      } else {
9524
0
        clonedDoc->mOriginalDocument = this;
9525
0
      }
9526
0
9527
0
      clonedDoc->mOriginalDocument->mStaticCloneCount++;
9528
0
9529
0
      size_t sheetsCount = SheetCount();
9530
0
      for (size_t i = 0; i < sheetsCount; ++i) {
9531
0
        RefPtr<StyleSheet> sheet = SheetAt(i);
9532
0
        if (sheet) {
9533
0
          if (sheet->IsApplicable()) {
9534
0
            RefPtr<StyleSheet> clonedSheet =
9535
0
              sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9536
0
            NS_WARNING_ASSERTION(clonedSheet,
9537
0
                                 "Cloning a stylesheet didn't work!");
9538
0
            if (clonedSheet) {
9539
0
              clonedDoc->AddStyleSheet(clonedSheet);
9540
0
            }
9541
0
          }
9542
0
        }
9543
0
      }
9544
0
9545
0
      for (int t = 0; t < AdditionalSheetTypeCount; ++t) {
9546
0
        auto& sheets = mAdditionalSheets[additionalSheetType(t)];
9547
0
        for (StyleSheet* sheet : sheets) {
9548
0
          if (sheet->IsApplicable()) {
9549
0
            RefPtr<StyleSheet> clonedSheet =
9550
0
              sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9551
0
            NS_WARNING_ASSERTION(clonedSheet,
9552
0
                                 "Cloning a stylesheet didn't work!");
9553
0
            if (clonedSheet) {
9554
0
              clonedDoc->AddAdditionalStyleSheet(additionalSheetType(t), clonedSheet);
9555
0
            }
9556
0
          }
9557
0
        }
9558
0
      }
9559
0
9560
0
      // Font faces created with the JS API will not be reflected in the
9561
0
      // stylesheets and need to be copied over to the cloned document.
9562
0
      if (const FontFaceSet* set = GetFonts()) {
9563
0
        set->CopyNonRuleFacesTo(clonedDoc->Fonts());
9564
0
      }
9565
0
9566
0
    }
9567
0
  }
9568
0
  mCreatingStaticClone = false;
9569
0
  return clonedDoc.forget();
9570
0
}
9571
9572
void
9573
nsIDocument::UnlinkOriginalDocumentIfStatic()
9574
0
{
9575
0
  if (IsStaticDocument() && mOriginalDocument) {
9576
0
    MOZ_ASSERT(mOriginalDocument->mStaticCloneCount > 0);
9577
0
    mOriginalDocument->mStaticCloneCount--;
9578
0
    mOriginalDocument = nullptr;
9579
0
  }
9580
0
  MOZ_ASSERT(!mOriginalDocument);
9581
0
}
9582
9583
nsresult
9584
nsIDocument::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
9585
                                          int32_t *aHandle)
9586
0
{
9587
0
  if (mFrameRequestCallbackCounter == INT32_MAX) {
9588
0
    // Can't increment without overflowing; bail out
9589
0
    return NS_ERROR_NOT_AVAILABLE;
9590
0
  }
9591
0
  int32_t newHandle = ++mFrameRequestCallbackCounter;
9592
0
9593
0
  DebugOnly<FrameRequest*> request =
9594
0
    mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
9595
0
  NS_ASSERTION(request, "This is supposed to be infallible!");
9596
0
  UpdateFrameRequestCallbackSchedulingState();
9597
0
9598
0
  *aHandle = newHandle;
9599
0
  return NS_OK;
9600
0
}
9601
9602
void
9603
nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
9604
0
{
9605
0
  // mFrameRequestCallbacks is stored sorted by handle
9606
0
  if (mFrameRequestCallbacks.RemoveElementSorted(aHandle)) {
9607
0
    UpdateFrameRequestCallbackSchedulingState();
9608
0
  }
9609
0
}
9610
9611
nsresult
9612
nsIDocument::GetStateObject(nsIVariant** aState)
9613
0
{
9614
0
  // Get the document's current state object. This is the object backing both
9615
0
  // history.state and popStateEvent.state.
9616
0
  //
9617
0
  // mStateObjectContainer may be null; this just means that there's no
9618
0
  // current state object.
9619
0
9620
0
  if (!mStateObjectCached && mStateObjectContainer) {
9621
0
    AutoJSContext cx;
9622
0
    nsIGlobalObject* sgo = GetScopeObject();
9623
0
    NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
9624
0
    JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
9625
0
    NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
9626
0
    JSAutoRealm ar(cx, global);
9627
0
9628
0
    mStateObjectContainer->
9629
0
      DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
9630
0
  }
9631
0
9632
0
  NS_IF_ADDREF(*aState = mStateObjectCached);
9633
0
  return NS_OK;
9634
0
}
9635
9636
void
9637
nsIDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
9638
0
{
9639
0
  mTiming = aTiming;
9640
0
  if (!mLoadingTimeStamp.IsNull() && mTiming) {
9641
0
    mTiming->SetDOMLoadingTimeStamp(GetDocumentURI(), mLoadingTimeStamp);
9642
0
  }
9643
0
}
9644
9645
nsContentList*
9646
nsIDocument::ImageMapList()
9647
0
{
9648
0
  if (!mImageMaps) {
9649
0
    mImageMaps = new nsContentList(this, kNameSpaceID_XHTML,
9650
0
                                   nsGkAtoms::map, nsGkAtoms::map);
9651
0
  }
9652
0
9653
0
  return mImageMaps;
9654
0
}
9655
9656
#define DEPRECATED_OPERATION(_op) #_op "Warning",
9657
static const char* kDeprecationWarnings[] = {
9658
#include "nsDeprecatedOperationList.h"
9659
  nullptr
9660
};
9661
#undef DEPRECATED_OPERATION
9662
9663
#define DOCUMENT_WARNING(_op) #_op "Warning",
9664
static const char* kDocumentWarnings[] = {
9665
#include "nsDocumentWarningList.h"
9666
  nullptr
9667
};
9668
#undef DOCUMENT_WARNING
9669
9670
static UseCounter
9671
OperationToUseCounter(nsIDocument::DeprecatedOperations aOperation)
9672
0
{
9673
0
  switch(aOperation) {
9674
0
#define DEPRECATED_OPERATION(_op) \
9675
0
    case nsIDocument::e##_op: return eUseCounter_##_op;
9676
0
#include "nsDeprecatedOperationList.h"
9677
0
#undef DEPRECATED_OPERATION
9678
0
  default:
9679
0
    MOZ_CRASH();
9680
0
  }
9681
0
}
9682
9683
bool
9684
nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation) const
9685
0
{
9686
0
  return mDeprecationWarnedAbout[aOperation];
9687
0
}
9688
9689
void
9690
nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
9691
                           bool asError /* = false */) const
9692
0
{
9693
0
  MOZ_ASSERT(NS_IsMainThread());
9694
0
  if (HasWarnedAbout(aOperation)) {
9695
0
    return;
9696
0
  }
9697
0
  mDeprecationWarnedAbout[aOperation] = true;
9698
0
  // Don't count deprecated operations for about pages since those pages
9699
0
  // are almost in our control, and we always need to remove uses there
9700
0
  // before we remove the operation itself anyway.
9701
0
  if (!IsAboutPage()) {
9702
0
    const_cast<nsIDocument*>(this)->
9703
0
      SetDocumentAndPageUseCounter(OperationToUseCounter(aOperation));
9704
0
  }
9705
0
  uint32_t flags = asError ? nsIScriptError::errorFlag
9706
0
                           : nsIScriptError::warningFlag;
9707
0
  nsContentUtils::ReportToConsole(flags,
9708
0
                                  NS_LITERAL_CSTRING("DOM Core"), this,
9709
0
                                  nsContentUtils::eDOM_PROPERTIES,
9710
0
                                  kDeprecationWarnings[aOperation]);
9711
0
}
9712
9713
bool
9714
nsIDocument::HasWarnedAbout(DocumentWarnings aWarning) const
9715
0
{
9716
0
  return mDocWarningWarnedAbout[aWarning];
9717
0
}
9718
9719
void
9720
nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
9721
                           bool asError /* = false */,
9722
                           const char16_t **aParams /* = nullptr */,
9723
                           uint32_t aParamsLength /* = 0 */) const
9724
0
{
9725
0
  MOZ_ASSERT(NS_IsMainThread());
9726
0
  if (HasWarnedAbout(aWarning)) {
9727
0
    return;
9728
0
  }
9729
0
  mDocWarningWarnedAbout[aWarning] = true;
9730
0
  uint32_t flags = asError ? nsIScriptError::errorFlag
9731
0
                           : nsIScriptError::warningFlag;
9732
0
  nsContentUtils::ReportToConsole(flags,
9733
0
                                  NS_LITERAL_CSTRING("DOM Core"), this,
9734
0
                                  nsContentUtils::eDOM_PROPERTIES,
9735
0
                                  kDocumentWarnings[aWarning],
9736
0
                                  aParams,
9737
0
                                  aParamsLength);
9738
0
}
9739
9740
mozilla::dom::ImageTracker*
9741
nsIDocument::ImageTracker()
9742
0
{
9743
0
  if (!mImageTracker) {
9744
0
    mImageTracker = new mozilla::dom::ImageTracker;
9745
0
  }
9746
0
  return mImageTracker;
9747
0
}
9748
9749
static bool
9750
AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
9751
0
{
9752
0
  nsTArray<nsIObjectLoadingContent*>* plugins =
9753
0
    reinterpret_cast<nsTArray<nsIObjectLoadingContent*>*>(userArg);
9754
0
  MOZ_ASSERT(plugins);
9755
0
  aDocument->GetPlugins(*plugins);
9756
0
  return true;
9757
0
}
9758
9759
void
9760
nsIDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
9761
0
{
9762
0
  aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
9763
0
  for (auto iter = mPlugins.ConstIter(); !iter.Done(); iter.Next()) {
9764
0
    aPlugins.AppendElement(iter.Get()->GetKey());
9765
0
  }
9766
0
  EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
9767
0
}
9768
9769
void
9770
nsIDocument::ScheduleSVGUseElementShadowTreeUpdate(SVGUseElement& aUseElement)
9771
0
{
9772
0
  MOZ_ASSERT(aUseElement.IsInComposedDoc());
9773
0
9774
0
  mSVGUseElementsNeedingShadowTreeUpdate.PutEntry(&aUseElement);
9775
0
9776
0
  if (nsIPresShell* shell = GetShell()) {
9777
0
    shell->EnsureStyleFlush();
9778
0
  }
9779
0
}
9780
9781
void
9782
nsIDocument::DoUpdateSVGUseElementShadowTrees()
9783
0
{
9784
0
  MOZ_ASSERT(!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty());
9785
0
  nsTArray<RefPtr<SVGUseElement>> useElementsToUpdate;
9786
0
9787
0
  do {
9788
0
    useElementsToUpdate.Clear();
9789
0
    useElementsToUpdate.SetCapacity(mSVGUseElementsNeedingShadowTreeUpdate.Count());
9790
0
9791
0
    {
9792
0
      for (auto iter = mSVGUseElementsNeedingShadowTreeUpdate.ConstIter();
9793
0
           !iter.Done();
9794
0
           iter.Next()) {
9795
0
        useElementsToUpdate.AppendElement(iter.Get()->GetKey());
9796
0
      }
9797
0
      mSVGUseElementsNeedingShadowTreeUpdate.Clear();
9798
0
    }
9799
0
9800
0
    for (auto& useElement : useElementsToUpdate) {
9801
0
      if (MOZ_UNLIKELY(!useElement->IsInComposedDoc())) {
9802
0
        // The element was in another <use> shadow tree which we processed
9803
0
        // already and also needed an update, and is removed from the document
9804
0
        // now, so nothing to do here.
9805
0
        MOZ_ASSERT(useElementsToUpdate.Length() > 1);
9806
0
        continue;
9807
0
      }
9808
0
      useElement->UpdateShadowTree();
9809
0
    }
9810
0
  } while (!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty());
9811
0
}
9812
9813
void
9814
nsIDocument::NotifyMediaFeatureValuesChanged()
9815
0
{
9816
0
  for (auto iter = mResponsiveContent.ConstIter(); !iter.Done();
9817
0
       iter.Next()) {
9818
0
    RefPtr<HTMLImageElement> imageElement = iter.Get()->GetKey();
9819
0
    imageElement->MediaFeatureValuesChanged();
9820
0
  }
9821
0
}
9822
9823
already_AddRefed<Touch>
9824
nsIDocument::CreateTouch(nsGlobalWindowInner* aView,
9825
                         EventTarget* aTarget,
9826
                         int32_t aIdentifier,
9827
                         int32_t aPageX, int32_t aPageY,
9828
                         int32_t aScreenX, int32_t aScreenY,
9829
                         int32_t aClientX, int32_t aClientY,
9830
                         int32_t aRadiusX, int32_t aRadiusY,
9831
                         float aRotationAngle,
9832
                         float aForce)
9833
0
{
9834
0
  RefPtr<Touch> touch = new Touch(aTarget,
9835
0
                                  aIdentifier,
9836
0
                                  aPageX, aPageY,
9837
0
                                  aScreenX, aScreenY,
9838
0
                                  aClientX, aClientY,
9839
0
                                  aRadiusX, aRadiusY,
9840
0
                                  aRotationAngle,
9841
0
                                  aForce);
9842
0
  return touch.forget();
9843
0
}
9844
9845
already_AddRefed<TouchList>
9846
nsIDocument::CreateTouchList()
9847
0
{
9848
0
  RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9849
0
  return retval.forget();
9850
0
}
9851
9852
already_AddRefed<TouchList>
9853
nsIDocument::CreateTouchList(Touch& aTouch,
9854
                             const Sequence<OwningNonNull<Touch> >& aTouches)
9855
0
{
9856
0
  RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9857
0
  retval->Append(&aTouch);
9858
0
  for (uint32_t i = 0; i < aTouches.Length(); ++i) {
9859
0
    retval->Append(aTouches[i].get());
9860
0
  }
9861
0
  return retval.forget();
9862
0
}
9863
9864
already_AddRefed<TouchList>
9865
nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches)
9866
0
{
9867
0
  RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9868
0
  for (uint32_t i = 0; i < aTouches.Length(); ++i) {
9869
0
    retval->Append(aTouches[i].get());
9870
0
  }
9871
0
  return retval.forget();
9872
0
}
9873
9874
already_AddRefed<nsDOMCaretPosition>
9875
nsIDocument::CaretPositionFromPoint(float aX, float aY)
9876
0
{
9877
0
  nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
9878
0
  nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
9879
0
  nsPoint pt(x, y);
9880
0
9881
0
  FlushPendingNotifications(FlushType::Layout);
9882
0
9883
0
  nsIPresShell *ps = GetShell();
9884
0
  if (!ps) {
9885
0
    return nullptr;
9886
0
  }
9887
0
9888
0
  nsIFrame *rootFrame = ps->GetRootFrame();
9889
0
9890
0
  // XUL docs, unlike HTML, have no frame tree until everything's done loading
9891
0
  if (!rootFrame) {
9892
0
    return nullptr;
9893
0
  }
9894
0
9895
0
  nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
9896
0
      nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
9897
0
  if (!ptFrame) {
9898
0
    return nullptr;
9899
0
  }
9900
0
9901
0
  // We require frame-relative coordinates for GetContentOffsetsFromPoint.
9902
0
  nsPoint aOffset;
9903
0
  nsCOMPtr<nsIWidget> widget = nsContentUtils::GetWidget(ps, &aOffset);
9904
0
  LayoutDeviceIntPoint refPoint =
9905
0
    nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), aOffset, GetPresContext());
9906
0
  nsPoint adjustedPoint =
9907
0
    nsLayoutUtils::GetEventCoordinatesRelativeTo(widget, refPoint, ptFrame);
9908
0
9909
0
  nsFrame::ContentOffsets offsets =
9910
0
    ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
9911
0
9912
0
  nsCOMPtr<nsIContent> node = offsets.content;
9913
0
  uint32_t offset = offsets.offset;
9914
0
  nsCOMPtr<nsIContent> anonNode = node;
9915
0
  bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
9916
0
  if (nodeIsAnonymous) {
9917
0
    node = ptFrame->GetContent();
9918
0
    nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
9919
0
    HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(nonanon);
9920
0
    nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
9921
0
    nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
9922
0
    if (textFrame || numberFrame) {
9923
0
      // If the anonymous content node has a child, then we need to make sure
9924
0
      // that we get the appropriate child, as otherwise the offset may not be
9925
0
      // correct when we construct a range for it.
9926
0
      nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
9927
0
      if (firstChild) {
9928
0
        anonNode = firstChild;
9929
0
      }
9930
0
9931
0
      if (textArea) {
9932
0
        offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
9933
0
      }
9934
0
9935
0
      node = nonanon;
9936
0
    } else {
9937
0
      node = nullptr;
9938
0
      offset = 0;
9939
0
    }
9940
0
  }
9941
0
9942
0
  RefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
9943
0
  if (nodeIsAnonymous) {
9944
0
    aCaretPos->SetAnonymousContentNode(anonNode);
9945
0
  }
9946
0
  return aCaretPos.forget();
9947
0
}
9948
9949
bool
9950
nsIDocument::IsPotentiallyScrollable(HTMLBodyElement* aBody)
9951
0
{
9952
0
  // We rely on correct frame information here, so need to flush frames.
9953
0
  FlushPendingNotifications(FlushType::Frames);
9954
0
9955
0
  // An element is potentially scrollable if all of the following conditions are
9956
0
  // true:
9957
0
9958
0
  // The element has an associated CSS layout box.
9959
0
  nsIFrame* bodyFrame = aBody->GetPrimaryFrame();
9960
0
  if (!bodyFrame) {
9961
0
    return false;
9962
0
  }
9963
0
9964
0
  // The element is not the HTML body element, or it is and the root element's
9965
0
  // used value of the overflow-x or overflow-y properties is not visible.
9966
0
  MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement());
9967
0
  nsIFrame* parentFrame = aBody->GetParent()->GetPrimaryFrame();
9968
0
  if (parentFrame &&
9969
0
      parentFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
9970
0
      parentFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
9971
0
    return false;
9972
0
  }
9973
0
9974
0
  // The element's used value of the overflow-x or overflow-y properties is not
9975
0
  // visible.
9976
0
  if (bodyFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
9977
0
      bodyFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
9978
0
    return false;
9979
0
  }
9980
0
9981
0
  return true;
9982
0
}
9983
9984
Element*
9985
nsIDocument::GetScrollingElement()
9986
0
{
9987
0
  // Keep this in sync with IsScrollingElement.
9988
0
  if (GetCompatibilityMode() == eCompatibility_NavQuirks) {
9989
0
    RefPtr<HTMLBodyElement> body = GetBodyElement();
9990
0
    if (body && !IsPotentiallyScrollable(body)) {
9991
0
      return body;
9992
0
    }
9993
0
9994
0
    return nullptr;
9995
0
  }
9996
0
9997
0
  return GetRootElement();
9998
0
}
9999
10000
bool
10001
nsIDocument::IsScrollingElement(Element* aElement)
10002
0
{
10003
0
  // Keep this in sync with GetScrollingElement.
10004
0
  MOZ_ASSERT(aElement);
10005
0
10006
0
  if (GetCompatibilityMode() != eCompatibility_NavQuirks) {
10007
0
    return aElement == GetRootElement();
10008
0
  }
10009
0
10010
0
  // In the common case when aElement != body, avoid refcounting.
10011
0
  HTMLBodyElement* body = GetBodyElement();
10012
0
  if (aElement != body) {
10013
0
    return false;
10014
0
  }
10015
0
10016
0
  // Now we know body is non-null, since aElement is not null.  It's the
10017
0
  // scrolling element for the document if it itself is not potentially
10018
0
  // scrollable.
10019
0
  RefPtr<HTMLBodyElement> strongBody(body);
10020
0
  return !IsPotentiallyScrollable(strongBody);
10021
0
}
10022
10023
class UnblockParsingPromiseHandler final : public PromiseNativeHandler
10024
{
10025
public:
10026
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
10027
  NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
10028
10029
  explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise,
10030
                                        const BlockParsingOptions& aOptions)
10031
    : mPromise(aPromise)
10032
0
  {
10033
0
    nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
10034
0
    if (parser && (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) {
10035
0
      parser->BlockParser();
10036
0
      mParser = do_GetWeakReference(parser);
10037
0
      mDocument = aDocument;
10038
0
      mDocument->BlockOnload();
10039
0
    }
10040
0
  }
10041
10042
  void
10043
  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
10044
0
  {
10045
0
    MaybeUnblockParser();
10046
0
10047
0
    mPromise->MaybeResolve(aCx, aValue);
10048
0
  }
10049
10050
  void
10051
  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
10052
0
  {
10053
0
    MaybeUnblockParser();
10054
0
10055
0
    mPromise->MaybeReject(aCx, aValue);
10056
0
  }
10057
10058
protected:
10059
  virtual ~UnblockParsingPromiseHandler()
10060
0
  {
10061
0
    // If we're being cleaned up by the cycle collector, our mDocument reference
10062
0
    // may have been unlinked while our mParser weak reference is still alive.
10063
0
    if (mDocument) {
10064
0
      MaybeUnblockParser();
10065
0
    }
10066
0
  }
10067
10068
private:
10069
0
  void MaybeUnblockParser() {
10070
0
    nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
10071
0
    if (parser) {
10072
0
      MOZ_DIAGNOSTIC_ASSERT(mDocument);
10073
0
      nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
10074
0
      if (parser == docParser) {
10075
0
        parser->UnblockParser();
10076
0
        parser->ContinueInterruptedParsingAsync();
10077
0
        mDocument->UnblockOnload(false);
10078
0
      }
10079
0
    }
10080
0
    mParser = nullptr;
10081
0
    mDocument = nullptr;
10082
0
  }
10083
10084
  nsWeakPtr mParser;
10085
  RefPtr<Promise> mPromise;
10086
  RefPtr<nsIDocument> mDocument;
10087
};
10088
10089
NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
10090
10091
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
10092
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
10093
0
NS_INTERFACE_MAP_END
10094
10095
NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler)
10096
NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler)
10097
10098
already_AddRefed<Promise>
10099
nsIDocument::BlockParsing(Promise& aPromise, const BlockParsingOptions& aOptions, ErrorResult& aRv)
10100
0
{
10101
0
  RefPtr<Promise> resultPromise = Promise::Create(aPromise.GetParentObject(), aRv);
10102
0
  if (aRv.Failed()) {
10103
0
    return nullptr;
10104
0
  }
10105
0
10106
0
  RefPtr<PromiseNativeHandler> promiseHandler = new UnblockParsingPromiseHandler(this, resultPromise,
10107
0
                                                                                 aOptions);
10108
0
  aPromise.AppendNativeHandler(promiseHandler);
10109
0
10110
0
  return resultPromise.forget();
10111
0
}
10112
10113
already_AddRefed<nsIURI>
10114
nsIDocument::GetMozDocumentURIIfNotForErrorPages()
10115
0
{
10116
0
  if (mFailedChannel) {
10117
0
    nsCOMPtr<nsIURI> failedURI;
10118
0
    if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
10119
0
      return failedURI.forget();
10120
0
    }
10121
0
  }
10122
0
10123
0
  nsCOMPtr<nsIURI> uri = GetDocumentURIObject();
10124
0
  if (!uri) {
10125
0
    return nullptr;
10126
0
  }
10127
0
10128
0
  return uri.forget();
10129
0
}
10130
10131
Promise*
10132
nsIDocument::GetDocumentReadyForIdle(ErrorResult& aRv)
10133
0
{
10134
0
  if (!mReadyForIdle) {
10135
0
    nsIGlobalObject* global = GetScopeObject();
10136
0
    if (!global) {
10137
0
      aRv.Throw(NS_ERROR_NOT_AVAILABLE);
10138
0
      return nullptr;
10139
0
    }
10140
0
10141
0
    mReadyForIdle = Promise::Create(global, aRv);
10142
0
    if (aRv.Failed()) {
10143
0
      return nullptr;
10144
0
    }
10145
0
  }
10146
0
10147
0
  return mReadyForIdle;
10148
0
}
10149
10150
void
10151
nsIDocument::MaybeResolveReadyForIdle()
10152
0
{
10153
0
  IgnoredErrorResult rv;
10154
0
  Promise* readyPromise = GetDocumentReadyForIdle(rv);
10155
0
  if (readyPromise) {
10156
0
    readyPromise->MaybeResolve(this);
10157
0
  }
10158
0
}
10159
10160
nsIDOMXULCommandDispatcher*
10161
nsIDocument::GetCommandDispatcher()
10162
0
{
10163
0
  // Only chrome documents are allowed to use command dispatcher.
10164
0
  if (!nsContentUtils::IsChromeDoc(this)) {
10165
0
    return nullptr;
10166
0
  }
10167
0
  if (!mCommandDispatcher) {
10168
0
    // Create our command dispatcher and hook it up.
10169
0
    mCommandDispatcher = new nsXULCommandDispatcher(this);
10170
0
  }
10171
0
  return mCommandDispatcher;
10172
0
}
10173
10174
static JSObject*
10175
GetScopeObjectOfNode(nsINode* node)
10176
0
{
10177
0
    MOZ_ASSERT(node, "Must not be called with null.");
10178
0
10179
0
    // Window root occasionally keeps alive a node of a document whose
10180
0
    // window is already dead. If in this brief period someone calls
10181
0
    // GetPopupNode and we return that node, we can end up creating a
10182
0
    // reflector for the node in the wrong global (the current global,
10183
0
    // not the document global, because we won't know what the document
10184
0
    // global is).  Returning an orphan node like that to JS would be a
10185
0
    // bug anyway, so to avoid this, let's do the same check as fetching
10186
0
    // GetParentObjet() on the document does to determine the scope and
10187
0
    // if it returns null let's just return null in XULDocument::GetPopupNode.
10188
0
    nsIDocument* doc = node->OwnerDoc();
10189
0
    MOZ_ASSERT(doc, "This should never happen.");
10190
0
10191
0
    nsIGlobalObject* global = doc->GetScopeObject();
10192
0
    return global ? global->GetGlobalJSObject() : nullptr;
10193
0
}
10194
10195
10196
already_AddRefed<nsPIWindowRoot>
10197
nsIDocument::GetWindowRoot()
10198
0
{
10199
0
  if (!mDocumentContainer) {
10200
0
    return nullptr;
10201
0
  }
10202
0
  // XXX It's unclear why this can't just use GetWindow().
10203
0
  nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow();
10204
0
  return piWin ? piWin->GetTopWindowRoot() : nullptr;
10205
0
}
10206
10207
already_AddRefed<nsINode>
10208
nsIDocument::GetPopupNode()
10209
0
{
10210
0
    nsCOMPtr<nsINode> node;
10211
0
    nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
10212
0
    if (rootWin) {
10213
0
        node = rootWin->GetPopupNode(); // addref happens here
10214
0
    }
10215
0
10216
0
    if (!node) {
10217
0
        nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
10218
0
        if (pm) {
10219
0
            node = pm->GetLastTriggerPopupNode(this);
10220
0
        }
10221
0
    }
10222
0
10223
0
    if (node && GetScopeObjectOfNode(node)) {
10224
0
        return node.forget();
10225
0
    }
10226
0
10227
0
    return nullptr;
10228
0
}
10229
10230
void
10231
nsIDocument::SetPopupNode(nsINode* aNode)
10232
0
{
10233
0
    nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
10234
0
    if (rootWin) {
10235
0
        rootWin->SetPopupNode(aNode);
10236
0
    }
10237
0
}
10238
10239
// Returns the rangeOffset element from the XUL Popup Manager. This is for
10240
// chrome callers only.
10241
nsINode*
10242
nsIDocument::GetPopupRangeParent(ErrorResult& aRv)
10243
0
{
10244
0
    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
10245
0
    if (!pm) {
10246
0
        aRv.Throw(NS_ERROR_FAILURE);
10247
0
        return nullptr;
10248
0
    }
10249
0
10250
0
    return pm->GetMouseLocationParent();
10251
0
}
10252
10253
// Returns the rangeOffset element from the XUL Popup Manager.
10254
int32_t
10255
nsIDocument::GetPopupRangeOffset(ErrorResult& aRv)
10256
0
{
10257
0
    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
10258
0
    if (!pm) {
10259
0
        aRv.Throw(NS_ERROR_FAILURE);
10260
0
        return 0;
10261
0
    }
10262
0
10263
0
    return pm->MouseLocationOffset();
10264
0
}
10265
10266
already_AddRefed<nsINode>
10267
nsIDocument::GetTooltipNode()
10268
0
{
10269
0
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
10270
0
  if (pm) {
10271
0
    nsCOMPtr<nsINode> node = pm->GetLastTriggerTooltipNode(this);
10272
0
    if (node) {
10273
0
      return node.forget();
10274
0
    }
10275
0
  }
10276
0
10277
0
  return nullptr;
10278
0
}
10279
10280
nsIHTMLCollection*
10281
nsIDocument::Children()
10282
0
{
10283
0
  if (!mChildrenCollection) {
10284
0
    mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
10285
0
                                            nsGkAtoms::_asterisk,
10286
0
                                            nsGkAtoms::_asterisk,
10287
0
                                            false);
10288
0
  }
10289
0
10290
0
  return mChildrenCollection;
10291
0
}
10292
10293
uint32_t
10294
nsIDocument::ChildElementCount()
10295
0
{
10296
0
  return Children()->Length();
10297
0
}
10298
10299
namespace mozilla {
10300
10301
// Singleton class to manage the list of fullscreen documents which are the
10302
// root of a branch which contains fullscreen documents. We maintain this list
10303
// so that we can easily exit all windows from fullscreen when the user
10304
// presses the escape key.
10305
class FullscreenRoots {
10306
public:
10307
  // Adds the root of given document to the manager. Calling this method
10308
  // with a document whose root is already contained has no effect.
10309
  static void Add(nsIDocument* aDoc);
10310
10311
  // Iterates over every root in the root list, and calls aFunction, passing
10312
  // each root once to aFunction. It is safe to call Add() and Remove() while
10313
  // iterating over the list (i.e. in aFunction). Documents that are removed
10314
  // from the manager during traversal are not traversed, and documents that
10315
  // are added to the manager during traversal are also not traversed.
10316
  static void ForEach(void(*aFunction)(nsIDocument* aDoc));
10317
10318
  // Removes the root of a specific document from the manager.
10319
  static void Remove(nsIDocument* aDoc);
10320
10321
  // Returns true if all roots added to the list have been removed.
10322
  static bool IsEmpty();
10323
10324
private:
10325
10326
0
  FullscreenRoots() {
10327
0
    MOZ_COUNT_CTOR(FullscreenRoots);
10328
0
  }
10329
0
  ~FullscreenRoots() {
10330
0
    MOZ_COUNT_DTOR(FullscreenRoots);
10331
0
  }
10332
10333
  enum {
10334
    NotFound = uint32_t(-1)
10335
  };
10336
  // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
10337
  static uint32_t Find(nsIDocument* aRoot);
10338
10339
  // Returns true if aRoot is in the list of fullscreen roots.
10340
  static bool Contains(nsIDocument* aRoot);
10341
10342
  // Singleton instance of the FullscreenRoots. This is instantiated when a
10343
  // root is added, and it is deleted when the last root is removed.
10344
  static FullscreenRoots* sInstance;
10345
10346
  // List of weak pointers to roots.
10347
  nsTArray<nsWeakPtr> mRoots;
10348
};
10349
10350
FullscreenRoots* FullscreenRoots::sInstance = nullptr;
10351
10352
/* static */
10353
void
10354
FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
10355
0
{
10356
0
  if (!sInstance) {
10357
0
    return;
10358
0
  }
10359
0
  // Create a copy of the roots array, and iterate over the copy. This is so
10360
0
  // that if an element is removed from mRoots we don't mess up our iteration.
10361
0
  nsTArray<nsWeakPtr> roots(sInstance->mRoots);
10362
0
  // Call aFunction on all entries.
10363
0
  for (uint32_t i = 0; i < roots.Length(); i++) {
10364
0
    nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
10365
0
    // Check that the root isn't in the manager. This is so that new additions
10366
0
    // while we were running don't get traversed.
10367
0
    if (root && FullscreenRoots::Contains(root)) {
10368
0
      aFunction(root);
10369
0
    }
10370
0
  }
10371
0
}
10372
10373
/* static */
10374
bool
10375
FullscreenRoots::Contains(nsIDocument* aRoot)
10376
0
{
10377
0
  return FullscreenRoots::Find(aRoot) != NotFound;
10378
0
}
10379
10380
/* static */
10381
void
10382
FullscreenRoots::Add(nsIDocument* aDoc)
10383
0
{
10384
0
  nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
10385
0
  if (!FullscreenRoots::Contains(root)) {
10386
0
    if (!sInstance) {
10387
0
      sInstance = new FullscreenRoots();
10388
0
    }
10389
0
    sInstance->mRoots.AppendElement(do_GetWeakReference(root));
10390
0
  }
10391
0
}
10392
10393
/* static */
10394
uint32_t
10395
FullscreenRoots::Find(nsIDocument* aRoot)
10396
0
{
10397
0
  if (!sInstance) {
10398
0
    return NotFound;
10399
0
  }
10400
0
  nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
10401
0
  for (uint32_t i = 0; i < roots.Length(); i++) {
10402
0
    nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
10403
0
    if (otherRoot == aRoot) {
10404
0
      return i;
10405
0
    }
10406
0
  }
10407
0
  return NotFound;
10408
0
}
10409
10410
/* static */
10411
void
10412
FullscreenRoots::Remove(nsIDocument* aDoc)
10413
0
{
10414
0
  nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
10415
0
  uint32_t index = Find(root);
10416
0
  NS_ASSERTION(index != NotFound,
10417
0
    "Should only try to remove roots which are still added!");
10418
0
  if (index == NotFound || !sInstance) {
10419
0
    return;
10420
0
  }
10421
0
  sInstance->mRoots.RemoveElementAt(index);
10422
0
  if (sInstance->mRoots.IsEmpty()) {
10423
0
    delete sInstance;
10424
0
    sInstance = nullptr;
10425
0
  }
10426
0
}
10427
10428
/* static */
10429
bool
10430
FullscreenRoots::IsEmpty()
10431
0
{
10432
0
  return !sInstance;
10433
0
}
10434
10435
// Any fullscreen change waiting for the widget to finish transition
10436
// is queued here. This is declared static instead of a member of
10437
// nsDocument because in the majority of time, there would be at most
10438
// one document requesting or exiting fullscreen. We shouldn't waste
10439
// the space to hold for it in every document.
10440
class PendingFullscreenChangeList
10441
{
10442
public:
10443
  PendingFullscreenChangeList() = delete;
10444
10445
  template<typename T>
10446
  static void Add(UniquePtr<T> aChange)
10447
0
  {
10448
0
    sList.insertBack(aChange.release());
10449
0
  }
Unexecuted instantiation: void mozilla::PendingFullscreenChangeList::Add<mozilla::FullscreenExit>(mozilla::UniquePtr<mozilla::FullscreenExit, mozilla::DefaultDelete<mozilla::FullscreenExit> >)
Unexecuted instantiation: void mozilla::PendingFullscreenChangeList::Add<mozilla::FullscreenRequest>(mozilla::UniquePtr<mozilla::FullscreenRequest, mozilla::DefaultDelete<mozilla::FullscreenRequest> >)
10450
10451
  static const FullscreenChange* GetLast()
10452
0
  {
10453
0
    return sList.getLast();
10454
0
  }
10455
10456
  enum IteratorOption
10457
  {
10458
    // When we are committing fullscreen changes or preparing for
10459
    // that, we generally want to iterate all requests in the same
10460
    // window with eDocumentsWithSameRoot option.
10461
    eDocumentsWithSameRoot,
10462
    // If we are removing a document from the tree, we would only
10463
    // want to remove the requests from the given document and its
10464
    // descendants. For that case, use eInclusiveDescendants.
10465
    eInclusiveDescendants
10466
  };
10467
10468
  template<typename T>
10469
  class Iterator
10470
  {
10471
  public:
10472
    explicit Iterator(nsIDocument* aDoc, IteratorOption aOption)
10473
      : mCurrent(PendingFullscreenChangeList::sList.getFirst())
10474
      , mRootShellForIteration(aDoc->GetDocShell())
10475
0
    {
10476
0
      if (mCurrent) {
10477
0
        if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) {
10478
0
          mRootShellForIteration->
10479
0
            GetRootTreeItem(getter_AddRefs(mRootShellForIteration));
10480
0
        }
10481
0
        SkipToNextMatch();
10482
0
      }
10483
0
    }
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::Iterator(nsIDocument*, mozilla::PendingFullscreenChangeList::IteratorOption)
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::Iterator(nsIDocument*, mozilla::PendingFullscreenChangeList::IteratorOption)
10484
10485
    UniquePtr<T> TakeAndNext()
10486
0
    {
10487
0
      auto thisChange = TakeAndNextInternal();
10488
0
      SkipToNextMatch();
10489
0
      return thisChange;
10490
0
    }
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::TakeAndNext()
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::TakeAndNext()
10491
0
    bool AtEnd() const { return mCurrent == nullptr; }
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::AtEnd() const
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::AtEnd() const
10492
10493
  private:
10494
    UniquePtr<T> TakeAndNextInternal()
10495
0
    {
10496
0
      FullscreenChange* thisChange = mCurrent;
10497
0
      MOZ_ASSERT(thisChange->Type() == T::kType);
10498
0
      mCurrent = mCurrent->removeAndGetNext();
10499
0
      return WrapUnique(static_cast<T*>(thisChange));
10500
0
    }
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::TakeAndNextInternal()
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::TakeAndNextInternal()
10501
    void SkipToNextMatch()
10502
0
    {
10503
0
      while (mCurrent) {
10504
0
        if (mCurrent->Type() == T::kType) {
10505
0
          nsCOMPtr<nsIDocShellTreeItem>
10506
0
            docShell = mCurrent->Document()->GetDocShell();
10507
0
          if (!docShell) {
10508
0
            // Always automatically drop fullscreen changes which are
10509
0
            // from a document detached from the doc shell.
10510
0
            UniquePtr<T> change = TakeAndNextInternal();
10511
0
            change->MayRejectPromise();
10512
0
            continue;
10513
0
          }
10514
0
          while (docShell && docShell != mRootShellForIteration) {
10515
0
            docShell->GetParent(getter_AddRefs(docShell));
10516
0
          }
10517
0
          if (docShell) {
10518
0
            break;
10519
0
          }
10520
0
        }
10521
0
        // The current one either don't have matched type, or isn't
10522
0
        // inside the given subtree, so skip this item.
10523
0
        mCurrent = mCurrent->getNext();
10524
0
      }
10525
0
    }
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::SkipToNextMatch()
Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::SkipToNextMatch()
10526
10527
    FullscreenChange* mCurrent;
10528
    nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration;
10529
  };
10530
10531
private:
10532
  static LinkedList<FullscreenChange> sList;
10533
};
10534
10535
/* static */ LinkedList<FullscreenChange> PendingFullscreenChangeList::sList;
10536
10537
} // end namespace mozilla.
10538
10539
nsIDocument*
10540
nsIDocument::GetFullscreenRoot()
10541
0
{
10542
0
  nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
10543
0
  return root;
10544
0
}
10545
10546
void
10547
nsIDocument::SetFullscreenRoot(nsIDocument* aRoot)
10548
0
{
10549
0
  mFullscreenRoot = do_GetWeakReference(aRoot);
10550
0
}
10551
10552
already_AddRefed<Promise>
10553
nsIDocument::ExitFullscreen(ErrorResult& aRv)
10554
0
{
10555
0
  UniquePtr<FullscreenExit> exit = FullscreenExit::Create(this, aRv);
10556
0
  RefPtr<Promise> promise = exit->GetPromise();
10557
0
  RestorePreviousFullscreenState(std::move(exit));
10558
0
  return promise.forget();
10559
0
}
10560
10561
static void
10562
AskWindowToExitFullscreen(nsIDocument* aDoc)
10563
0
{
10564
0
  if (XRE_GetProcessType() == GeckoProcessType_Content) {
10565
0
    nsContentUtils::DispatchEventOnlyToChrome(
10566
0
      aDoc, ToSupports(aDoc), NS_LITERAL_STRING("MozDOMFullscreen:Exit"),
10567
0
      CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr);
10568
0
  } else {
10569
0
    if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
10570
0
      win->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, false);
10571
0
    }
10572
0
  }
10573
0
}
10574
10575
class nsCallExitFullscreen : public Runnable
10576
{
10577
public:
10578
  explicit nsCallExitFullscreen(nsIDocument* aDoc)
10579
    : mozilla::Runnable("nsCallExitFullscreen")
10580
    , mDoc(aDoc)
10581
0
  {
10582
0
  }
10583
10584
  NS_IMETHOD Run() final
10585
0
  {
10586
0
    if (!mDoc) {
10587
0
      FullscreenRoots::ForEach(&AskWindowToExitFullscreen);
10588
0
    } else {
10589
0
      AskWindowToExitFullscreen(mDoc);
10590
0
    }
10591
0
    return NS_OK;
10592
0
  }
10593
10594
private:
10595
  nsCOMPtr<nsIDocument> mDoc;
10596
};
10597
10598
/* static */ void
10599
nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
10600
0
{
10601
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
10602
0
  nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
10603
0
  if (aDoc) {
10604
0
    aDoc->Dispatch(TaskCategory::Other, exit.forget());
10605
0
  } else {
10606
0
    NS_DispatchToCurrentThread(exit.forget());
10607
0
  }
10608
0
}
10609
10610
static bool
10611
CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
10612
0
{
10613
0
  if (aDoc->FullscreenStackTop()) {
10614
0
    uint32_t* count = static_cast<uint32_t*>(aData);
10615
0
    (*count)++;
10616
0
  }
10617
0
  return true;
10618
0
}
10619
10620
static uint32_t
10621
CountFullscreenSubDocuments(nsIDocument* aDoc)
10622
0
{
10623
0
  uint32_t count = 0;
10624
0
  aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
10625
0
  return count;
10626
0
}
10627
10628
bool
10629
nsIDocument::IsFullscreenLeaf()
10630
0
{
10631
0
  // A fullscreen leaf document is fullscreen, and has no fullscreen
10632
0
  // subdocuments.
10633
0
  if (!FullscreenStackTop()) {
10634
0
    return false;
10635
0
  }
10636
0
  return CountFullscreenSubDocuments(this) == 0;
10637
0
}
10638
10639
bool
10640
GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
10641
0
{
10642
0
  if (aDoc->IsFullscreenLeaf()) {
10643
0
    nsIDocument** result = static_cast<nsIDocument**>(aData);
10644
0
    *result = aDoc;
10645
0
    return false;
10646
0
  }
10647
0
  if (aDoc->FullscreenStackTop()) {
10648
0
    aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
10649
0
  }
10650
0
  return true;
10651
0
}
10652
10653
static nsIDocument*
10654
GetFullscreenLeaf(nsIDocument* aDoc)
10655
0
{
10656
0
  nsIDocument* leaf = nullptr;
10657
0
  GetFullscreenLeaf(aDoc, &leaf);
10658
0
  if (leaf) {
10659
0
    return leaf;
10660
0
  }
10661
0
  // Otherwise we could be either in a non-fullscreen doc tree, or we're
10662
0
  // below the fullscreen doc. Start the search from the root.
10663
0
  nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
10664
0
  // Check that the root is actually fullscreen so we don't waste time walking
10665
0
  // around its descendants.
10666
0
  if (!root->FullscreenStackTop()) {
10667
0
    return nullptr;
10668
0
  }
10669
0
  GetFullscreenLeaf(root, &leaf);
10670
0
  return leaf;
10671
0
}
10672
10673
static bool
10674
ResetFullscreen(nsIDocument* aDocument, void* aData)
10675
0
{
10676
0
  if (Element* fsElement = aDocument->FullscreenStackTop()) {
10677
0
    NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
10678
0
        "Should have at most 1 fullscreen subdocument.");
10679
0
    aDocument->CleanupFullscreenState();
10680
0
    NS_ASSERTION(!aDocument->FullscreenStackTop(),
10681
0
                 "Should reset fullscreen");
10682
0
    DispatchFullscreenChange(aDocument, fsElement);
10683
0
    aDocument->EnumerateSubDocuments(ResetFullscreen, nullptr);
10684
0
  }
10685
0
  return true;
10686
0
}
10687
10688
// Since nsIDocument::ExitFullscreenInDocTree() could be called from
10689
// Element::UnbindFromTree() where it is not safe to synchronously run
10690
// script. This runnable is the script part of that function.
10691
class ExitFullscreenScriptRunnable : public Runnable
10692
{
10693
public:
10694
  explicit ExitFullscreenScriptRunnable(nsIDocument* aRoot, nsIDocument* aLeaf)
10695
    : mozilla::Runnable("ExitFullscreenScriptRunnable")
10696
    , mRoot(aRoot)
10697
    , mLeaf(aLeaf)
10698
0
  {
10699
0
  }
10700
10701
  NS_IMETHOD Run() override
10702
0
  {
10703
0
    // Dispatch MozDOMFullscreen:Exited to the original fullscreen leaf
10704
0
    // document since we want this event to follow the same path that
10705
0
    // MozDOMFullscreen:Entered was dispatched.
10706
0
    nsContentUtils::DispatchEventOnlyToChrome(
10707
0
      mLeaf, ToSupports(mLeaf),
10708
0
      NS_LITERAL_STRING("MozDOMFullscreen:Exited"),
10709
0
      CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr);
10710
0
    // Ensure the window exits fullscreen.
10711
0
    if (nsPIDOMWindowOuter* win = mRoot->GetWindow()) {
10712
0
      win->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen, false);
10713
0
    }
10714
0
    return NS_OK;
10715
0
  }
10716
10717
private:
10718
  nsCOMPtr<nsIDocument> mRoot;
10719
  nsCOMPtr<nsIDocument> mLeaf;
10720
};
10721
10722
/* static */ void
10723
nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
10724
0
{
10725
0
  MOZ_ASSERT(aMaybeNotARootDoc);
10726
0
10727
0
  // Unlock the pointer
10728
0
  UnlockPointer();
10729
0
10730
0
  // Resolve all promises which waiting for exit fullscreen.
10731
0
  PendingFullscreenChangeList::Iterator<FullscreenExit> iter(
10732
0
    aMaybeNotARootDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
10733
0
  while (!iter.AtEnd()) {
10734
0
    UniquePtr<FullscreenExit> exit = iter.TakeAndNext();
10735
0
    exit->MayResolvePromise();
10736
0
  }
10737
0
10738
0
  nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
10739
0
  if (!root || !root->FullscreenStackTop()) {
10740
0
    // If a document was detached before exiting from fullscreen, it is
10741
0
    // possible that the root had left fullscreen state. In this case,
10742
0
    // we would not get anything from the ResetFullscreen() call. Root's
10743
0
    // not being a fullscreen doc also means the widget should have
10744
0
    // exited fullscreen state. It means even if we do not return here,
10745
0
    // we would actually do nothing below except crashing ourselves via
10746
0
    // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
10747
0
    // document.
10748
0
    return;
10749
0
  }
10750
0
10751
0
  // Record the fullscreen leaf document for MozDOMFullscreen:Exited.
10752
0
  // See ExitFullscreenScriptRunnable::Run for details. We have to
10753
0
  // record it here because we don't have such information after we
10754
0
  // reset the fullscreen state below.
10755
0
  nsIDocument* fullscreenLeaf = GetFullscreenLeaf(root);
10756
0
10757
0
  // Walk the tree of fullscreen documents, and reset their fullscreen state.
10758
0
  ResetFullscreen(root, nullptr);
10759
0
10760
0
  NS_ASSERTION(!root->FullscreenStackTop(),
10761
0
    "Fullscreen root should no longer be a fullscreen doc...");
10762
0
10763
0
  // Move the top-level window out of fullscreen mode.
10764
0
  FullscreenRoots::Remove(root);
10765
0
10766
0
  nsContentUtils::AddScriptRunner(
10767
0
    new ExitFullscreenScriptRunnable(root, fullscreenLeaf));
10768
0
}
10769
10770
static void
10771
DispatchFullscreenNewOriginEvent(nsIDocument* aDoc)
10772
0
{
10773
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
10774
0
    new AsyncEventDispatcher(
10775
0
        aDoc, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"),
10776
0
        CanBubble::eYes, ChromeOnlyDispatch::eYes);
10777
0
  asyncDispatcher->PostDOMEvent();
10778
0
}
10779
10780
void
10781
nsIDocument::RestorePreviousFullscreenState(UniquePtr<FullscreenExit> aExit)
10782
0
{
10783
0
  NS_ASSERTION(!FullscreenStackTop() || !FullscreenRoots::IsEmpty(),
10784
0
    "Should have at least 1 fullscreen root when fullscreen!");
10785
0
10786
0
  if (!FullscreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
10787
0
    aExit->MayRejectPromise();
10788
0
    return;
10789
0
  }
10790
0
10791
0
  nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
10792
0
  AutoTArray<Element*, 8> exitElements;
10793
0
10794
0
  nsIDocument* doc = fullScreenDoc;
10795
0
  // Collect all subdocuments.
10796
0
  for (; doc != this; doc = doc->GetParentDocument()) {
10797
0
    Element* fsElement = doc->FullscreenStackTop();
10798
0
    MOZ_ASSERT(fsElement, "Parent document of "
10799
0
               "a fullscreen document without fullscreen element?");
10800
0
    exitElements.AppendElement(fsElement);
10801
0
  }
10802
0
  MOZ_ASSERT(doc == this, "Must have reached this doc");
10803
0
  // Collect all ancestor documents which we are going to change.
10804
0
  for (; doc; doc = doc->GetParentDocument()) {
10805
0
    MOZ_ASSERT(!doc->mFullscreenStack.IsEmpty(),
10806
0
               "Ancestor of fullscreen document must also be in fullscreen");
10807
0
    Element* fsElement = doc->FullscreenStackTop();
10808
0
    if (doc != this) {
10809
0
      if (auto* iframe = HTMLIFrameElement::FromNode(fsElement)) {
10810
0
        if (iframe->FullscreenFlag()) {
10811
0
          // If this is an iframe, and it explicitly requested
10812
0
          // fullscreen, don't rollback it automatically.
10813
0
          break;
10814
0
        }
10815
0
      }
10816
0
    }
10817
0
    exitElements.AppendElement(fsElement);
10818
0
    if (doc->mFullscreenStack.Length() > 1) {
10819
0
      break;
10820
0
    }
10821
0
  }
10822
0
10823
0
  nsIDocument* lastDoc = exitElements.LastElement()->OwnerDoc();
10824
0
  if (!lastDoc->GetParentDocument() &&
10825
0
      lastDoc->mFullscreenStack.Length() == 1) {
10826
0
    // If we are fully exiting fullscreen, don't touch anything here,
10827
0
    // just wait for the window to get out from fullscreen first.
10828
0
    PendingFullscreenChangeList::Add(std::move(aExit));
10829
0
    AskWindowToExitFullscreen(this);
10830
0
    return;
10831
0
  }
10832
0
10833
0
  // If fullscreen mode is updated the pointer should be unlocked
10834
0
  UnlockPointer();
10835
0
  // All documents listed in the array except the last one are going to
10836
0
  // completely exit from the fullscreen state.
10837
0
  for (auto i : IntegerRange(exitElements.Length() - 1)) {
10838
0
    exitElements[i]->OwnerDoc()->CleanupFullscreenState();
10839
0
  }
10840
0
  // The last document will either rollback one fullscreen element, or
10841
0
  // completely exit from the fullscreen state as well.
10842
0
  nsIDocument* newFullscreenDoc;
10843
0
  if (lastDoc->mFullscreenStack.Length() > 1) {
10844
0
    lastDoc->FullscreenStackPop();
10845
0
    newFullscreenDoc = lastDoc;
10846
0
  } else {
10847
0
    lastDoc->CleanupFullscreenState();
10848
0
    newFullscreenDoc = lastDoc->GetParentDocument();
10849
0
  }
10850
0
  // Dispatch the fullscreenchange event to all document listed. Note
10851
0
  // that the loop order is reversed so that events are dispatched in
10852
0
  // the tree order as indicated in the spec.
10853
0
  for (Element* e : Reversed(exitElements)) {
10854
0
    DispatchFullscreenChange(e->OwnerDoc(), e);
10855
0
  }
10856
0
  aExit->MayResolvePromise();
10857
0
10858
0
  MOZ_ASSERT(newFullscreenDoc, "If we were going to exit from fullscreen on "
10859
0
             "all documents in this doctree, we should've asked the window to "
10860
0
             "exit first instead of reaching here.");
10861
0
  if (fullScreenDoc != newFullscreenDoc &&
10862
0
      !nsContentUtils::HaveEqualPrincipals(fullScreenDoc, newFullscreenDoc)) {
10863
0
    // We've popped so enough off the stack that we've rolled back to
10864
0
    // a fullscreen element in a parent document. If this document is
10865
0
    // cross origin, dispatch an event to chrome so it knows to show
10866
0
    // the warning UI.
10867
0
    DispatchFullscreenNewOriginEvent(newFullscreenDoc);
10868
0
  }
10869
0
}
10870
10871
class nsCallRequestFullscreen : public Runnable
10872
{
10873
public:
10874
  explicit nsCallRequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
10875
    : mozilla::Runnable("nsCallRequestFullscreen")
10876
    , mRequest(std::move(aRequest))
10877
0
  {
10878
0
  }
10879
10880
  NS_IMETHOD Run() override
10881
0
  {
10882
0
    nsIDocument* doc = mRequest->Document();
10883
0
    doc->RequestFullscreen(std::move(mRequest));
10884
0
    return NS_OK;
10885
0
  }
10886
10887
  UniquePtr<FullscreenRequest> mRequest;
10888
};
10889
10890
void
10891
nsIDocument::AsyncRequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
10892
0
{
10893
0
  // Request fullscreen asynchronously.
10894
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
10895
0
  nsCOMPtr<nsIRunnable> event = new nsCallRequestFullscreen(std::move(aRequest));
10896
0
  Dispatch(TaskCategory::Other, event.forget());
10897
0
}
10898
10899
static void
10900
UpdateViewportScrollbarOverrideForFullscreen(nsIDocument* aDoc)
10901
0
{
10902
0
  if (nsPresContext* presContext = aDoc->GetPresContext()) {
10903
0
    presContext->UpdateViewportScrollStylesOverride();
10904
0
  }
10905
0
}
10906
10907
static void
10908
ClearFullscreenStateOnElement(Element* aElement)
10909
0
{
10910
0
  // Remove styles from existing top element.
10911
0
  EventStateManager::SetFullscreenState(aElement, false);
10912
0
  // Reset iframe fullscreen flag.
10913
0
  if (aElement->IsHTMLElement(nsGkAtoms::iframe)) {
10914
0
    static_cast<HTMLIFrameElement*>(aElement)->SetFullscreenFlag(false);
10915
0
  }
10916
0
}
10917
10918
void
10919
nsIDocument::CleanupFullscreenState()
10920
0
{
10921
0
  // Iterate the fullscreen stack and clear the fullscreen states.
10922
0
  // Since we also need to clear the fullscreen-ancestor state, and
10923
0
  // currently fullscreen elements can only be placed in hierarchy
10924
0
  // order in the stack, reversely iterating the stack could be more
10925
0
  // efficient. NOTE that fullscreen-ancestor state would be removed
10926
0
  // in bug 1199529, and the elements may not in hierarchy order
10927
0
  // after bug 1195213.
10928
0
  for (nsWeakPtr& weakPtr : Reversed(mFullscreenStack)) {
10929
0
    if (nsCOMPtr<Element> element = do_QueryReferent(weakPtr)) {
10930
0
      ClearFullscreenStateOnElement(element);
10931
0
    }
10932
0
  }
10933
0
  mFullscreenStack.Clear();
10934
0
  mFullscreenRoot = nullptr;
10935
0
  UpdateViewportScrollbarOverrideForFullscreen(this);
10936
0
}
10937
10938
bool
10939
nsIDocument::FullscreenStackPush(Element* aElement)
10940
0
{
10941
0
  NS_ASSERTION(aElement, "Must pass non-null to FullscreenStackPush()");
10942
0
  Element* top = FullscreenStackTop();
10943
0
  if (top == aElement || !aElement) {
10944
0
    return false;
10945
0
  }
10946
0
  EventStateManager::SetFullscreenState(aElement, true);
10947
0
  mFullscreenStack.AppendElement(do_GetWeakReference(aElement));
10948
0
  NS_ASSERTION(FullscreenStackTop() == aElement, "Should match");
10949
0
  UpdateViewportScrollbarOverrideForFullscreen(this);
10950
0
  return true;
10951
0
}
10952
10953
void
10954
nsIDocument::FullscreenStackPop()
10955
0
{
10956
0
  if (mFullscreenStack.IsEmpty()) {
10957
0
    return;
10958
0
  }
10959
0
10960
0
  ClearFullscreenStateOnElement(FullscreenStackTop());
10961
0
10962
0
  // Remove top element. Note the remaining top element in the stack
10963
0
  // will not have fullscreen style bits set, so we will need to restore
10964
0
  // them on the new top element before returning.
10965
0
  uint32_t last = mFullscreenStack.Length() - 1;
10966
0
  mFullscreenStack.RemoveElementAt(last);
10967
0
10968
0
  // Pop from the stack null elements (references to elements which have
10969
0
  // been GC'd since they were added to the stack) and elements which are
10970
0
  // no longer in this document.
10971
0
  while (!mFullscreenStack.IsEmpty()) {
10972
0
    Element* element = FullscreenStackTop();
10973
0
    if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) {
10974
0
      NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULLSCREEN),
10975
0
                   "Should have already removed fullscreen styles");
10976
0
      uint32_t last = mFullscreenStack.Length() - 1;
10977
0
      mFullscreenStack.RemoveElementAt(last);
10978
0
    } else {
10979
0
      // The top element of the stack is now an in-doc element. Return here.
10980
0
      break;
10981
0
    }
10982
0
  }
10983
0
10984
0
  UpdateViewportScrollbarOverrideForFullscreen(this);
10985
0
}
10986
10987
Element*
10988
nsIDocument::FullscreenStackTop()
10989
0
{
10990
0
  if (mFullscreenStack.IsEmpty()) {
10991
0
    return nullptr;
10992
0
  }
10993
0
  uint32_t last = mFullscreenStack.Length() - 1;
10994
0
  nsCOMPtr<Element> element(do_QueryReferent(mFullscreenStack[last]));
10995
0
  NS_ASSERTION(element, "Should have fullscreen element!");
10996
0
  NS_ASSERTION(element->IsInComposedDoc(), "Fullscreen element should be in doc");
10997
0
  NS_ASSERTION(element->OwnerDoc() == this, "Fullscreen element should be in this doc");
10998
0
  return element;
10999
0
}
11000
11001
nsTArray<Element*>
11002
nsIDocument::GetFullscreenStack() const
11003
0
{
11004
0
  nsTArray<Element*> elements;
11005
0
  for (const nsWeakPtr& ptr : mFullscreenStack) {
11006
0
    if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
11007
0
      MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULLSCREEN));
11008
0
      elements.AppendElement(elem);
11009
0
    }
11010
0
  }
11011
0
  return elements;
11012
0
}
11013
11014
// Returns true if aDoc is in the focused tab in the active window.
11015
static bool
11016
IsInActiveTab(nsIDocument* aDoc)
11017
0
{
11018
0
  nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
11019
0
  if (!docshell) {
11020
0
    return false;
11021
0
  }
11022
0
11023
0
  bool isActive = false;
11024
0
  docshell->GetIsActive(&isActive);
11025
0
  if (!isActive) {
11026
0
    return false;
11027
0
  }
11028
0
11029
0
  nsCOMPtr<nsIDocShellTreeItem> rootItem;
11030
0
  docshell->GetRootTreeItem(getter_AddRefs(rootItem));
11031
0
  if (!rootItem) {
11032
0
    return false;
11033
0
  }
11034
0
  nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
11035
0
  if (!rootWin) {
11036
0
    return false;
11037
0
  }
11038
0
11039
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11040
0
  if (!fm) {
11041
0
    return false;
11042
0
  }
11043
0
11044
0
  nsCOMPtr<mozIDOMWindowProxy> activeWindow;
11045
0
  fm->GetActiveWindow(getter_AddRefs(activeWindow));
11046
0
  if (!activeWindow) {
11047
0
    return false;
11048
0
  }
11049
0
11050
0
  return activeWindow == rootWin;
11051
0
}
11052
11053
nsresult nsIDocument::RemoteFrameFullscreenChanged(Element* aFrameElement)
11054
0
{
11055
0
  // Ensure the frame element is the fullscreen element in this document.
11056
0
  // If the frame element is already the fullscreen element in this document,
11057
0
  // this has no effect.
11058
0
  auto request = FullscreenRequest::CreateForRemote(aFrameElement);
11059
0
  RequestFullscreen(std::move(request));
11060
0
  return NS_OK;
11061
0
}
11062
11063
nsresult nsIDocument::RemoteFrameFullscreenReverted()
11064
0
{
11065
0
  UniquePtr<FullscreenExit> exit = FullscreenExit::CreateForRemote(this);
11066
0
  RestorePreviousFullscreenState(std::move(exit));
11067
0
  return NS_OK;
11068
0
}
11069
11070
/* static */ bool
11071
nsIDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
11072
0
{
11073
0
  MOZ_ASSERT(NS_IsMainThread());
11074
0
  return nsContentUtils::IsSystemCaller(aCx) ||
11075
0
         nsContentUtils::IsUnprefixedFullscreenApiEnabled();
11076
0
}
11077
11078
static bool
11079
HasFullscreenSubDocument(nsIDocument* aDoc)
11080
0
{
11081
0
  uint32_t count = CountFullscreenSubDocuments(aDoc);
11082
0
  NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
11083
0
  return count >= 1;
11084
0
}
11085
11086
// Returns nullptr if a request for Fullscreen API is currently enabled
11087
// in the given document. Returns a static string indicates the reason
11088
// why it is not enabled otherwise.
11089
static const char*
11090
GetFullscreenError(nsIDocument* aDoc, CallerType aCallerType)
11091
0
{
11092
0
  bool apiEnabled = nsContentUtils::IsFullscreenApiEnabled();
11093
0
  if (apiEnabled && aCallerType == CallerType::System) {
11094
0
    // Chrome code can always use the fullscreen API, provided it's not
11095
0
    // explicitly disabled.
11096
0
    return nullptr;
11097
0
  }
11098
0
11099
0
  if (!apiEnabled) {
11100
0
    return "FullscreenDeniedDisabled";
11101
0
  }
11102
0
11103
0
  if (!aDoc->IsVisible()) {
11104
0
    return "FullscreenDeniedHidden";
11105
0
  }
11106
0
11107
0
  // Ensure that all containing elements are <iframe> and have
11108
0
  // allowfullscreen attribute set.
11109
0
  nsCOMPtr<nsIDocShell> docShell(aDoc->GetDocShell());
11110
0
  if (!docShell || !docShell->GetFullscreenAllowed()) {
11111
0
    return "FullscreenDeniedContainerNotAllowed";
11112
0
  }
11113
0
  return nullptr;
11114
0
}
11115
11116
bool
11117
nsIDocument::FullscreenElementReadyCheck(const FullscreenRequest& aRequest)
11118
0
{
11119
0
  Element* elem = aRequest.Element();
11120
0
  // Strictly speaking, this isn't part of the fullscreen element ready
11121
0
  // check in the spec, but per steps in the spec, when an element which
11122
0
  // is already the fullscreen element requests fullscreen, nothing
11123
0
  // should change and no event should be dispatched, but we still need
11124
0
  // to resolve the returned promise.
11125
0
  if (elem == FullscreenStackTop()) {
11126
0
    aRequest.MayResolvePromise();
11127
0
    return false;
11128
0
  }
11129
0
  if (!elem->IsInComposedDoc()) {
11130
0
    aRequest.Reject("FullscreenDeniedNotInDocument");
11131
0
    return false;
11132
0
  }
11133
0
  if (elem->OwnerDoc() != this) {
11134
0
    aRequest.Reject("FullscreenDeniedMovedDocument");
11135
0
    return false;
11136
0
  }
11137
0
  if (!GetWindow()) {
11138
0
    aRequest.Reject("FullscreenDeniedLostWindow");
11139
0
    return false;
11140
0
  }
11141
0
  if (const char* msg = GetFullscreenError(this, aRequest.mCallerType)) {
11142
0
    aRequest.Reject(msg);
11143
0
    return false;
11144
0
  }
11145
0
  if (HasFullscreenSubDocument(this)) {
11146
0
    aRequest.Reject("FullscreenDeniedSubDocFullScreen");
11147
0
    return false;
11148
0
  }
11149
0
  //XXXsmaug Note, we don't follow the latest fullscreen spec here.
11150
0
  //         This whole check could be probably removed.
11151
0
  if (FullscreenStackTop() &&
11152
0
      !nsContentUtils::ContentIsHostIncludingDescendantOf(elem,
11153
0
                                                          FullscreenStackTop())) {
11154
0
    // If this document is fullscreen, only grant fullscreen requests from
11155
0
    // a descendant of the current fullscreen element.
11156
0
    aRequest.Reject("FullscreenDeniedNotDescendant");
11157
0
    return false;
11158
0
  }
11159
0
  if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
11160
0
    aRequest.Reject("FullscreenDeniedNotFocusedTab");
11161
0
    return false;
11162
0
  }
11163
0
  // Deny requests when a windowed plugin is focused.
11164
0
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
11165
0
  if (!fm) {
11166
0
    NS_WARNING("Failed to retrieve focus manager in fullscreen request.");
11167
0
    aRequest.MayRejectPromise();
11168
0
    return false;
11169
0
  }
11170
0
  if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(fm->GetFocusedElement())) {
11171
0
    aRequest.Reject("FullscreenDeniedFocusedPlugin");
11172
0
    return false;
11173
0
  }
11174
0
  return true;
11175
0
}
11176
11177
static nsCOMPtr<nsPIDOMWindowOuter>
11178
GetRootWindow(nsIDocument* aDoc)
11179
0
{
11180
0
  nsIDocShell* docShell = aDoc->GetDocShell();
11181
0
  if (!docShell) {
11182
0
    return nullptr;
11183
0
  }
11184
0
  nsCOMPtr<nsIDocShellTreeItem> rootItem;
11185
0
  docShell->GetRootTreeItem(getter_AddRefs(rootItem));
11186
0
  return rootItem ? rootItem->GetWindow() : nullptr;
11187
0
}
11188
11189
static bool
11190
ShouldApplyFullscreenDirectly(nsIDocument* aDoc,
11191
                              nsPIDOMWindowOuter* aRootWin)
11192
0
{
11193
0
  if (XRE_GetProcessType() == GeckoProcessType_Content) {
11194
0
    // If we are in the content process, we can apply the fullscreen
11195
0
    // state directly only if we have been in DOM fullscreen, because
11196
0
    // otherwise we always need to notify the chrome.
11197
0
    return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement();
11198
0
  } else {
11199
0
    // If we are in the chrome process, and the window has not been in
11200
0
    // fullscreen, we certainly need to make that fullscreen first.
11201
0
    if (!aRootWin->GetFullScreen()) {
11202
0
      return false;
11203
0
    }
11204
0
    // The iterator not being at end indicates there is still some
11205
0
    // pending fullscreen request relates to this document. We have to
11206
0
    // push the request to the pending queue so requests are handled
11207
0
    // in the correct order.
11208
0
    PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
11209
0
      aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
11210
0
    if (!iter.AtEnd()) {
11211
0
      return false;
11212
0
    }
11213
0
    // We have to apply the fullscreen state directly in this case,
11214
0
    // because nsGlobalWindow::SetFullscreenInternal() will do nothing
11215
0
    // if it is already in fullscreen. If we do not apply the state but
11216
0
    // instead add it to the queue and wait for the window as normal,
11217
0
    // we would get stuck.
11218
0
    return true;
11219
0
  }
11220
0
}
11221
11222
void
11223
nsIDocument::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
11224
0
{
11225
0
  nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this);
11226
0
  if (!rootWin) {
11227
0
    aRequest->MayRejectPromise();
11228
0
    return;
11229
0
  }
11230
0
11231
0
  if (ShouldApplyFullscreenDirectly(this, rootWin)) {
11232
0
    ApplyFullscreen(std::move(aRequest));
11233
0
    return;
11234
0
  }
11235
0
11236
0
  // Per spec only HTML, <svg>, and <math> should be allowed, but
11237
0
  // we also need to allow XUL elements right now.
11238
0
  Element* elem = aRequest->Element();
11239
0
  if (!elem->IsHTMLElement() && !elem->IsXULElement() &&
11240
0
      !elem->IsSVGElement(nsGkAtoms::svg) &&
11241
0
      !elem->IsMathMLElement(nsGkAtoms::math)) {
11242
0
    aRequest->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
11243
0
    return;
11244
0
  }
11245
0
11246
0
  // We don't need to check element ready before this point, because
11247
0
  // if we called ApplyFullscreen, it would check that for us.
11248
0
  if (!FullscreenElementReadyCheck(*aRequest)) {
11249
0
    return;
11250
0
  }
11251
0
11252
0
  PendingFullscreenChangeList::Add(std::move(aRequest));
11253
0
  if (XRE_GetProcessType() == GeckoProcessType_Content) {
11254
0
    // If we are not the top level process, dispatch an event to make
11255
0
    // our parent process go fullscreen first.
11256
0
    nsContentUtils::DispatchEventOnlyToChrome(
11257
0
      this, ToSupports(this), NS_LITERAL_STRING("MozDOMFullscreen:Request"),
11258
0
      CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr);
11259
0
  } else {
11260
0
    // Make the window fullscreen.
11261
0
    rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true);
11262
0
  }
11263
0
}
11264
11265
/* static */ bool
11266
nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
11267
0
{
11268
0
  bool handled = false;
11269
0
  PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
11270
0
    aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
11271
0
  while (!iter.AtEnd()) {
11272
0
    UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
11273
0
    nsIDocument* doc = request->Document();
11274
0
    if (doc->ApplyFullscreen(std::move(request))) {
11275
0
      handled = true;
11276
0
    }
11277
0
  }
11278
0
  return handled;
11279
0
}
11280
11281
static void
11282
ClearPendingFullscreenRequests(nsIDocument* aDoc)
11283
0
{
11284
0
  PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
11285
0
    aDoc, PendingFullscreenChangeList::eInclusiveDescendants);
11286
0
  while (!iter.AtEnd()) {
11287
0
    UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
11288
0
    request->MayRejectPromise();
11289
0
  }
11290
0
}
11291
11292
bool
11293
nsIDocument::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest)
11294
0
{
11295
0
  if (!FullscreenElementReadyCheck(*aRequest)) {
11296
0
    return false;
11297
0
  }
11298
0
11299
0
  // Stash a reference to any existing fullscreen doc, we'll use this later
11300
0
  // to detect if the origin which is fullscreen has changed.
11301
0
  nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
11302
0
11303
0
  // Stores a list of documents which we must dispatch "fullscreenchange"
11304
0
  // too. We're required by the spec to dispatch the events in root-to-leaf
11305
0
  // order, but we traverse the doctree in a leaf-to-root order, so we save
11306
0
  // references to the documents we must dispatch to so that we get the order
11307
0
  // as specified.
11308
0
  AutoTArray<nsIDocument*, 8> changed;
11309
0
11310
0
  // Remember the root document, so that if a fullscreen document is hidden
11311
0
  // we can reset fullscreen state in the remaining visible fullscreen documents.
11312
0
  nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this);
11313
0
11314
0
  // If a document is already in fullscreen, then unlock the mouse pointer
11315
0
  // before setting a new document to fullscreen
11316
0
  UnlockPointer();
11317
0
11318
0
  // Set the fullscreen element. This sets the fullscreen style on the
11319
0
  // element, and the fullscreen-ancestor styles on ancestors of the element
11320
0
  // in this document.
11321
0
  Element* elem = aRequest->Element();
11322
0
  DebugOnly<bool> x = FullscreenStackPush(elem);
11323
0
  NS_ASSERTION(x, "Fullscreen state of requesting doc should always change!");
11324
0
  // Set the iframe fullscreen flag.
11325
0
  if (auto* iframe = HTMLIFrameElement::FromNode(elem)) {
11326
0
    iframe->SetFullscreenFlag(true);
11327
0
  }
11328
0
  changed.AppendElement(this);
11329
0
11330
0
  // Propagate up the document hierarchy, setting the fullscreen element as
11331
0
  // the element's container in ancestor documents. This also sets the
11332
0
  // appropriate css styles as well. Note we don't propagate down the
11333
0
  // document hierarchy, the fullscreen element (or its container) is not
11334
0
  // visible there. Stop when we reach the root document.
11335
0
  nsIDocument* child = this;
11336
0
  while (true) {
11337
0
    child->SetFullscreenRoot(fullScreenRootDoc);
11338
0
    NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
11339
0
        "Fullscreen root should be set!");
11340
0
    if (child == fullScreenRootDoc) {
11341
0
      break;
11342
0
    }
11343
0
    nsIDocument* parent = child->GetParentDocument();
11344
0
    Element* element = parent->FindContentForSubDocument(child);
11345
0
    if (static_cast<nsDocument*>(parent)->FullscreenStackPush(element)) {
11346
0
      changed.AppendElement(parent);
11347
0
      child = parent;
11348
0
    } else {
11349
0
      // We've reached either the root, or a point in the doctree where the
11350
0
      // new fullscreen element container is the same as the previous
11351
0
      // fullscreen element's container. No more changes need to be made
11352
0
      // to the fullscreen stacks of documents further up the tree.
11353
0
      break;
11354
0
    }
11355
0
  }
11356
0
11357
0
  FullscreenRoots::Add(this);
11358
0
11359
0
  // If it is the first entry of the fullscreen, trigger an event so
11360
0
  // that the UI can response to this change, e.g. hide chrome, or
11361
0
  // notifying parent process to enter fullscreen. Note that chrome
11362
0
  // code may also want to listen to MozDOMFullscreen:NewOrigin event
11363
0
  // to pop up warning UI.
11364
0
  if (!previousFullscreenDoc) {
11365
0
    nsContentUtils::DispatchEventOnlyToChrome(
11366
0
      this, ToSupports(elem), NS_LITERAL_STRING("MozDOMFullscreen:Entered"),
11367
0
      CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr);
11368
0
  }
11369
0
11370
0
  // The origin which is fullscreen gets changed. Trigger an event so
11371
0
  // that the chrome knows to pop up a warning UI. Note that
11372
0
  // previousFullscreenDoc == nullptr upon first entry, so we always
11373
0
  // take this path on the first entry. Also note that, in a multi-
11374
0
  // process browser, the code in content process is responsible for
11375
0
  // sending message with the origin to its parent, and the parent
11376
0
  // shouldn't rely on this event itself.
11377
0
  if (aRequest->mShouldNotifyNewOrigin &&
11378
0
      !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11379
0
    DispatchFullscreenNewOriginEvent(this);
11380
0
  }
11381
0
11382
0
  // Dispatch "fullscreenchange" events. Note that the loop order is
11383
0
  // reversed so that events are dispatched in the tree order as
11384
0
  // indicated in the spec.
11385
0
  for (nsIDocument* d : Reversed(changed)) {
11386
0
    DispatchFullscreenChange(d, d->FullscreenStackTop());
11387
0
  }
11388
0
  aRequest->MayResolvePromise();
11389
0
  return true;
11390
0
}
11391
11392
bool
11393
nsIDocument::FullscreenEnabled(CallerType aCallerType)
11394
0
{
11395
0
  return !GetFullscreenError(this, aCallerType);
11396
0
}
11397
11398
void
11399
nsIDocument::SetOrientationPendingPromise(Promise* aPromise)
11400
0
{
11401
0
  mOrientationPendingPromise = aPromise;
11402
0
}
11403
11404
static void
11405
DispatchPointerLockChange(nsIDocument* aTarget)
11406
0
{
11407
0
  if (!aTarget) {
11408
0
    return;
11409
0
  }
11410
0
11411
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
11412
0
    new AsyncEventDispatcher(aTarget,
11413
0
                             NS_LITERAL_STRING("pointerlockchange"),
11414
0
                             CanBubble::eYes,
11415
0
                             ChromeOnlyDispatch::eNo);
11416
0
  asyncDispatcher->PostDOMEvent();
11417
0
}
11418
11419
static void
11420
DispatchPointerLockError(nsIDocument* aTarget, const char* aMessage)
11421
0
{
11422
0
  if (!aTarget) {
11423
0
    return;
11424
0
  }
11425
0
11426
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
11427
0
    new AsyncEventDispatcher(aTarget,
11428
0
                             NS_LITERAL_STRING("pointerlockerror"),
11429
0
                             CanBubble::eYes,
11430
0
                             ChromeOnlyDispatch::eNo);
11431
0
  asyncDispatcher->PostDOMEvent();
11432
0
  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
11433
0
                                  NS_LITERAL_CSTRING("DOM"), aTarget,
11434
0
                                  nsContentUtils::eDOM_PROPERTIES,
11435
0
                                  aMessage);
11436
0
}
11437
11438
class PointerLockRequest final : public Runnable
11439
{
11440
public:
11441
  PointerLockRequest(Element* aElement, bool aUserInputOrChromeCaller)
11442
    : mozilla::Runnable("PointerLockRequest")
11443
    , mElement(do_GetWeakReference(aElement))
11444
    , mDocument(do_GetWeakReference(aElement->OwnerDoc()))
11445
    , mUserInputOrChromeCaller(aUserInputOrChromeCaller)
11446
0
  {}
11447
11448
  NS_IMETHOD Run() final;
11449
11450
private:
11451
  nsWeakPtr mElement;
11452
  nsWeakPtr mDocument;
11453
  bool mUserInputOrChromeCaller;
11454
};
11455
11456
static const char*
11457
GetPointerLockError(Element* aElement, Element* aCurrentLock,
11458
                    bool aNoFocusCheck = false)
11459
0
{
11460
0
  // Check if pointer lock pref is enabled
11461
0
  if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
11462
0
    return "PointerLockDeniedDisabled";
11463
0
  }
11464
0
11465
0
  nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
11466
0
  if (aCurrentLock && aCurrentLock->OwnerDoc() != ownerDoc) {
11467
0
    return "PointerLockDeniedInUse";
11468
0
  }
11469
0
11470
0
  if (!aElement->IsInComposedDoc()) {
11471
0
    return "PointerLockDeniedNotInDocument";
11472
0
  }
11473
0
11474
0
  if (ownerDoc->GetSandboxFlags() & SANDBOXED_POINTER_LOCK) {
11475
0
    return "PointerLockDeniedSandboxed";
11476
0
  }
11477
0
11478
0
  // Check if the element is in a document with a docshell.
11479
0
  if (!ownerDoc->GetContainer()) {
11480
0
    return "PointerLockDeniedHidden";
11481
0
  }
11482
0
  nsCOMPtr<nsPIDOMWindowOuter> ownerWindow = ownerDoc->GetWindow();
11483
0
  if (!ownerWindow) {
11484
0
    return "PointerLockDeniedHidden";
11485
0
  }
11486
0
  nsCOMPtr<nsPIDOMWindowInner> ownerInnerWindow = ownerDoc->GetInnerWindow();
11487
0
  if (!ownerInnerWindow) {
11488
0
    return "PointerLockDeniedHidden";
11489
0
  }
11490
0
  if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
11491
0
    return "PointerLockDeniedHidden";
11492
0
  }
11493
0
11494
0
  nsCOMPtr<nsPIDOMWindowOuter> top = ownerWindow->GetScriptableTop();
11495
0
  if (!top || !top->GetExtantDoc() || top->GetExtantDoc()->Hidden()) {
11496
0
    return "PointerLockDeniedHidden";
11497
0
  }
11498
0
11499
0
  if (!aNoFocusCheck) {
11500
0
    mozilla::ErrorResult rv;
11501
0
    if (!top->GetExtantDoc()->HasFocus(rv)) {
11502
0
      return "PointerLockDeniedNotFocused";
11503
0
    }
11504
0
  }
11505
0
11506
0
  return nullptr;
11507
0
}
11508
11509
static void
11510
ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument,
11511
                           Element* aPointerLockedElement)
11512
0
{
11513
0
  // aDocument here is not really necessary, as it is the uncomposed
11514
0
  // document of both aElement and aPointerLockedElement as far as one
11515
0
  // is not nullptr, and they wouldn't both be nullptr in any case.
11516
0
  // But since the caller of this function should have known what the
11517
0
  // document is, we just don't try to figure out what it should be.
11518
0
  MOZ_ASSERT(aDocument);
11519
0
  MOZ_ASSERT(aElement != aPointerLockedElement);
11520
0
  if (aPointerLockedElement) {
11521
0
    MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument);
11522
0
    aPointerLockedElement->ClearPointerLock();
11523
0
  }
11524
0
  if (aElement) {
11525
0
    MOZ_ASSERT(aElement->GetComposedDoc() == aDocument);
11526
0
    aElement->SetPointerLock();
11527
0
    EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
11528
0
    EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
11529
0
    NS_ASSERTION(EventStateManager::sPointerLockedElement &&
11530
0
                 EventStateManager::sPointerLockedDoc,
11531
0
                 "aElement and this should support weak references!");
11532
0
  } else {
11533
0
    EventStateManager::sPointerLockedElement = nullptr;
11534
0
    EventStateManager::sPointerLockedDoc = nullptr;
11535
0
  }
11536
0
  // Retarget all events to aElement via capture or
11537
0
  // stop retargeting if aElement is nullptr.
11538
0
  nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
11539
0
  DispatchPointerLockChange(aDocument);
11540
0
}
11541
11542
NS_IMETHODIMP
11543
PointerLockRequest::Run()
11544
0
{
11545
0
  nsCOMPtr<Element> e = do_QueryReferent(mElement);
11546
0
  nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
11547
0
  nsDocument* d = static_cast<nsDocument*>(doc.get());
11548
0
  const char* error = nullptr;
11549
0
  if (!e || !d || !e->GetComposedDoc()) {
11550
0
    error = "PointerLockDeniedNotInDocument";
11551
0
  } else if (e->GetComposedDoc() != d) {
11552
0
    error = "PointerLockDeniedMovedDocument";
11553
0
  }
11554
0
  if (!error) {
11555
0
    nsCOMPtr<Element> pointerLockedElement =
11556
0
      do_QueryReferent(EventStateManager::sPointerLockedElement);
11557
0
    if (e == pointerLockedElement) {
11558
0
      DispatchPointerLockChange(d);
11559
0
      return NS_OK;
11560
0
    }
11561
0
    // Note, we must bypass focus change, so pass true as the last parameter!
11562
0
    error = GetPointerLockError(e, pointerLockedElement, true);
11563
0
    // Another element in the same document is requesting pointer lock,
11564
0
    // just grant it without user input check.
11565
0
    if (!error && pointerLockedElement) {
11566
0
      ChangePointerLockedElement(e, d, pointerLockedElement);
11567
0
      return NS_OK;
11568
0
    }
11569
0
  }
11570
0
  // If it is neither user input initiated, nor requested in fullscreen,
11571
0
  // it should be rejected.
11572
0
  if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) {
11573
0
    error = "PointerLockDeniedNotInputDriven";
11574
0
  }
11575
0
  if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
11576
0
    error = "PointerLockDeniedFailedToLock";
11577
0
  }
11578
0
  if (error) {
11579
0
    DispatchPointerLockError(d, error);
11580
0
    return NS_OK;
11581
0
  }
11582
0
11583
0
  ChangePointerLockedElement(e, d, nullptr);
11584
0
  nsContentUtils::DispatchEventOnlyToChrome(
11585
0
    doc, ToSupports(e), NS_LITERAL_STRING("MozDOMPointerLock:Entered"),
11586
0
    CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr);
11587
0
  return NS_OK;
11588
0
}
11589
11590
void
11591
nsIDocument::RequestPointerLock(Element* aElement, CallerType aCallerType)
11592
0
{
11593
0
  NS_ASSERTION(aElement,
11594
0
    "Must pass non-null element to nsDocument::RequestPointerLock");
11595
0
11596
0
  nsCOMPtr<Element> pointerLockedElement =
11597
0
    do_QueryReferent(EventStateManager::sPointerLockedElement);
11598
0
  if (aElement == pointerLockedElement) {
11599
0
    DispatchPointerLockChange(this);
11600
0
    return;
11601
0
  }
11602
0
11603
0
  if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) {
11604
0
    DispatchPointerLockError(this, msg);
11605
0
    return;
11606
0
  }
11607
0
11608
0
  bool userInputOrSystemCaller = EventStateManager::IsHandlingUserInput() ||
11609
0
                                 aCallerType == CallerType::System;
11610
0
  nsCOMPtr<nsIRunnable> request =
11611
0
    new PointerLockRequest(aElement, userInputOrSystemCaller);
11612
0
  Dispatch(TaskCategory::Other, request.forget());
11613
0
}
11614
11615
bool
11616
nsIDocument::SetPointerLock(Element* aElement, int aCursorStyle)
11617
0
{
11618
0
  MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this,
11619
0
             "We should be either unlocking pointer (aElement is nullptr), "
11620
0
             "or locking pointer to an element in this document");
11621
#ifdef DEBUG
11622
  if (!aElement) {
11623
    nsCOMPtr<nsIDocument> pointerLockedDoc =
11624
      do_QueryReferent(EventStateManager::sPointerLockedDoc);
11625
    MOZ_ASSERT(pointerLockedDoc == this);
11626
  }
11627
#endif
11628
11629
0
  nsIPresShell* shell = GetShell();
11630
0
  if (!shell) {
11631
0
    NS_WARNING("SetPointerLock(): No PresShell");
11632
0
    if (!aElement) {
11633
0
      // If we are unlocking pointer lock, but for some reason the doc
11634
0
      // has already detached from the presshell, just ask the event
11635
0
      // state manager to release the pointer.
11636
0
      EventStateManager::SetPointerLock(nullptr, nullptr);
11637
0
      return true;
11638
0
    }
11639
0
    return false;
11640
0
  }
11641
0
  nsPresContext* presContext = shell->GetPresContext();
11642
0
  if (!presContext) {
11643
0
    NS_WARNING("SetPointerLock(): Unable to get PresContext");
11644
0
    return false;
11645
0
  }
11646
0
11647
0
  nsCOMPtr<nsIWidget> widget;
11648
0
  nsIFrame* rootFrame = shell->GetRootFrame();
11649
0
  if (!NS_WARN_IF(!rootFrame)) {
11650
0
    widget = rootFrame->GetNearestWidget();
11651
0
    NS_WARNING_ASSERTION(
11652
0
      widget,
11653
0
      "SetPointerLock(): Unable to find widget in "
11654
0
      "shell->GetRootFrame()->GetNearestWidget();");
11655
0
    if (aElement && !widget) {
11656
0
      return false;
11657
0
    }
11658
0
  }
11659
0
11660
0
  // Hide the cursor and set pointer lock for future mouse events
11661
0
  RefPtr<EventStateManager> esm = presContext->EventStateManager();
11662
0
  esm->SetCursor(aCursorStyle, nullptr, false,
11663
0
                 0.0f, 0.0f, widget, true);
11664
0
  EventStateManager::SetPointerLock(widget, aElement);
11665
0
11666
0
  return true;
11667
0
}
11668
11669
void
11670
nsIDocument::UnlockPointer(nsIDocument* aDoc)
11671
0
{
11672
0
  if (!EventStateManager::sIsPointerLocked) {
11673
0
    return;
11674
0
  }
11675
0
11676
0
  nsCOMPtr<nsIDocument> pointerLockedDoc =
11677
0
    do_QueryReferent(EventStateManager::sPointerLockedDoc);
11678
0
  if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
11679
0
    return;
11680
0
  }
11681
0
  if (!pointerLockedDoc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
11682
0
    return;
11683
0
  }
11684
0
11685
0
  nsCOMPtr<Element> pointerLockedElement =
11686
0
    do_QueryReferent(EventStateManager::sPointerLockedElement);
11687
0
  ChangePointerLockedElement(nullptr, pointerLockedDoc, pointerLockedElement);
11688
0
11689
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
11690
0
    new AsyncEventDispatcher(pointerLockedElement,
11691
0
                             NS_LITERAL_STRING("MozDOMPointerLock:Exited"),
11692
0
                             CanBubble::eYes,
11693
0
                             ChromeOnlyDispatch::eYes);
11694
0
  asyncDispatcher->RunDOMEventWhenSafe();
11695
0
}
11696
11697
void
11698
nsIDocument::UpdateVisibilityState()
11699
0
{
11700
0
  dom::VisibilityState oldState = mVisibilityState;
11701
0
  mVisibilityState = ComputeVisibilityState();
11702
0
  if (oldState != mVisibilityState) {
11703
0
    nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
11704
0
                                         NS_LITERAL_STRING("visibilitychange"),
11705
0
                                         CanBubble::eYes,
11706
0
                                         Cancelable::eNo);
11707
0
    EnumerateActivityObservers(NotifyActivityChanged, nullptr);
11708
0
  }
11709
0
11710
0
  if (mVisibilityState == dom::VisibilityState::Visible) {
11711
0
    MaybeActiveMediaComponents();
11712
0
  }
11713
0
}
11714
11715
VisibilityState
11716
nsIDocument::ComputeVisibilityState() const
11717
0
{
11718
0
  // We have to check a few pieces of information here:
11719
0
  // 1)  Are we in bfcache (!IsVisible())?  If so, nothing else matters.
11720
0
  // 2)  Do we have an outer window?  If not, we're hidden.  Note that we don't
11721
0
  //     want to use GetWindow here because it does weird groveling for windows
11722
0
  //     in some cases.
11723
0
  // 3)  Is our outer window background?  If so, we're hidden.
11724
0
  // Otherwise, we're visible.
11725
0
  if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
11726
0
      mWindow->GetOuterWindow()->IsBackground()) {
11727
0
    return dom::VisibilityState::Hidden;
11728
0
  }
11729
0
11730
0
  return dom::VisibilityState::Visible;
11731
0
}
11732
11733
void
11734
nsIDocument::PostVisibilityUpdateEvent()
11735
0
{
11736
0
  nsCOMPtr<nsIRunnable> event =
11737
0
    NewRunnableMethod("nsIDocument::UpdateVisibilityState",
11738
0
                      this,
11739
0
                      &nsIDocument::UpdateVisibilityState);
11740
0
  Dispatch(TaskCategory::Other, event.forget());
11741
0
}
11742
11743
void
11744
nsIDocument::MaybeActiveMediaComponents()
11745
0
{
11746
0
  if (!mWindow) {
11747
0
    return;
11748
0
  }
11749
0
11750
0
  GetWindow()->MaybeActiveMediaComponents();
11751
0
}
11752
11753
/* virtual */ void
11754
nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aSizes) const
11755
0
{
11756
0
  if (mPresShell) {
11757
0
    mPresShell->AddSizeOfIncludingThis(aSizes);
11758
0
  }
11759
0
11760
0
  aSizes.mPropertyTablesSize +=
11761
0
    mPropertyTable.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11762
0
11763
0
  if (EventListenerManager* elm = GetExistingListenerManager()) {
11764
0
    aSizes.mDOMEventListenersCount += elm->ListenerCount();
11765
0
  }
11766
0
11767
0
  if (mNodeInfoManager) {
11768
0
    mNodeInfoManager->AddSizeOfIncludingThis(aSizes);
11769
0
  }
11770
0
11771
0
  aSizes.mDOMMediaQueryLists +=
11772
0
    mDOMMediaQueryLists.sizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11773
0
11774
0
  for (const MediaQueryList* mql : mDOMMediaQueryLists) {
11775
0
    aSizes.mDOMMediaQueryLists +=
11776
0
      mql->SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11777
0
  }
11778
0
11779
0
  // Measurement of the following members may be added later if DMD finds it
11780
0
  // is worthwhile:
11781
0
  // - many!
11782
0
}
11783
11784
void
11785
nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const
11786
0
{
11787
0
  aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
11788
0
  DocAddSizeOfExcludingThis(aWindowSizes);
11789
0
}
11790
11791
11792
void
11793
nsDocument::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
11794
                                   size_t* aNodeSize) const
11795
0
{
11796
0
  // This AddSizeOfExcludingThis() overrides the one from nsINode.  But
11797
0
  // nsDocuments can only appear at the top of the DOM tree, and we use the
11798
0
  // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
11799
0
  // be called.
11800
0
  MOZ_CRASH();
11801
0
}
11802
11803
/* static */ void
11804
nsIDocument::AddSizeOfNodeTree(nsINode& aNode, nsWindowSizes& aWindowSizes)
11805
0
{
11806
0
  size_t nodeSize = 0;
11807
0
  aNode.AddSizeOfIncludingThis(aWindowSizes, &nodeSize);
11808
0
11809
0
  // This is where we transfer the nodeSize obtained from
11810
0
  // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
11811
0
  switch (aNode.NodeType()) {
11812
0
  case nsINode::ELEMENT_NODE:
11813
0
    aWindowSizes.mDOMElementNodesSize += nodeSize;
11814
0
    break;
11815
0
  case nsINode::TEXT_NODE:
11816
0
    aWindowSizes.mDOMTextNodesSize += nodeSize;
11817
0
    break;
11818
0
  case nsINode::CDATA_SECTION_NODE:
11819
0
    aWindowSizes.mDOMCDATANodesSize += nodeSize;
11820
0
    break;
11821
0
  case nsINode::COMMENT_NODE:
11822
0
    aWindowSizes.mDOMCommentNodesSize += nodeSize;
11823
0
    break;
11824
0
  default:
11825
0
    aWindowSizes.mDOMOtherSize += nodeSize;
11826
0
    break;
11827
0
  }
11828
0
11829
0
  if (EventListenerManager* elm = aNode.GetExistingListenerManager()) {
11830
0
    aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
11831
0
  }
11832
0
11833
0
  if (aNode.IsContent()) {
11834
0
    nsTArray<nsIContent*> anonKids;
11835
0
    nsContentUtils::AppendNativeAnonymousChildren(aNode.AsContent(),
11836
0
                                                  anonKids,
11837
0
                                                  nsIContent::eAllChildren);
11838
0
    for (nsIContent* anonKid : anonKids) {
11839
0
      AddSizeOfNodeTree(*anonKid, aWindowSizes);
11840
0
    }
11841
0
11842
0
    if (auto* element = Element::FromNode(aNode)) {
11843
0
      if (ShadowRoot* shadow = element->GetShadowRoot()) {
11844
0
        AddSizeOfNodeTree(*shadow, aWindowSizes);
11845
0
      }
11846
0
11847
0
      for (nsXBLBinding* binding = element->GetXBLBinding();
11848
0
           binding;
11849
0
           binding = binding->GetBaseBinding()) {
11850
0
        if (nsIContent* anonContent = binding->GetAnonymousContent()) {
11851
0
          AddSizeOfNodeTree(*anonContent, aWindowSizes);
11852
0
        }
11853
0
      }
11854
0
    }
11855
0
  }
11856
0
11857
0
  // NOTE(emilio): If you feel smart and want to change this function to use
11858
0
  // GetNextNode(), think twice, since you'd need to handle <xbl:content> in a
11859
0
  // sane way, and kids of <content> won't point to the parent, so we'd never
11860
0
  // find the root node where we should stop at.
11861
0
  for (nsIContent* kid = aNode.GetFirstChild(); kid; kid = kid->GetNextSibling()) {
11862
0
    AddSizeOfNodeTree(*kid, aWindowSizes);
11863
0
  }
11864
0
}
11865
11866
void
11867
nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
11868
0
{
11869
0
  nsINode::AddSizeOfExcludingThis(aWindowSizes, &aWindowSizes.mDOMOtherSize);
11870
0
11871
0
  for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextSibling()) {
11872
0
    AddSizeOfNodeTree(*kid, aWindowSizes);
11873
0
  }
11874
0
11875
0
  // IMPORTANT: for our ComputedValues measurements, we want to measure
11876
0
  // ComputedValues accessible from DOM elements before ComputedValues not
11877
0
  // accessible from DOM elements (i.e. accessible only from the frame tree).
11878
0
  //
11879
0
  // Therefore, the measurement of the nsIDocument superclass must happen after
11880
0
  // the measurement of DOM nodes (above), because nsIDocument contains the
11881
0
  // PresShell, which contains the frame tree.
11882
0
  nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
11883
0
11884
0
  DocumentOrShadowRoot::AddSizeOfExcludingThis(aWindowSizes);
11885
0
  for (auto& sheetArray : mAdditionalSheets) {
11886
0
    AddSizeOfOwnedSheetArrayExcludingThis(aWindowSizes, sheetArray);
11887
0
  }
11888
0
  // Lumping in the loader with the style-sheets size is not ideal,
11889
0
  // but most of the things in there are in fact stylesheets, so it
11890
0
  // doesn't seem worthwhile to separate it out.
11891
0
  aWindowSizes.mLayoutStyleSheetsSize +=
11892
0
    CSSLoader()->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
11893
0
11894
0
  aWindowSizes.mDOMOtherSize += mAttrStyleSheet
11895
0
                              ? mAttrStyleSheet->DOMSizeOfIncludingThis(
11896
0
                                  aWindowSizes.mState.mMallocSizeOf)
11897
0
                              : 0;
11898
0
11899
0
  aWindowSizes.mDOMOtherSize +=
11900
0
    mStyledLinks.ShallowSizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
11901
0
11902
0
  // Measurement of the following members may be added later if DMD finds it
11903
0
  // is worthwhile:
11904
0
  // - many!
11905
0
}
11906
11907
already_AddRefed<nsIDocument>
11908
nsIDocument::Constructor(const GlobalObject& aGlobal,
11909
                         ErrorResult& rv)
11910
0
{
11911
0
  nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
11912
0
  if (!global) {
11913
0
    rv.Throw(NS_ERROR_UNEXPECTED);
11914
0
    return nullptr;
11915
0
  }
11916
0
11917
0
  nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports());
11918
0
  if (!prin) {
11919
0
    rv.Throw(NS_ERROR_UNEXPECTED);
11920
0
    return nullptr;
11921
0
  }
11922
0
11923
0
  nsCOMPtr<nsIURI> uri;
11924
0
  NS_NewURI(getter_AddRefs(uri), "about:blank");
11925
0
  if (!uri) {
11926
0
    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
11927
0
    return nullptr;
11928
0
  }
11929
0
11930
0
  nsCOMPtr<nsIDocument> doc;
11931
0
  nsresult res =
11932
0
    NS_NewDOMDocument(getter_AddRefs(doc),
11933
0
                      VoidString(),
11934
0
                      EmptyString(),
11935
0
                      nullptr,
11936
0
                      uri,
11937
0
                      uri,
11938
0
                      prin->GetPrincipal(),
11939
0
                      true,
11940
0
                      global,
11941
0
                      DocumentFlavorPlain);
11942
0
  if (NS_FAILED(res)) {
11943
0
    rv.Throw(res);
11944
0
    return nullptr;
11945
0
  }
11946
0
11947
0
  doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
11948
0
11949
0
  return doc.forget();
11950
0
}
11951
11952
XPathExpression*
11953
nsIDocument::CreateExpression(const nsAString& aExpression,
11954
                              XPathNSResolver* aResolver,
11955
                              ErrorResult& rv)
11956
0
{
11957
0
  return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
11958
0
}
11959
11960
nsINode*
11961
nsIDocument::CreateNSResolver(nsINode& aNodeResolver)
11962
0
{
11963
0
  return XPathEvaluator()->CreateNSResolver(aNodeResolver);
11964
0
}
11965
11966
already_AddRefed<XPathResult>
11967
nsIDocument::Evaluate(JSContext* aCx, const nsAString& aExpression,
11968
                      nsINode& aContextNode, XPathNSResolver* aResolver,
11969
                      uint16_t aType, JS::Handle<JSObject*> aResult,
11970
                      ErrorResult& rv)
11971
0
{
11972
0
  return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
11973
0
                                    aType, aResult, rv);
11974
0
}
11975
11976
already_AddRefed<nsIXULWindow>
11977
nsIDocument::GetXULWindowIfToplevelChrome() const
11978
0
{
11979
0
  nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
11980
0
  if (!item) {
11981
0
    return nullptr;
11982
0
  }
11983
0
  nsCOMPtr<nsIDocShellTreeOwner> owner;
11984
0
  item->GetTreeOwner(getter_AddRefs(owner));
11985
0
  nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner);
11986
0
  if (!xulWin) {
11987
0
    return nullptr;
11988
0
  }
11989
0
  nsCOMPtr<nsIDocShell> xulWinShell;
11990
0
  xulWin->GetDocShell(getter_AddRefs(xulWinShell));
11991
0
  if (!SameCOMIdentity(xulWinShell, item)) {
11992
0
    return nullptr;
11993
0
  }
11994
0
  return xulWin.forget();
11995
0
}
11996
11997
nsIDocument*
11998
nsIDocument::GetTopLevelContentDocument()
11999
0
{
12000
0
  nsIDocument* parent;
12001
0
12002
0
  if (!mLoadedAsData) {
12003
0
    parent = this;
12004
0
  } else {
12005
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
12006
0
    if (!window) {
12007
0
      return nullptr;
12008
0
    }
12009
0
12010
0
    parent = window->GetExtantDoc();
12011
0
    if (!parent) {
12012
0
      return nullptr;
12013
0
    }
12014
0
  }
12015
0
12016
0
  do {
12017
0
    if (parent->IsTopLevelContentDocument()) {
12018
0
      break;
12019
0
    }
12020
0
12021
0
    // If we ever have a non-content parent before we hit a toplevel content
12022
0
    // parent, then we're never going to find one.  Just bail.
12023
0
    if (!parent->IsContentDocument()) {
12024
0
      return nullptr;
12025
0
    }
12026
0
12027
0
    nsIDocument* candidate = parent->GetParentDocument();
12028
0
    parent = static_cast<nsDocument*>(candidate);
12029
0
  } while (parent);
12030
0
12031
0
  return parent;
12032
0
}
12033
12034
static bool
12035
MightBeChromeScheme(nsIURI* aURI)
12036
0
{
12037
0
  MOZ_ASSERT(aURI);
12038
0
  bool isChrome = true;
12039
0
  aURI->SchemeIs("chrome", &isChrome);
12040
0
  return isChrome;
12041
0
}
12042
12043
static bool
12044
MightBeAboutOrChromeScheme(nsIURI* aURI)
12045
0
{
12046
0
  MOZ_ASSERT(aURI);
12047
0
  bool isAbout = true;
12048
0
  aURI->SchemeIs("about", &isAbout);
12049
0
  return isAbout || MightBeChromeScheme(aURI);
12050
0
}
12051
12052
void
12053
nsIDocument::PropagateUseCounters(nsIDocument* aParentDocument)
12054
0
{
12055
0
  MOZ_ASSERT(this != aParentDocument);
12056
0
12057
0
  // Don't count chrome resources, even in the web content.
12058
0
  nsCOMPtr<nsIURI> uri;
12059
0
  NodePrincipal()->GetURI(getter_AddRefs(uri));
12060
0
  if (!uri || MightBeChromeScheme(uri)) {
12061
0
    return;
12062
0
  }
12063
0
12064
0
  // What really matters here is that our use counters get propagated as
12065
0
  // high up in the content document hierarchy as possible.  So,
12066
0
  // starting with aParentDocument, we need to find the toplevel content
12067
0
  // document, and propagate our use counters into its
12068
0
  // mChildDocumentUseCounters.
12069
0
  nsIDocument* contentParent = aParentDocument->GetTopLevelContentDocument();
12070
0
12071
0
  if (!contentParent) {
12072
0
    return;
12073
0
  }
12074
0
12075
0
  contentParent->mChildDocumentUseCounters |= mUseCounters;
12076
0
  contentParent->mChildDocumentUseCounters |= mChildDocumentUseCounters;
12077
0
}
12078
12079
void
12080
nsIDocument::SetPageUseCounter(UseCounter aUseCounter)
12081
0
{
12082
0
  // We want to set the use counter on the "page" that owns us; the definition
12083
0
  // of "page" depends on what kind of document we are.  See the comments below
12084
0
  // for details.  In any event, checking all the conditions below is
12085
0
  // reasonably expensive, so we cache whether we've notified our owning page.
12086
0
  if (mNotifiedPageForUseCounter[aUseCounter]) {
12087
0
    return;
12088
0
  }
12089
0
  mNotifiedPageForUseCounter[aUseCounter] = true;
12090
0
12091
0
  if (mDisplayDocument) {
12092
0
    // If we are a resource document, we won't have a docshell and so we won't
12093
0
    // record any page use counters on this document.  Instead, we should
12094
0
    // forward it up to the document that loaded us.
12095
0
    MOZ_ASSERT(!mDocumentContainer);
12096
0
    mDisplayDocument->SetChildDocumentUseCounter(aUseCounter);
12097
0
    return;
12098
0
  }
12099
0
12100
0
  if (IsBeingUsedAsImage()) {
12101
0
    // If this is an SVG image document, we also won't have a docshell.
12102
0
    MOZ_ASSERT(!mDocumentContainer);
12103
0
    return;
12104
0
  }
12105
0
12106
0
  // We only care about use counters in content.  If we're already a toplevel
12107
0
  // content document, then we should have already set the use counter on
12108
0
  // ourselves, and we are done.
12109
0
  nsIDocument* contentParent = GetTopLevelContentDocument();
12110
0
  if (!contentParent) {
12111
0
    return;
12112
0
  }
12113
0
12114
0
  if (this == contentParent) {
12115
0
    MOZ_ASSERT(GetUseCounter(aUseCounter));
12116
0
    return;
12117
0
  }
12118
0
12119
0
  contentParent->SetChildDocumentUseCounter(aUseCounter);
12120
0
}
12121
12122
bool
12123
nsIDocument::HasScriptsBlockedBySandbox()
12124
0
{
12125
0
  return mSandboxFlags & SANDBOXED_SCRIPTS;
12126
0
}
12127
12128
bool
12129
nsIDocument::InlineScriptAllowedByCSP()
12130
0
{
12131
0
  // this function assumes the inline script is parser created
12132
0
  //  (e.g., before setting attribute(!) event handlers)
12133
0
  nsCOMPtr<nsIContentSecurityPolicy> csp;
12134
0
  nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
12135
0
  NS_ENSURE_SUCCESS(rv, true);
12136
0
  bool allowsInlineScript = true;
12137
0
  if (csp) {
12138
0
    nsresult rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
12139
0
                                       EmptyString(), // aNonce
12140
0
                                       true,          // aParserCreated
12141
0
                                       nullptr,       // aTriggeringElement
12142
0
                                       EmptyString(), // FIXME get script sample (bug 1314567)
12143
0
                                       0,             // aLineNumber
12144
0
                                       0,             // aColumnNumber
12145
0
                                       &allowsInlineScript);
12146
0
    NS_ENSURE_SUCCESS(rv, true);
12147
0
  }
12148
0
  return allowsInlineScript;
12149
0
}
12150
12151
static bool
12152
ReportExternalResourceUseCounters(nsIDocument* aDocument, void* aData)
12153
0
{
12154
0
  const auto reportKind
12155
0
    = nsDocument::UseCounterReportKind::eIncludeExternalResources;
12156
0
  static_cast<nsDocument*>(aDocument)->ReportUseCounters(reportKind);
12157
0
  return true;
12158
0
}
12159
12160
void
12161
nsIDocument::ReportUseCounters(UseCounterReportKind aKind)
12162
0
{
12163
0
  static const bool sDebugUseCounters = false;
12164
0
  if (mReportedUseCounters) {
12165
0
    return;
12166
0
  }
12167
0
12168
0
  mReportedUseCounters = true;
12169
0
12170
0
  if (aKind == UseCounterReportKind::eIncludeExternalResources) {
12171
0
    EnumerateExternalResources(ReportExternalResourceUseCounters, nullptr);
12172
0
  }
12173
0
12174
0
  if (Telemetry::HistogramUseCounterCount > 0 &&
12175
0
      (IsContentDocument() || IsResourceDoc())) {
12176
0
    nsCOMPtr<nsIURI> uri;
12177
0
    NodePrincipal()->GetURI(getter_AddRefs(uri));
12178
0
    if (!uri || MightBeAboutOrChromeScheme(uri)) {
12179
0
      return;
12180
0
    }
12181
0
12182
0
    if (sDebugUseCounters) {
12183
0
      nsCString spec = uri->GetSpecOrDefault();
12184
0
12185
0
      // URIs can be rather long for data documents, so truncate them to
12186
0
      // some reasonable length.
12187
0
      spec.Truncate(std::min(128U, spec.Length()));
12188
0
      printf("-- Use counters for %s --\n", spec.get());
12189
0
    }
12190
0
12191
0
    // We keep separate counts for individual documents and top-level
12192
0
    // pages to more accurately track how many web pages might break if
12193
0
    // certain features were removed.  Consider the case of a single
12194
0
    // HTML document with several SVG images and/or iframes with
12195
0
    // sub-documents of their own.  If we maintained a single set of use
12196
0
    // counters and all the sub-documents use a particular feature, then
12197
0
    // telemetry would indicate that we would be breaking N documents if
12198
0
    // that feature were removed.  Whereas with a document/top-level
12199
0
    // page split, we can see that N documents would be affected, but
12200
0
    // only a single web page would be affected.
12201
0
12202
0
    // The difference between the values of these two histograms and the
12203
0
    // related use counters below tell us how many pages did *not* use
12204
0
    // the feature in question.  For instance, if we see that a given
12205
0
    // session has destroyed 30 content documents, but a particular use
12206
0
    // counter shows only a count of 5, we can infer that the use
12207
0
    // counter was *not* used in 25 of those 30 documents.
12208
0
    //
12209
0
    // We do things this way, rather than accumulating a boolean flag
12210
0
    // for each use counter, to avoid sending histograms for features
12211
0
    // that don't get widely used.  Doing things in this fashion means
12212
0
    // smaller telemetry payloads and faster processing on the server
12213
0
    // side.
12214
0
    Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1);
12215
0
    if (IsTopLevelContentDocument()) {
12216
0
      Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1);
12217
0
    }
12218
0
12219
0
    for (int32_t c = 0;
12220
0
         c < eUseCounter_Count; ++c) {
12221
0
      UseCounter uc = static_cast<UseCounter>(c);
12222
0
12223
0
      Telemetry::HistogramID id =
12224
0
        static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter + uc * 2);
12225
0
      bool value = GetUseCounter(uc);
12226
0
12227
0
      if (value) {
12228
0
        if (sDebugUseCounters) {
12229
0
          const char* name = Telemetry::GetHistogramName(id);
12230
0
          if (name) {
12231
0
            printf("  %s", name);
12232
0
          } else {
12233
0
            printf("  #%d", id);
12234
0
          }
12235
0
          printf(": %d\n", value);
12236
0
        }
12237
0
12238
0
        Telemetry::Accumulate(id, 1);
12239
0
      }
12240
0
12241
0
      if (IsTopLevelContentDocument()) {
12242
0
        id = static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter +
12243
0
                                                 uc * 2 + 1);
12244
0
        value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc);
12245
0
12246
0
        if (value) {
12247
0
          if (sDebugUseCounters) {
12248
0
            const char* name = Telemetry::GetHistogramName(id);
12249
0
            if (name) {
12250
0
              printf("  %s", name);
12251
0
            } else {
12252
0
              printf("  #%d", id);
12253
0
            }
12254
0
            printf(": %d\n", value);
12255
0
          }
12256
0
12257
0
          Telemetry::Accumulate(id, 1);
12258
0
        }
12259
0
      }
12260
0
    }
12261
0
  }
12262
0
12263
0
  if (IsTopLevelContentDocument() && !IsResourceDoc()) {
12264
0
    using mozilla::Telemetry::LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE;
12265
0
    LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE label;
12266
0
    switch (mViewportOverflowType) {
12267
0
#define CASE_OVERFLOW_TYPE(t_)                            \
12268
0
      case ViewportOverflowType::t_:                      \
12269
0
        label = LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE::t_; \
12270
0
        break;
12271
0
      CASE_OVERFLOW_TYPE(NoOverflow)
12272
0
      CASE_OVERFLOW_TYPE(Desktop)
12273
0
      CASE_OVERFLOW_TYPE(ButNotMinScaleSize)
12274
0
      CASE_OVERFLOW_TYPE(MinScaleSize)
12275
0
#undef CASE_OVERFLOW_TYPE
12276
0
    }
12277
0
    Telemetry::AccumulateCategorical(label);
12278
0
  }
12279
0
}
12280
12281
void
12282
nsIDocument::UpdateIntersectionObservations()
12283
0
{
12284
0
  if (mIntersectionObservers.IsEmpty()) {
12285
0
    return;
12286
0
  }
12287
0
12288
0
  DOMHighResTimeStamp time = 0;
12289
0
  if (nsPIDOMWindowInner* window = GetInnerWindow()) {
12290
0
    Performance* perf = window->GetPerformance();
12291
0
    if (perf) {
12292
0
      time = perf->Now();
12293
0
    }
12294
0
  }
12295
0
  nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count());
12296
0
  for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
12297
0
    DOMIntersectionObserver* observer = iter.Get()->GetKey();
12298
0
    observers.AppendElement(observer);
12299
0
  }
12300
0
  for (const auto& observer : observers) {
12301
0
    if (observer) {
12302
0
      observer->Update(this, time);
12303
0
    }
12304
0
  }
12305
0
}
12306
12307
void
12308
nsIDocument::ScheduleIntersectionObserverNotification()
12309
0
{
12310
0
  if (mIntersectionObservers.IsEmpty()) {
12311
0
    return;
12312
0
  }
12313
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
12314
0
  nsCOMPtr<nsIRunnable> notification =
12315
0
    NewRunnableMethod("nsDocument::NotifyIntersectionObservers",
12316
0
                      this,
12317
0
                      &nsDocument::NotifyIntersectionObservers);
12318
0
  Dispatch(TaskCategory::Other, notification.forget());
12319
0
}
12320
12321
void
12322
nsIDocument::NotifyIntersectionObservers()
12323
0
{
12324
0
  nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count());
12325
0
  for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
12326
0
    DOMIntersectionObserver* observer = iter.Get()->GetKey();
12327
0
    observers.AppendElement(observer);
12328
0
  }
12329
0
  for (const auto& observer : observers) {
12330
0
    if (observer) {
12331
0
      observer->Notify();
12332
0
    }
12333
0
  }
12334
0
}
12335
12336
static bool
12337
NotifyLayerManagerRecreatedCallback(nsIDocument* aDocument, void* aData)
12338
0
{
12339
0
  aDocument->NotifyLayerManagerRecreated();
12340
0
  return true;
12341
0
}
12342
12343
void
12344
nsIDocument::NotifyLayerManagerRecreated()
12345
0
{
12346
0
  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
12347
0
  EnumerateSubDocuments(NotifyLayerManagerRecreatedCallback, nullptr);
12348
0
}
12349
12350
XPathEvaluator*
12351
nsIDocument::XPathEvaluator()
12352
0
{
12353
0
  if (!mXPathEvaluator) {
12354
0
    mXPathEvaluator.reset(new dom::XPathEvaluator(this));
12355
0
  }
12356
0
  return mXPathEvaluator.get();
12357
0
}
12358
12359
already_AddRefed<nsIDocumentEncoder>
12360
nsIDocument::GetCachedEncoder()
12361
0
{
12362
0
  return mCachedEncoder.forget();
12363
0
}
12364
12365
void
12366
nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)
12367
0
{
12368
0
  mCachedEncoder = aEncoder;
12369
0
}
12370
12371
void
12372
nsIDocument::SetContentTypeInternal(const nsACString& aType)
12373
0
{
12374
0
  if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None &&
12375
0
      aType.EqualsLiteral("application/xhtml+xml")) {
12376
0
    mDefaultElementType = kNameSpaceID_XHTML;
12377
0
  }
12378
0
12379
0
  mCachedEncoder = nullptr;
12380
0
  mContentType = aType;
12381
0
  mContentTypeForWriteCalls = aType;
12382
0
}
12383
12384
nsILoadContext*
12385
nsIDocument::GetLoadContext() const
12386
0
{
12387
0
  return mDocumentContainer;
12388
0
}
12389
12390
nsIDocShell*
12391
nsIDocument::GetDocShell() const
12392
0
{
12393
0
  return mDocumentContainer;
12394
0
}
12395
12396
void
12397
nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer)
12398
0
{
12399
0
  mStateObjectContainer = scContainer;
12400
0
  mStateObjectCached = nullptr;
12401
0
}
12402
12403
nsIDocument::DocumentTheme
12404
nsIDocument::GetDocumentLWTheme()
12405
0
{
12406
0
  if (mDocLWTheme == Doc_Theme_Uninitialized) {
12407
0
    mDocLWTheme = ThreadSafeGetDocumentLWTheme();
12408
0
  }
12409
0
  return mDocLWTheme;
12410
0
}
12411
12412
nsIDocument::DocumentTheme
12413
nsIDocument::ThreadSafeGetDocumentLWTheme() const
12414
0
{
12415
0
  if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) {
12416
0
    return Doc_Theme_None;
12417
0
  }
12418
0
12419
0
  if (mDocLWTheme != Doc_Theme_Uninitialized) {
12420
0
    return mDocLWTheme;
12421
0
  }
12422
0
12423
0
  DocumentTheme theme = Doc_Theme_None; // No lightweight theme by default
12424
0
  Element* element = GetRootElement();
12425
0
  if (element && element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::lwtheme,
12426
0
                                      nsGkAtoms::_true, eCaseMatters)) {
12427
0
    theme = Doc_Theme_Neutral;
12428
0
    nsAutoString lwTheme;
12429
0
    element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme);
12430
0
    if (lwTheme.EqualsLiteral("dark")) {
12431
0
      theme = Doc_Theme_Dark;
12432
0
    } else if (lwTheme.EqualsLiteral("bright")) {
12433
0
      theme = Doc_Theme_Bright;
12434
0
    }
12435
0
  }
12436
0
12437
0
  return theme;
12438
0
}
12439
12440
already_AddRefed<Element>
12441
nsIDocument::CreateHTMLElement(nsAtom* aTag)
12442
0
{
12443
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
12444
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
12445
0
                                           ELEMENT_NODE);
12446
0
  MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
12447
0
12448
0
  nsCOMPtr<Element> element;
12449
0
  DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
12450
0
                                             nodeInfo.forget(),
12451
0
                                             mozilla::dom::NOT_FROM_PARSER);
12452
0
12453
0
  MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
12454
0
  return element.forget();
12455
0
}
12456
12457
bool
12458
MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
12459
0
{
12460
0
  nsCOMArray<nsIDocument>* documents =
12461
0
    static_cast<nsCOMArray<nsIDocument>*>(aData);
12462
0
  if (aDoc) {
12463
0
    aDoc->SetIsInSyncOperation(true);
12464
0
    if (nsCOMPtr<nsPIDOMWindowInner> window = aDoc->GetInnerWindow()) {
12465
0
      window->TimeoutManager().BeginSyncOperation();
12466
0
    }
12467
0
    documents->AppendObject(aDoc);
12468
0
    aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
12469
0
  }
12470
0
  return true;
12471
0
}
12472
12473
nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
12474
0
{
12475
0
  mMicroTaskLevel = 0;
12476
0
  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
12477
0
  if (ccjs) {
12478
0
    mMicroTaskLevel = ccjs->MicroTaskLevel();
12479
0
    ccjs->SetMicroTaskLevel(0);
12480
0
  }
12481
0
  if (aDoc) {
12482
0
    if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
12483
0
      if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) {
12484
0
        nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
12485
0
        MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
12486
0
      }
12487
0
    }
12488
0
  }
12489
0
}
12490
12491
nsAutoSyncOperation::~nsAutoSyncOperation()
12492
0
{
12493
0
  for (int32_t i = 0; i < mDocuments.Count(); ++i) {
12494
0
    if (nsCOMPtr<nsPIDOMWindowInner> window = mDocuments[i]->GetInnerWindow()) {
12495
0
      window->TimeoutManager().EndSyncOperation();
12496
0
    }
12497
0
    mDocuments[i]->SetIsInSyncOperation(false);
12498
0
  }
12499
0
  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
12500
0
  if (ccjs) {
12501
0
    ccjs->SetMicroTaskLevel(mMicroTaskLevel);
12502
0
  }
12503
0
}
12504
12505
gfxUserFontSet*
12506
nsIDocument::GetUserFontSet(bool aFlushUserFontSet)
12507
0
{
12508
0
  // We want to initialize the user font set lazily the first time the
12509
0
  // user asks for it, rather than building it too early and forcing
12510
0
  // rule cascade creation.  Thus we try to enforce the invariant that
12511
0
  // we *never* build the user font set until the first call to
12512
0
  // GetUserFontSet.  However, once it's been requested, we can't wait
12513
0
  // for somebody to call GetUserFontSet in order to rebuild it (see
12514
0
  // comments below in MarkUserFontSetDirty for why).
12515
#ifdef DEBUG
12516
  bool userFontSetGottenBefore = mGetUserFontSetCalled;
12517
#endif
12518
  // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually
12519
0
  // flush.
12520
0
  mGetUserFontSetCalled = true;
12521
0
  if (mFontFaceSetDirty && aFlushUserFontSet) {
12522
0
    // If this assertion fails, and there have actually been changes to
12523
0
    // @font-face rules, then we will call StyleChangeReflow in
12524
0
    // FlushUserFontSet.  If we're in the middle of reflow,
12525
0
    // that's a bad thing to do, and the caller was responsible for
12526
0
    // flushing first.  If we're not (e.g., in frame construction), it's
12527
0
    // ok.
12528
0
    NS_ASSERTION(!userFontSetGottenBefore ||
12529
0
                 !GetShell() ||
12530
0
                 !GetShell()->IsReflowLocked(),
12531
0
                 "FlushUserFontSet should have been called first");
12532
0
    FlushUserFontSet();
12533
0
  }
12534
0
12535
0
  if (!mFontFaceSet) {
12536
0
    return nullptr;
12537
0
  }
12538
0
12539
0
  return mFontFaceSet->GetUserFontSet();
12540
0
}
12541
12542
void
12543
nsIDocument::FlushUserFontSet()
12544
0
{
12545
0
  if (!mGetUserFontSetCalled) {
12546
0
    return; // No one cares about this font set yet, but we want to be careful
12547
0
            // to not unset our mFontFaceSetDirty bit, so when someone really
12548
0
            // does we'll create it.
12549
0
  }
12550
0
12551
0
  if (!mFontFaceSetDirty) {
12552
0
    return;
12553
0
  }
12554
0
12555
0
  mFontFaceSetDirty = false;
12556
0
12557
0
  if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
12558
0
    nsTArray<nsFontFaceRuleContainer> rules;
12559
0
    nsIPresShell* shell = GetShell();
12560
0
    if (shell && !shell->StyleSet()->AppendFontFaceRules(rules)) {
12561
0
      return;
12562
0
    }
12563
0
12564
0
12565
0
    if (!mFontFaceSet && !rules.IsEmpty()) {
12566
0
      nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
12567
0
      mFontFaceSet = new FontFaceSet(window, this);
12568
0
    }
12569
0
12570
0
    bool changed = false;
12571
0
    if (mFontFaceSet) {
12572
0
      changed = mFontFaceSet->UpdateRules(rules);
12573
0
    }
12574
0
12575
0
    // We need to enqueue a style change reflow (for later) to
12576
0
    // reflect that we're modifying @font-face rules.  (However,
12577
0
    // without a reflow, nothing will happen to start any downloads
12578
0
    // that are needed.)
12579
0
    if (changed && shell) {
12580
0
      if (nsPresContext* presContext = shell->GetPresContext()) {
12581
0
        presContext->UserFontSetUpdated();
12582
0
      }
12583
0
    }
12584
0
  }
12585
0
}
12586
12587
void
12588
nsIDocument::MarkUserFontSetDirty()
12589
0
{
12590
0
  if (!mGetUserFontSetCalled) {
12591
0
    // We want to lazily build the user font set the first time it's
12592
0
    // requested (so we don't force creation of rule cascades too
12593
0
    // early), so don't do anything now.
12594
0
    return;
12595
0
  }
12596
0
12597
0
  mFontFaceSetDirty = true;
12598
0
}
12599
12600
FontFaceSet*
12601
nsIDocument::Fonts()
12602
0
{
12603
0
  if (!mFontFaceSet) {
12604
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
12605
0
    mFontFaceSet = new FontFaceSet(window, this);
12606
0
    GetUserFontSet();  // this will cause the user font set to be created/updated
12607
0
  }
12608
0
  return mFontFaceSet;
12609
0
}
12610
12611
void
12612
nsIDocument::ReportHasScrollLinkedEffect()
12613
0
{
12614
0
  if (mHasScrollLinkedEffect) {
12615
0
    // We already did this once for this document, don't do it again.
12616
0
    return;
12617
0
  }
12618
0
  mHasScrollLinkedEffect = true;
12619
0
  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
12620
0
                                  NS_LITERAL_CSTRING("Async Pan/Zoom"),
12621
0
                                  this, nsContentUtils::eLAYOUT_PROPERTIES,
12622
0
                                  "ScrollLinkedEffectFound2");
12623
0
}
12624
12625
void
12626
nsIDocument::SetUserHasInteracted()
12627
0
{
12628
0
  MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
12629
0
          ("Document %p has been interacted by user.", this));
12630
0
12631
0
  // We maybe need to update the user-interaction permission.
12632
0
  MaybeStoreUserInteractionAsPermission();
12633
0
12634
0
  if (mUserHasInteracted) {
12635
0
    return;
12636
0
  }
12637
0
12638
0
  mUserHasInteracted = true;
12639
0
12640
0
  nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->GetLoadInfo() : nullptr;
12641
0
  if (loadInfo) {
12642
0
    loadInfo->SetDocumentHasUserInteracted(true);
12643
0
  }
12644
0
12645
0
  MaybeAllowStorageForOpener();
12646
0
}
12647
12648
void
12649
nsIDocument::NotifyUserGestureActivation()
12650
0
{
12651
0
  // Activate this document and all documents up to the top level
12652
0
  // content document.
12653
0
  nsIDocument* doc = this;
12654
0
  while (doc && !doc->mUserGestureActivated) {
12655
0
    MOZ_LOG(gUserInteractionPRLog,
12656
0
            LogLevel::Debug,
12657
0
            ("Document %p has been activated by user.", this));
12658
0
    doc->mUserGestureActivated = true;
12659
0
    doc = doc->GetSameTypeParentDocument();
12660
0
  }
12661
0
}
12662
12663
void
12664
nsIDocument::SetDocTreeHadAudibleMedia()
12665
0
{
12666
0
  nsIDocument* topLevelDoc = GetTopLevelContentDocument();
12667
0
  if (!topLevelDoc) {
12668
0
    return;
12669
0
  }
12670
0
12671
0
  if (!topLevelDoc->mDocTreeHadAudibleMedia) {
12672
0
    RefPtr<AsyncEventDispatcher> asyncDispatcher =
12673
0
      new AsyncEventDispatcher(topLevelDoc,
12674
0
                               NS_LITERAL_STRING("AudibleAutoplayMediaOccurred"),
12675
0
                               CanBubble::eYes,
12676
0
                               ChromeOnlyDispatch::eYes);
12677
0
    asyncDispatcher->PostDOMEvent();
12678
0
  }
12679
0
  topLevelDoc->mDocTreeHadAudibleMedia = true;
12680
0
}
12681
12682
void
12683
nsIDocument::SetDocTreeHadPlayRevoked()
12684
0
{
12685
0
  nsIDocument* topLevelDoc = GetTopLevelContentDocument();
12686
0
  if (topLevelDoc) {
12687
0
    topLevelDoc->mDocTreeHadPlayRevoked = true;
12688
0
  }
12689
0
}
12690
12691
void
12692
nsIDocument::MaybeAllowStorageForOpener()
12693
0
{
12694
0
  if (StaticPrefs::network_cookie_cookieBehavior() !=
12695
0
        nsICookieService::BEHAVIOR_REJECT_TRACKER ||
12696
0
      !AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions()) {
12697
0
    return;
12698
0
  }
12699
0
12700
0
  // This will probably change for project fission, but currently this document
12701
0
  // and the opener are on the same process. In the future, we should make this
12702
0
  // part async.
12703
0
12704
0
  nsPIDOMWindowInner* inner = GetInnerWindow();
12705
0
  if (NS_WARN_IF(!inner)) {
12706
0
    return;
12707
0
  }
12708
0
12709
0
  nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
12710
0
  if (NS_WARN_IF(!outer)) {
12711
0
    return;
12712
0
  }
12713
0
12714
0
  nsCOMPtr<nsPIDOMWindowOuter> outerOpener = outer->GetOpener();
12715
0
  if (!outerOpener) {
12716
0
    return;
12717
0
  }
12718
0
12719
0
  nsPIDOMWindowInner* openerInner = outerOpener->GetCurrentInnerWindow();
12720
0
  if (NS_WARN_IF(!openerInner)) {
12721
0
    return;
12722
0
  }
12723
0
12724
0
  // Let's take the principal from the opener.
12725
0
  nsIDocument* openerDocument = openerInner->GetExtantDoc();
12726
0
  if (NS_WARN_IF(!openerDocument)) {
12727
0
    return;
12728
0
  }
12729
0
12730
0
  nsCOMPtr<nsIURI> openerURI = openerDocument->GetDocumentURI();
12731
0
  if (NS_WARN_IF(!openerURI)) {
12732
0
    return;
12733
0
  }
12734
0
12735
0
  // No tracking resource.
12736
0
  if (!nsContentUtils::IsTrackingResourceWindow(inner)) {
12737
0
    return;
12738
0
  }
12739
0
12740
0
  // If the opener is not a 3rd party and if this window is not a 3rd party, we
12741
0
  // should not continue.
12742
0
  if (!nsContentUtils::IsThirdPartyWindowOrChannel(inner, nullptr, openerURI) &&
12743
0
      !nsContentUtils::IsThirdPartyWindowOrChannel(openerInner, nullptr,
12744
0
                                                   nullptr)) {
12745
0
    return;
12746
0
  }
12747
0
12748
0
  nsCOMPtr<nsIURI> uri = GetDocumentURI();
12749
0
  if (NS_WARN_IF(!uri)) {
12750
0
    return;
12751
0
  }
12752
0
12753
0
  nsAutoString origin;
12754
0
  nsresult rv = nsContentUtils::GetUTFOrigin(uri, origin);
12755
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
12756
0
    return;
12757
0
  }
12758
0
12759
0
  // We don't care when the asynchronous work finishes here.
12760
0
  Unused << AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(origin,
12761
0
                                                                     openerInner,
12762
0
                                                                     AntiTrackingCommon::eHeuristic);
12763
0
}
12764
12765
namespace {
12766
12767
// Documents can stay alive for days. We don't want to update the permission
12768
// value at any user-interaction, and, using a timer triggered any X seconds
12769
// should be good enough. 'X' is taken from
12770
// privacy.userInteraction.document.interval pref.
12771
//  We also want to store the user-interaction before shutting down, and, for
12772
//  this reason, this class implements nsIAsyncShutdownBlocker interface.
12773
class UserIntractionTimer final : public Runnable
12774
                                , public nsITimerCallback
12775
                                , public nsIAsyncShutdownBlocker
12776
{
12777
public:
12778
  NS_DECL_ISUPPORTS_INHERITED
12779
12780
  explicit UserIntractionTimer(nsIDocument* aDocument)
12781
    : Runnable("UserIntractionTimer")
12782
    , mPrincipal(aDocument->NodePrincipal())
12783
    , mDocument(do_GetWeakReference(aDocument))
12784
0
  {
12785
0
    static int32_t userInteractionTimerId = 0;
12786
0
    // Blocker names must be unique. Let's create it now because when needed,
12787
0
    // the document could be already gone.
12788
0
    mBlockerName.AppendPrintf("UserInteractionTimer %d for document %p",
12789
0
                              ++userInteractionTimerId, aDocument);
12790
0
  }
12791
12792
  // Runnable interface
12793
12794
  NS_IMETHOD
12795
  Run() override
12796
0
  {
12797
0
    uint32_t interval =
12798
0
      StaticPrefs::privacy_userInteraction_document_interval();
12799
0
    if (!interval) {
12800
0
      return NS_OK;
12801
0
    }
12802
0
12803
0
    RefPtr<UserIntractionTimer> self = this;
12804
0
    auto raii = MakeScopeExit([self] {
12805
0
      self->CancelTimerAndStoreUserInteraction();
12806
0
    });
12807
0
12808
0
    nsresult rv = NS_NewTimerWithCallback(getter_AddRefs(mTimer),
12809
0
                                          this, interval * 1000,
12810
0
                                          nsITimer::TYPE_ONE_SHOT,
12811
0
                                          SystemGroup::EventTargetFor(TaskCategory::Other));
12812
0
    NS_ENSURE_SUCCESS(rv, NS_OK);
12813
0
12814
0
    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
12815
0
    NS_ENSURE_TRUE(!!phase, NS_OK);
12816
0
12817
0
    rv = phase->AddBlocker(this, NS_LITERAL_STRING(__FILE__), __LINE__,
12818
0
                           NS_LITERAL_STRING("UserIntractionTimer shutdown"));
12819
0
    NS_ENSURE_SUCCESS(rv, NS_OK);
12820
0
12821
0
    raii.release();
12822
0
    return NS_OK;
12823
0
  }
12824
12825
  // nsITimerCallback interface
12826
12827
  NS_IMETHOD
12828
  Notify(nsITimer* aTimer) override
12829
0
  {
12830
0
    StoreUserInteraction();
12831
0
    return NS_OK;
12832
0
  }
12833
12834
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
12835
  using nsINamed::GetName;
12836
#endif
12837
12838
  // nsIAsyncShutdownBlocker interface
12839
12840
  NS_IMETHOD
12841
  GetName(nsAString& aName) override
12842
0
  {
12843
0
    aName = mBlockerName;
12844
0
    return NS_OK;
12845
0
  }
12846
12847
  NS_IMETHOD
12848
  BlockShutdown(nsIAsyncShutdownClient* aClient) override
12849
0
  {
12850
0
    CancelTimerAndStoreUserInteraction();
12851
0
    return NS_OK;
12852
0
  }
12853
12854
  NS_IMETHOD
12855
  GetState(nsIPropertyBag**) override
12856
0
  {
12857
0
    return NS_OK;
12858
0
  }
12859
12860
private:
12861
0
  ~UserIntractionTimer() = default;
12862
12863
  void
12864
  StoreUserInteraction()
12865
0
  {
12866
0
    // Remove the shutting down blocker
12867
0
    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
12868
0
    if (phase) {
12869
0
      phase->RemoveBlocker(this);
12870
0
    }
12871
0
12872
0
    // If the document is not gone, let's reset its timer flag.
12873
0
    nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocument);
12874
0
    if (document) {
12875
0
      AntiTrackingCommon::StoreUserInteractionFor(mPrincipal);
12876
0
      document->ResetUserInteractionTimer();
12877
0
    }
12878
0
  }
12879
12880
  void
12881
  CancelTimerAndStoreUserInteraction()
12882
0
  {
12883
0
    if (mTimer) {
12884
0
      mTimer->Cancel();
12885
0
      mTimer = nullptr;
12886
0
    }
12887
0
12888
0
    StoreUserInteraction();
12889
0
  }
12890
12891
  static already_AddRefed<nsIAsyncShutdownClient>
12892
  GetShutdownPhase()
12893
0
  {
12894
0
    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
12895
0
    NS_ENSURE_TRUE(!!svc, nullptr);
12896
0
12897
0
    nsCOMPtr<nsIAsyncShutdownClient> phase;
12898
0
    nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase));
12899
0
    NS_ENSURE_SUCCESS(rv, nullptr);
12900
0
12901
0
    return phase.forget();
12902
0
  }
12903
12904
  nsCOMPtr<nsIPrincipal> mPrincipal;
12905
  nsWeakPtr mDocument;
12906
12907
  nsCOMPtr<nsITimer> mTimer;
12908
12909
  nsString mBlockerName;
12910
};
12911
12912
NS_IMPL_ISUPPORTS_INHERITED(UserIntractionTimer, Runnable, nsITimerCallback,
12913
                            nsIAsyncShutdownBlocker)
12914
12915
} // anonymous
12916
12917
void
12918
nsIDocument::MaybeStoreUserInteractionAsPermission()
12919
0
{
12920
0
  // We care about user-interaction stored only for top-level documents.
12921
0
  if (GetSameTypeParentDocument()) {
12922
0
    return;
12923
0
  }
12924
0
12925
0
  if (!mUserHasInteracted) {
12926
0
    // First interaction, let's store this info now.
12927
0
    AntiTrackingCommon::StoreUserInteractionFor(NodePrincipal());
12928
0
    return;
12929
0
  }
12930
0
12931
0
  if (mHasUserInteractionTimerScheduled) {
12932
0
    return;
12933
0
  }
12934
0
12935
0
  nsCOMPtr<nsIRunnable> task = new UserIntractionTimer(this);
12936
0
  nsresult rv = NS_IdleDispatchToCurrentThread(task.forget(), 2500);
12937
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
12938
0
    return;
12939
0
  }
12940
0
12941
0
  // This value will be reset by the timer.
12942
0
  mHasUserInteractionTimerScheduled = true;
12943
0
}
12944
12945
void
12946
nsIDocument::ResetUserInteractionTimer()
12947
0
{
12948
0
  mHasUserInteractionTimerScheduled = false;
12949
0
}
12950
12951
bool
12952
nsIDocument::HasBeenUserGestureActivated()
12953
0
{
12954
0
  if (mUserGestureActivated) {
12955
0
    return true;
12956
0
  }
12957
0
12958
0
  // If any ancestor document is activated, so are we.
12959
0
  nsIDocument* doc = GetSameTypeParentDocument();
12960
0
  while (doc) {
12961
0
    if (doc->mUserGestureActivated) {
12962
0
      // An ancestor is also activated. Record activation on the unactivated
12963
0
      // sub-branch to speed up future queries.
12964
0
      NotifyUserGestureActivation();
12965
0
      break;
12966
0
    }
12967
0
    doc = doc->GetSameTypeParentDocument();
12968
0
  }
12969
0
12970
0
  return mUserGestureActivated;
12971
0
}
12972
12973
bool
12974
nsIDocument::IsExtensionPage() const
12975
0
{
12976
0
  return  Preferences::GetBool("media.autoplay.allow-extension-background-pages", true) &&
12977
0
          BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
12978
0
}
12979
12980
nsIDocument*
12981
nsIDocument::GetSameTypeParentDocument()
12982
0
{
12983
0
  nsCOMPtr<nsIDocShellTreeItem> current = GetDocShell();
12984
0
  if (!current) {
12985
0
    return nullptr;
12986
0
  }
12987
0
12988
0
  nsCOMPtr<nsIDocShellTreeItem> parent;
12989
0
  current->GetSameTypeParent(getter_AddRefs(parent));
12990
0
  if (!parent) {
12991
0
    return nullptr;
12992
0
  }
12993
0
12994
0
  return parent->GetDocument();
12995
0
}
12996
12997
/**
12998
 * Retrieves the classification of the Flash plugins in the document based on
12999
 * the classification lists. We perform AsyncInitFlashClassification on
13000
 * StartDocumentLoad() and the result may not be initialized when this function
13001
 * gets called. In that case, We can only unfortunately have a blocking wait.
13002
 *
13003
 * For more information, see
13004
 * toolkit/components/url-classifier/flash-block-lists.rst
13005
 */
13006
FlashClassification
13007
nsIDocument::PrincipalFlashClassification()
13008
0
{
13009
0
  MOZ_ASSERT(mPrincipalFlashClassifier);
13010
0
  return mPrincipalFlashClassifier->ClassifyMaybeSync(NodePrincipal(),
13011
0
                                                      IsThirdParty());
13012
0
}
13013
13014
/**
13015
 * Helper function for |nsDocument::PrincipalFlashClassification|
13016
 *
13017
 * Adds a table name string to a table list (a comma separated string). The
13018
 * table will not be added if the name is an empty string.
13019
 */
13020
static void
13021
MaybeAddTableToTableList(const nsACString& aTableNames,
13022
                         nsACString& aTableList)
13023
0
{
13024
0
  if (aTableNames.IsEmpty()) {
13025
0
    return;
13026
0
  }
13027
0
  if (!aTableList.IsEmpty()) {
13028
0
    aTableList.AppendLiteral(",");
13029
0
  }
13030
0
  aTableList.Append(aTableNames);
13031
0
}
13032
13033
/**
13034
 * Helper function for |nsDocument::PrincipalFlashClassification|
13035
 *
13036
 * Takes an array of table names and a comma separated list of table names
13037
 * Returns |true| if any table name in the array matches a table name in the
13038
 * comma separated list.
13039
 */
13040
static bool
13041
ArrayContainsTable(const nsTArray<nsCString>& aTableArray,
13042
                   const nsACString& aTableNames)
13043
0
{
13044
0
  for (const nsCString& table : aTableArray) {
13045
0
    // This check is sufficient because table names cannot contain commas and
13046
0
    // cannot contain another existing table name.
13047
0
    if (FindInReadable(table, aTableNames)) {
13048
0
      return true;
13049
0
    }
13050
0
  }
13051
0
  return false;
13052
0
}
13053
13054
namespace {
13055
13056
static const char* gCallbackPrefs[] = {
13057
  // We only need to register string-typed preferences.
13058
  "urlclassifier.flashAllowTable",
13059
  "urlclassifier.flashAllowExceptTable",
13060
  "urlclassifier.flashTable",
13061
  "urlclassifier.flashExceptTable",
13062
  "urlclassifier.flashSubDocTable",
13063
  "urlclassifier.flashSubDocExceptTable",
13064
  nullptr,
13065
};
13066
13067
// An object to store all preferences we need for flash blocking feature.
13068
struct PrefStore
13069
{
13070
  PrefStore()
13071
    : mFlashBlockEnabled(false)
13072
    , mPluginsHttpOnly(false)
13073
0
  {
13074
0
    Preferences::AddBoolVarCache(&mFlashBlockEnabled,
13075
0
                                 "plugins.flashBlock.enabled");
13076
0
    Preferences::AddBoolVarCache(&mPluginsHttpOnly,
13077
0
                                 "plugins.http_https_only");
13078
0
13079
0
    Preferences::RegisterCallbacks(
13080
0
      PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs),
13081
0
      gCallbackPrefs, this);
13082
0
13083
0
    UpdateStringPrefs();
13084
0
  }
13085
13086
  ~PrefStore()
13087
0
  {
13088
0
    Preferences::UnregisterCallbacks(
13089
0
      PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs),
13090
0
      gCallbackPrefs, this);
13091
0
  }
13092
13093
  void UpdateStringPrefs(const char* aPref = nullptr)
13094
0
  {
13095
0
    Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables);
13096
0
    Preferences::GetCString("urlclassifier.flashAllowExceptTable", mAllowExceptionsTables);
13097
0
    Preferences::GetCString("urlclassifier.flashTable", mDenyTables);
13098
0
    Preferences::GetCString("urlclassifier.flashExceptTable", mDenyExceptionsTables);
13099
0
    Preferences::GetCString("urlclassifier.flashSubDocTable", mSubDocDenyTables);
13100
0
    Preferences::GetCString("urlclassifier.flashSubDocExceptTable", mSubDocDenyExceptionsTables);
13101
0
  }
13102
13103
  bool mFlashBlockEnabled;
13104
  bool mPluginsHttpOnly;
13105
13106
  nsCString mAllowTables;
13107
  nsCString mAllowExceptionsTables;
13108
  nsCString mDenyTables;
13109
  nsCString mDenyExceptionsTables;
13110
  nsCString mSubDocDenyTables;
13111
  nsCString mSubDocDenyExceptionsTables;
13112
};
13113
13114
static const
13115
PrefStore& GetPrefStore()
13116
0
{
13117
0
  static UniquePtr<PrefStore> sPrefStore;
13118
0
  if (!sPrefStore) {
13119
0
    sPrefStore.reset(new PrefStore());
13120
0
    ClearOnShutdown(&sPrefStore);
13121
0
  }
13122
0
  return *sPrefStore;
13123
0
}
13124
13125
} // end of unnamed namespace.
13126
13127
////////////////////////////////////////////////////////////////////
13128
// PrincipalFlashClassifier implementation.
13129
13130
NS_IMPL_ISUPPORTS(PrincipalFlashClassifier, nsIURIClassifierCallback)
13131
13132
PrincipalFlashClassifier::PrincipalFlashClassifier()
13133
0
{
13134
0
  Reset();
13135
0
}
13136
13137
void
13138
PrincipalFlashClassifier::Reset()
13139
0
{
13140
0
  mAsyncClassified = false;
13141
0
  mMatchedTables.Clear();
13142
0
  mResult = FlashClassification::Unclassified;
13143
0
}
13144
13145
void
13146
PrincipalFlashClassifier::GetClassificationTables(bool aIsThirdParty,
13147
                                                  nsACString& aTables)
13148
0
{
13149
0
  aTables.Truncate();
13150
0
  auto& prefs = GetPrefStore();
13151
0
13152
0
  MaybeAddTableToTableList(prefs.mAllowTables, aTables);
13153
0
  MaybeAddTableToTableList(prefs.mAllowExceptionsTables, aTables);
13154
0
  MaybeAddTableToTableList(prefs.mDenyTables, aTables);
13155
0
  MaybeAddTableToTableList(prefs.mDenyExceptionsTables, aTables);
13156
0
13157
0
  if (aIsThirdParty) {
13158
0
    MaybeAddTableToTableList(prefs.mSubDocDenyTables, aTables);
13159
0
    MaybeAddTableToTableList(prefs.mSubDocDenyExceptionsTables, aTables);
13160
0
  }
13161
0
}
13162
13163
bool
13164
PrincipalFlashClassifier::EnsureUriClassifier()
13165
0
{
13166
0
  if (!mUriClassifier) {
13167
0
    mUriClassifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
13168
0
  }
13169
0
13170
0
  return !!mUriClassifier;
13171
0
}
13172
13173
FlashClassification
13174
PrincipalFlashClassifier::ClassifyMaybeSync(nsIPrincipal* aPrincipal, bool aIsThirdParty)
13175
0
{
13176
0
  if (FlashClassification::Unclassified != mResult) {
13177
0
    // We already have the result. Just return it.
13178
0
    return mResult;
13179
0
  }
13180
0
13181
0
  // TODO: Bug 1342333 - Entirely remove the use of the sync API
13182
0
  // (ClassifyLocalWithTables).
13183
0
  if (!mAsyncClassified) {
13184
0
13185
0
    //
13186
0
    // We may
13187
0
    //   1) have called AsyncClassifyLocalWithTables but OnClassifyComplete
13188
0
    //      hasn't been called.
13189
0
    //   2) haven't even called AsyncClassifyLocalWithTables.
13190
0
    //
13191
0
    // In both cases we need to do the synchronous classification as the fallback.
13192
0
    //
13193
0
13194
0
    if (!EnsureUriClassifier()) {
13195
0
      return FlashClassification::Denied;
13196
0
    }
13197
0
    mResult = CheckIfClassifyNeeded(aPrincipal);
13198
0
    if (FlashClassification::Unclassified != mResult) {
13199
0
      return mResult;
13200
0
    }
13201
0
13202
0
    nsresult rv;
13203
0
    nsAutoCString classificationTables;
13204
0
    GetClassificationTables(aIsThirdParty, classificationTables);
13205
0
13206
0
    if (!mClassificationURI) {
13207
0
      rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
13208
0
      if (NS_FAILED(rv) || !mClassificationURI) {
13209
0
        mResult = FlashClassification::Denied;
13210
0
        return mResult;
13211
0
      }
13212
0
    }
13213
0
13214
0
    rv = mUriClassifier->ClassifyLocalWithTables(mClassificationURI,
13215
0
                                                 classificationTables,
13216
0
                                                 mMatchedTables);
13217
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
13218
0
      if (rv == NS_ERROR_MALFORMED_URI) {
13219
0
        // This means that the URI had no hostname (ex: file://doc.html). In this
13220
0
        // case, we allow the default (Unknown plugin) behavior.
13221
0
        mResult = FlashClassification::Unknown;
13222
0
      } else {
13223
0
        mResult = FlashClassification::Denied;
13224
0
      }
13225
0
      return mResult;
13226
0
    }
13227
0
  }
13228
0
13229
0
  // Resolve the result based on mMatchedTables and aIsThirdParty.
13230
0
  mResult = Resolve(aIsThirdParty);
13231
0
  MOZ_ASSERT(FlashClassification::Unclassified != mResult);
13232
0
13233
0
  // The subsequent call of Result() will return the resolved result
13234
0
  // and never reach here until Reset() is called.
13235
0
  return mResult;
13236
0
}
13237
13238
/*virtual*/ nsresult
13239
PrincipalFlashClassifier::OnClassifyComplete(nsresult /*aErrorCode*/,
13240
                                             const nsACString& aLists, // Only this matters.
13241
                                             const nsACString& /*aProvider*/,
13242
                                             const nsACString& /*aPrefix*/)
13243
0
{
13244
0
  mAsyncClassified = true;
13245
0
13246
0
  if (FlashClassification::Unclassified != mResult) {
13247
0
    // Result() has been called prior to this callback.
13248
0
    return NS_OK;
13249
0
  }
13250
0
13251
0
  // TODO: Bug 1364804 - We should use a callback type which notifies
13252
0
  // the result as a string array rather than a formatted string.
13253
0
13254
0
  // We only populate the matched list without resolving the classification
13255
0
  // result because we are not sure if the parent doc has been properly set.
13256
0
  // We also parse the comma-separated tables to array. (the code is copied
13257
0
  // from Classifier::SplitTables.)
13258
0
  nsACString::const_iterator begin, iter, end;
13259
0
  aLists.BeginReading(begin);
13260
0
  aLists.EndReading(end);
13261
0
  while (begin != end) {
13262
0
    iter = begin;
13263
0
    FindCharInReadable(',', iter, end);
13264
0
    nsDependentCSubstring table = Substring(begin,iter);
13265
0
    if (!table.IsEmpty()) {
13266
0
      mMatchedTables.AppendElement(Substring(begin, iter));
13267
0
    }
13268
0
    begin = iter;
13269
0
    if (begin != end) {
13270
0
      begin++;
13271
0
    }
13272
0
  }
13273
0
13274
0
  return NS_OK;
13275
0
}
13276
13277
// We resolve the classification result based on aIsThirdParty
13278
// and the matched tables we got ealier on (via either sync or async API).
13279
FlashClassification
13280
PrincipalFlashClassifier::Resolve(bool aIsThirdParty)
13281
0
{
13282
0
  MOZ_ASSERT(FlashClassification::Unclassified == mResult,
13283
0
             "We already have resolved classification result.");
13284
0
13285
0
  if (mMatchedTables.IsEmpty()) {
13286
0
    return FlashClassification::Unknown;
13287
0
  }
13288
0
13289
0
  auto& prefs = GetPrefStore();
13290
0
  if (ArrayContainsTable(mMatchedTables, prefs.mDenyTables) &&
13291
0
      !ArrayContainsTable(mMatchedTables, prefs.mDenyExceptionsTables)) {
13292
0
    return FlashClassification::Denied;
13293
0
  } else if (ArrayContainsTable(mMatchedTables, prefs.mAllowTables) &&
13294
0
             !ArrayContainsTable(mMatchedTables, prefs.mAllowExceptionsTables)) {
13295
0
    return FlashClassification::Allowed;
13296
0
  }
13297
0
13298
0
  if (aIsThirdParty && ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyTables) &&
13299
0
      !ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyExceptionsTables)) {
13300
0
    return FlashClassification::Denied;
13301
0
  }
13302
0
13303
0
  return FlashClassification::Unknown;
13304
0
}
13305
13306
void
13307
PrincipalFlashClassifier::AsyncClassify(nsIPrincipal* aPrincipal)
13308
0
{
13309
0
  MOZ_ASSERT(FlashClassification::Unclassified == mResult,
13310
0
             "The old classification result should be reset first.");
13311
0
  Reset();
13312
0
  mResult = AsyncClassifyInternal(aPrincipal);
13313
0
}
13314
13315
FlashClassification
13316
PrincipalFlashClassifier::CheckIfClassifyNeeded(nsIPrincipal* aPrincipal)
13317
0
{
13318
0
  nsresult rv;
13319
0
13320
0
  auto& prefs = GetPrefStore();
13321
0
13322
0
  // If neither pref is on, skip the null-principal and principal URI checks.
13323
0
  if (prefs.mPluginsHttpOnly && !prefs.mFlashBlockEnabled) {
13324
0
   return FlashClassification::Unknown;
13325
0
  }
13326
0
13327
0
  nsCOMPtr<nsIPrincipal> principal = aPrincipal;
13328
0
  if (principal->GetIsNullPrincipal()) {
13329
0
    return FlashClassification::Denied;
13330
0
  }
13331
0
13332
0
  nsCOMPtr<nsIURI> classificationURI;
13333
0
  rv = principal->GetURI(getter_AddRefs(classificationURI));
13334
0
  if (NS_FAILED(rv) || !classificationURI) {
13335
0
    return FlashClassification::Denied;
13336
0
  }
13337
0
13338
0
  if (prefs.mPluginsHttpOnly) {
13339
0
    // Only allow plugins for documents from an HTTP/HTTPS origin. This should
13340
0
    // allow dependent data: URIs to load plugins, but not:
13341
0
    // * chrome documents
13342
0
    // * "bare" data: loads
13343
0
    // * FTP/gopher/file
13344
0
    nsAutoCString scheme;
13345
0
    rv = classificationURI->GetScheme(scheme);
13346
0
    if (NS_WARN_IF(NS_FAILED(rv)) ||
13347
0
        !(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))) {
13348
0
      return FlashClassification::Denied;
13349
0
    }
13350
0
  }
13351
0
13352
0
  // If flash blocking is disabled, it is equivalent to all sites being
13353
0
  // on neither list.
13354
0
  if (!prefs.mFlashBlockEnabled) {
13355
0
    return FlashClassification::Unknown;
13356
0
  }
13357
0
13358
0
  return FlashClassification::Unclassified;
13359
0
}
13360
13361
// Using nsIURIClassifier.asyncClassifyLocalWithTables to do classification
13362
// against the flash related tables based on the given principal.
13363
FlashClassification
13364
PrincipalFlashClassifier::AsyncClassifyInternal(nsIPrincipal* aPrincipal)
13365
0
{
13366
0
  auto result = CheckIfClassifyNeeded(aPrincipal);
13367
0
  if (FlashClassification::Unclassified != result) {
13368
0
    return result;
13369
0
  }
13370
0
13371
0
  // We haven't been able to decide if it's a third party document
13372
0
  // since determining if a document is third-party may depend on its
13373
0
  // parent document. At the time we call AsyncClassifyInternal
13374
0
  // (i.e. StartDocumentLoad) the parent document may not have been
13375
0
  // set. As a result, we wait until Resolve() to be called to
13376
0
  // take "is third party" into account. At this point, we just assume
13377
0
  // it's third-party to include every list.
13378
0
  nsAutoCString tables;
13379
0
  GetClassificationTables(true, tables);
13380
0
13381
0
  if (tables.IsEmpty()) {
13382
0
    return FlashClassification::Unknown;
13383
0
  }
13384
0
13385
0
  if (!EnsureUriClassifier()) {
13386
0
    return FlashClassification::Denied;
13387
0
  }
13388
0
13389
0
  nsresult rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
13390
0
  if (NS_FAILED(rv) || !mClassificationURI) {
13391
0
    return FlashClassification::Denied;
13392
0
  }
13393
0
13394
0
  // We don't support extra entries by pref for this classifier.
13395
0
  rv = mUriClassifier->AsyncClassifyLocalWithTables(mClassificationURI,
13396
0
                                                    tables,
13397
0
                                                    nsTArray<nsCString>(),
13398
0
                                                    nsTArray<nsCString>(),
13399
0
                                                    this);
13400
0
13401
0
  if (NS_FAILED(rv)) {
13402
0
    if (rv == NS_ERROR_MALFORMED_URI) {
13403
0
      // This means that the URI had no hostname (ex: file://doc.html). In this
13404
0
      // case, we allow the default (Unknown plugin) behavior.
13405
0
      return FlashClassification::Unknown;
13406
0
    } else {
13407
0
      return FlashClassification::Denied;
13408
0
    }
13409
0
  }
13410
0
13411
0
  return FlashClassification::Unclassified;
13412
0
}
13413
13414
FlashClassification
13415
nsIDocument::ComputeFlashClassification()
13416
0
{
13417
0
  nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell();
13418
0
  if (!current) {
13419
0
    return FlashClassification::Denied;
13420
0
  }
13421
0
  nsCOMPtr<nsIDocShellTreeItem> parent;
13422
0
  DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent));
13423
0
  MOZ_ASSERT(NS_SUCCEEDED(rv),
13424
0
             "nsIDocShellTreeItem::GetSameTypeParent should never fail");
13425
0
13426
0
  bool isTopLevel = !parent;
13427
0
  FlashClassification classification;
13428
0
  if (isTopLevel) {
13429
0
    classification = PrincipalFlashClassification();
13430
0
  } else {
13431
0
    nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
13432
0
    if (!parentDocument) {
13433
0
      return FlashClassification::Denied;
13434
0
    }
13435
0
    FlashClassification parentClassification =
13436
0
      parentDocument->DocumentFlashClassification();
13437
0
13438
0
    if (parentClassification == FlashClassification::Denied) {
13439
0
      classification = FlashClassification::Denied;
13440
0
    } else {
13441
0
      classification = PrincipalFlashClassification();
13442
0
13443
0
      // Allow unknown children to inherit allowed status from parent, but
13444
0
      // do not allow denied children to do so.
13445
0
      if (classification == FlashClassification::Unknown &&
13446
0
          parentClassification == FlashClassification::Allowed) {
13447
0
        classification = FlashClassification::Allowed;
13448
0
      }
13449
0
    }
13450
0
  }
13451
0
13452
0
  return classification;
13453
0
}
13454
13455
/**
13456
 * Retrieves the classification of plugins in this document. This is dependent
13457
 * on the classification of this document and all parent documents.
13458
 * This function is infallible - It must return some classification that
13459
 * callers can act on.
13460
 *
13461
 * This function will NOT return FlashClassification::Unclassified
13462
 */
13463
FlashClassification
13464
nsIDocument::DocumentFlashClassification()
13465
0
{
13466
0
  if (mFlashClassification == FlashClassification::Unclassified) {
13467
0
    FlashClassification result = ComputeFlashClassification();
13468
0
    mFlashClassification = result;
13469
0
    MOZ_ASSERT(result != FlashClassification::Unclassified,
13470
0
      "nsDocument::GetPluginClassification should never return Unclassified");
13471
0
  }
13472
0
13473
0
  return mFlashClassification;
13474
0
}
13475
13476
/**
13477
 * Initializes |mIsThirdParty| if necessary and returns its value. The value
13478
 * returned represents whether this document should be considered Third-Party.
13479
 *
13480
 * A top-level document cannot be a considered Third-Party; only subdocuments
13481
 * may. For a subdocument to be considered Third-Party, it must meet ANY ONE
13482
 * of the following requirements:
13483
 *  - The document's parent is Third-Party
13484
 *  - The document has a different scheme (http/https) than its parent document
13485
 *  - The document's domain and subdomain do not match those of its parent
13486
 *    document.
13487
 *
13488
 * If there is an error in determining whether the document is Third-Party,
13489
 * it will be assumed to be Third-Party for security reasons.
13490
 */
13491
bool
13492
nsIDocument::IsThirdParty()
13493
0
{
13494
0
  if (mIsThirdParty.isSome()) {
13495
0
    return mIsThirdParty.value();
13496
0
  }
13497
0
13498
0
  nsCOMPtr<nsIDocShellTreeItem> docshell = this->GetDocShell();
13499
0
  if (!docshell) {
13500
0
    mIsThirdParty.emplace(true);
13501
0
    return mIsThirdParty.value();
13502
0
  }
13503
0
13504
0
  nsCOMPtr<nsIDocShellTreeItem> parent;
13505
0
  nsresult rv = docshell->GetSameTypeParent(getter_AddRefs(parent));
13506
0
  MOZ_ASSERT(NS_SUCCEEDED(rv),
13507
0
             "nsIDocShellTreeItem::GetSameTypeParent should never fail");
13508
0
  bool isTopLevel = !parent;
13509
0
13510
0
  if (isTopLevel) {
13511
0
    mIsThirdParty.emplace(false);
13512
0
    return mIsThirdParty.value();
13513
0
  }
13514
0
13515
0
  nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
13516
0
  if (!parentDocument) {
13517
0
    // Failure
13518
0
    mIsThirdParty.emplace(true);
13519
0
    return mIsThirdParty.value();
13520
0
  }
13521
0
13522
0
  if (parentDocument->IsThirdParty()) {
13523
0
    mIsThirdParty.emplace(true);
13524
0
    return mIsThirdParty.value();
13525
0
  }
13526
0
13527
0
  nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
13528
0
  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(parentDocument,
13529
0
                                                             &rv);
13530
0
  if (NS_WARN_IF(NS_FAILED(rv) || !sop)) {
13531
0
    // Failure
13532
0
    mIsThirdParty.emplace(true);
13533
0
    return mIsThirdParty.value();
13534
0
  }
13535
0
  nsCOMPtr<nsIPrincipal> parentPrincipal = sop->GetPrincipal();
13536
0
13537
0
  bool principalsMatch = false;
13538
0
  rv = principal->Equals(parentPrincipal, &principalsMatch);
13539
0
13540
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
13541
0
    // Failure
13542
0
    mIsThirdParty.emplace(true);
13543
0
    return mIsThirdParty.value();
13544
0
  }
13545
0
13546
0
  if (!principalsMatch) {
13547
0
    mIsThirdParty.emplace(true);
13548
0
    return mIsThirdParty.value();
13549
0
  }
13550
0
13551
0
  // Fall-through. Document is not a Third-Party Document.
13552
0
  mIsThirdParty.emplace(false);
13553
0
  return mIsThirdParty.value();
13554
0
}
13555
13556
void
13557
nsIDocument::ClearStaleServoData()
13558
0
{
13559
0
  DocumentStyleRootIterator iter(this);
13560
0
  while (Element* root = iter.GetNextStyleRoot()) {
13561
0
    RestyleManager::ClearServoDataFromSubtree(root);
13562
0
  }
13563
0
}
13564
13565
Selection*
13566
nsIDocument::GetSelection(ErrorResult& aRv)
13567
0
{
13568
0
  nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
13569
0
  if (!window) {
13570
0
    return nullptr;
13571
0
  }
13572
0
13573
0
  if (!window->IsCurrentInnerWindow()) {
13574
0
    return nullptr;
13575
0
  }
13576
0
13577
0
  return nsGlobalWindowInner::Cast(window)->GetSelection(aRv);
13578
0
}
13579
13580
already_AddRefed<mozilla::dom::Promise>
13581
nsIDocument::HasStorageAccess(mozilla::ErrorResult& aRv)
13582
0
{
13583
0
  nsIGlobalObject* global = GetScopeObject();
13584
0
  if (!global) {
13585
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
13586
0
    return nullptr;
13587
0
  }
13588
0
13589
0
  RefPtr<Promise> promise = Promise::Create(global, aRv);
13590
0
  if (aRv.Failed()) {
13591
0
    return nullptr;
13592
0
  }
13593
0
13594
0
  if (NodePrincipal()->GetIsNullPrincipal()) {
13595
0
    promise->MaybeResolve(false);
13596
0
    return promise.forget();
13597
0
  }
13598
0
13599
0
  if (IsTopLevelContentDocument()) {
13600
0
    promise->MaybeResolve(true);
13601
0
    return promise.forget();
13602
0
  }
13603
0
13604
0
  nsCOMPtr<nsIDocument> topLevelDoc = GetTopLevelContentDocument();
13605
0
  if (!topLevelDoc) {
13606
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
13607
0
    return nullptr;
13608
0
  }
13609
0
  if (topLevelDoc->NodePrincipal()->Equals(NodePrincipal())) {
13610
0
    promise->MaybeResolve(true);
13611
0
    return promise.forget();
13612
0
  }
13613
0
13614
0
  if (AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions() &&
13615
0
      StaticPrefs::network_cookie_cookieBehavior() ==
13616
0
        nsICookieService::BEHAVIOR_REJECT_TRACKER) {
13617
0
    // If we need to abide by Content Blocking cookie restrictions, ensure to
13618
0
    // first do all of our storage access checks.  If storage access isn't
13619
0
    // disabled in our document, given that we're a third-party, we must either
13620
0
    // not be a tracker, or be whitelisted for some reason (e.g. a storage
13621
0
    // access permission being granted).  In that case, resolve the promise and
13622
0
    // say we have obtained storage access.
13623
0
    if (!nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {
13624
0
      // Note, storage might be allowed because the top-level document is on
13625
0
      // the content blocking allowlist!  In that case, don't provide special
13626
0
      // treatment here.
13627
0
      bool isOnAllowList = false;
13628
0
      if (NS_SUCCEEDED(AntiTrackingCommon::IsOnContentBlockingAllowList(
13629
0
                         topLevelDoc->GetDocumentURI(),
13630
0
                         AntiTrackingCommon::eStorageChecks,
13631
0
                         isOnAllowList)) &&
13632
0
          !isOnAllowList) {
13633
0
        promise->MaybeResolve(true);
13634
0
        return promise.forget();
13635
0
      }
13636
0
    }
13637
0
  }
13638
0
13639
0
  nsPIDOMWindowInner* inner = GetInnerWindow();
13640
0
  nsGlobalWindowOuter* outer = nullptr;
13641
0
  if (inner) {
13642
0
    outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
13643
0
    promise->MaybeResolve(outer->HasStorageAccess());
13644
0
  } else {
13645
0
    promise->MaybeRejectWithUndefined();
13646
0
  }
13647
0
  return promise.forget();
13648
0
}
13649
13650
already_AddRefed<mozilla::dom::Promise>
13651
nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv)
13652
0
{
13653
0
  nsIGlobalObject* global = GetScopeObject();
13654
0
  if (!global) {
13655
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
13656
0
    return nullptr;
13657
0
  }
13658
0
13659
0
  RefPtr<Promise> promise = Promise::Create(global, aRv);
13660
0
  if (aRv.Failed()) {
13661
0
    return nullptr;
13662
0
  }
13663
0
13664
0
  // Step 1. If the document already has been granted access, resolve.
13665
0
  nsPIDOMWindowInner* inner = GetInnerWindow();
13666
0
  nsGlobalWindowOuter* outer = nullptr;
13667
0
  if (inner) {
13668
0
    outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
13669
0
    if (outer->HasStorageAccess()) {
13670
0
      promise->MaybeResolveWithUndefined();
13671
0
      return promise.forget();
13672
0
    }
13673
0
  }
13674
0
13675
0
  // Step 2. If the document has a null origin, reject.
13676
0
  if (NodePrincipal()->GetIsNullPrincipal()) {
13677
0
    promise->MaybeRejectWithUndefined();
13678
0
    return promise.forget();
13679
0
  }
13680
0
13681
0
  // Only enforce third-party checks when there is a reason to enforce them.
13682
0
  if (StaticPrefs::network_cookie_cookieBehavior() !=
13683
0
        nsICookieService::BEHAVIOR_ACCEPT) {
13684
0
    // Step 3. If the document's frame is the main frame, resolve.
13685
0
    if (IsTopLevelContentDocument()) {
13686
0
      promise->MaybeResolveWithUndefined();
13687
0
      return promise.forget();
13688
0
    }
13689
0
13690
0
    // Step 4. If the sub frame's origin is equal to the main frame's, resolve.
13691
0
    nsCOMPtr<nsIDocument> topLevelDoc = GetTopLevelContentDocument();
13692
0
    if (!topLevelDoc) {
13693
0
      aRv.Throw(NS_ERROR_NOT_AVAILABLE);
13694
0
      return nullptr;
13695
0
    }
13696
0
    if (topLevelDoc->NodePrincipal()->Equals(NodePrincipal())) {
13697
0
      promise->MaybeResolveWithUndefined();
13698
0
      return promise.forget();
13699
0
    }
13700
0
  }
13701
0
13702
0
  // Step 5. If the sub frame is not sandboxed, skip to step 7.
13703
0
  // Step 6. If the sub frame doesn't have the token
13704
0
  //         "allow-storage-access-by-user-activation", reject.
13705
0
  if (mSandboxFlags & SANDBOXED_STORAGE_ACCESS) {
13706
0
    promise->MaybeRejectWithUndefined();
13707
0
    return promise.forget();
13708
0
  }
13709
0
13710
0
  // Step 7. If the sub frame's parent frame is not the top frame, reject.
13711
0
  nsIDocument* parent = GetParentDocument();
13712
0
  if (parent && !parent->IsTopLevelContentDocument()) {
13713
0
    promise->MaybeRejectWithUndefined();
13714
0
    return promise.forget();
13715
0
  }
13716
0
13717
0
  // Step 8. If the browser is not processing a user gesture, reject.
13718
0
  if (!EventStateManager::IsHandlingUserInput()) {
13719
0
    promise->MaybeRejectWithUndefined();
13720
0
    return promise.forget();
13721
0
  }
13722
0
13723
0
  // Step 9. Check any additional rules that the browser has.
13724
0
  //         Examples: Whitelists, blacklists, on-device classification,
13725
0
  //         user settings, anti-clickjacking heuristics, or prompting the
13726
0
  //         user for explicit permission. Reject if some rule is not fulfilled.
13727
0
13728
0
  if (nsContentUtils::IsInPrivateBrowsing(this)) {
13729
0
    // If the document is in PB mode, it doesn't have access to its persistent
13730
0
    // cookie jar, so reject the promise here.
13731
0
    promise->MaybeRejectWithUndefined();
13732
0
    return promise.forget();
13733
0
  }
13734
0
13735
0
  bool granted = true;
13736
0
  bool isTrackingWindow = false;
13737
0
  if (AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions() &&
13738
0
      StaticPrefs::network_cookie_cookieBehavior() ==
13739
0
        nsICookieService::BEHAVIOR_REJECT_TRACKER) {
13740
0
    // Only do something special for third-party tracking content.
13741
0
    if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {
13742
0
      // Note: If this has returned true, the top-level document is guaranteed
13743
0
      // to not be on the Content Blocking allow list.
13744
0
      DebugOnly<bool> isOnAllowList = false;
13745
0
      MOZ_ASSERT_IF(NS_SUCCEEDED(AntiTrackingCommon::IsOnContentBlockingAllowList(
13746
0
                                   parent->GetDocumentURI(),
13747
0
                                   AntiTrackingCommon::eStorageChecks,
13748
0
                                   isOnAllowList)),
13749
0
                    !isOnAllowList);
13750
0
13751
0
      isTrackingWindow = true;
13752
0
      // TODO: prompt for permission
13753
0
    }
13754
0
  }
13755
0
13756
0
  // Step 10. Grant the document access to cookies and store that fact for
13757
0
  //          the purposes of future calls to hasStorageAccess() and
13758
0
  //          requestStorageAccess().
13759
0
  if (granted && inner) {
13760
0
    outer->SetHasStorageAccess(true);
13761
0
    if (isTrackingWindow) {
13762
0
      nsCOMPtr<nsIURI> uri = GetDocumentURI();
13763
0
      if (NS_WARN_IF(!uri)) {
13764
0
        aRv.Throw(NS_ERROR_NOT_AVAILABLE);
13765
0
        return nullptr;
13766
0
      }
13767
0
      nsAutoString origin;
13768
0
      nsresult rv = nsContentUtils::GetUTFOrigin(uri, origin);
13769
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
13770
0
        aRv.Throw(rv);
13771
0
        return nullptr;
13772
0
      }
13773
0
      AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(origin,
13774
0
                                                               inner,
13775
0
                                                               AntiTrackingCommon::eStorageAccessAPI)
13776
0
        ->Then(GetCurrentThreadSerialEventTarget(), __func__,
13777
0
               [promise] (bool) {
13778
0
                 promise->MaybeResolveWithUndefined();
13779
0
               },
13780
0
               [promise] (bool) {
13781
0
                 promise->MaybeRejectWithUndefined();
13782
0
               });
13783
0
    } else {
13784
0
      promise->MaybeResolveWithUndefined();
13785
0
    }
13786
0
  }
13787
0
  return promise.forget();
13788
0
}
13789
13790
void
13791
nsIDocument::RecordNavigationTiming(ReadyState aReadyState)
13792
0
{
13793
0
  if (!XRE_IsContentProcess()) {
13794
0
    return;
13795
0
  }
13796
0
  if (!IsTopLevelContentDocument()) {
13797
0
    return;
13798
0
  }
13799
0
  // If we dont have the timing yet (mostly because the doc is still loading),
13800
0
  // get it from docshell.
13801
0
  RefPtr<nsDOMNavigationTiming> timing = mTiming;
13802
0
  if (!timing) {
13803
0
    if (!mDocumentContainer) {
13804
0
      return;
13805
0
    }
13806
0
    timing = mDocumentContainer->GetNavigationTiming();
13807
0
    if (!timing) {
13808
0
      return;
13809
0
    }
13810
0
  }
13811
0
  TimeStamp startTime = timing->GetNavigationStartTimeStamp();
13812
0
  switch (aReadyState) {
13813
0
    case READYSTATE_LOADING:
13814
0
      if (!mDOMLoadingSet) {
13815
0
        Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS,
13816
0
                                       startTime);
13817
0
        mDOMLoadingSet = true;
13818
0
      }
13819
0
      break;
13820
0
    case READYSTATE_INTERACTIVE:
13821
0
      if (!mDOMInteractiveSet) {
13822
0
        Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS,
13823
0
                                       startTime);
13824
0
        mDOMInteractiveSet = true;
13825
0
      }
13826
0
      break;
13827
0
    case READYSTATE_COMPLETE:
13828
0
      if (!mDOMCompleteSet) {
13829
0
        Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS,
13830
0
                                       startTime);
13831
0
        mDOMCompleteSet = true;
13832
0
      }
13833
0
      break;
13834
0
    default:
13835
0
      NS_WARNING("Unexpected ReadyState value");
13836
0
      break;
13837
0
  }
13838
0
}
13839
13840
bool
13841
nsIDocument::ModuleScriptsEnabled()
13842
0
{
13843
0
  static bool sEnabledForContent = false;
13844
0
  static bool sCachedPref = false;
13845
0
  if (!sCachedPref) {
13846
0
    sCachedPref = true;
13847
0
    Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false);
13848
0
  }
13849
0
13850
0
  return nsContentUtils::IsChromeDoc(this) || sEnabledForContent;
13851
0
}
13852
13853
void
13854
nsIDocument::ReportShadowDOMUsage()
13855
0
{
13856
0
  if (mHasReportedShadowDOMUsage) {
13857
0
    return;
13858
0
  }
13859
0
13860
0
  nsIDocument* topLevel = GetTopLevelContentDocument();
13861
0
  if (topLevel && !topLevel->mHasReportedShadowDOMUsage) {
13862
0
    topLevel->mHasReportedShadowDOMUsage = true;
13863
0
    nsString uri;
13864
0
    Unused << topLevel->GetDocumentURI(uri);
13865
0
    if (!uri.IsEmpty()) {
13866
0
      nsAutoString msg = NS_LITERAL_STRING("Shadow DOM used in [") + uri +
13867
0
        NS_LITERAL_STRING("] or in some of its subdocuments.");
13868
0
      nsContentUtils::ReportToConsoleNonLocalized(msg, nsIScriptError::infoFlag,
13869
0
                                                  NS_LITERAL_CSTRING("DOM"),
13870
0
                                                  topLevel);
13871
0
    }
13872
0
  }
13873
0
13874
0
  mHasReportedShadowDOMUsage = true;
13875
0
}