Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/nsHTMLDocument.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsHTMLDocument.h"
8
9
#include "nsIContentPolicy.h"
10
#include "mozilla/DebugOnly.h"
11
#include "mozilla/dom/HTMLAllCollection.h"
12
#include "nsCOMPtr.h"
13
#include "nsGlobalWindow.h"
14
#include "nsString.h"
15
#include "nsPrintfCString.h"
16
#include "nsReadableUtils.h"
17
#include "nsUnicharUtils.h"
18
#include "nsIHTMLContentSink.h"
19
#include "nsIXMLContentSink.h"
20
#include "nsHTMLParts.h"
21
#include "nsHTMLStyleSheet.h"
22
#include "nsGkAtoms.h"
23
#include "nsIPresShell.h"
24
#include "nsPresContext.h"
25
#include "nsPIDOMWindow.h"
26
#include "nsDOMString.h"
27
#include "nsIStreamListener.h"
28
#include "nsIURI.h"
29
#include "nsIURIMutator.h"
30
#include "nsIIOService.h"
31
#include "nsNetUtil.h"
32
#include "nsIPrivateBrowsingChannel.h"
33
#include "nsIContentViewer.h"
34
#include "nsDocShell.h"
35
#include "nsDocShellLoadTypes.h"
36
#include "nsIWebNavigation.h"
37
#include "nsIBaseWindow.h"
38
#include "nsIScriptContext.h"
39
#include "nsIXPConnect.h"
40
#include "nsContentList.h"
41
#include "nsError.h"
42
#include "nsIPrincipal.h"
43
#include "nsJSPrincipals.h"
44
#include "nsIScriptSecurityManager.h"
45
#include "nsAttrName.h"
46
#include "nsNodeUtils.h"
47
48
#include "nsNetCID.h"
49
#include "nsICookieService.h"
50
51
#include "nsIServiceManager.h"
52
#include "nsIConsoleService.h"
53
#include "nsIComponentManager.h"
54
#include "nsParserCIID.h"
55
#include "nsNameSpaceManager.h"
56
#include "nsGenericHTMLElement.h"
57
#include "mozilla/css/Loader.h"
58
#include "nsIHttpChannel.h"
59
#include "nsIFile.h"
60
#include "nsFrameSelection.h"
61
62
#include "nsContentUtils.h"
63
#include "nsJSUtils.h"
64
#include "nsIDocumentInlines.h"
65
#include "nsIDocumentEncoder.h" //for outputting selection
66
#include "nsICachingChannel.h"
67
#include "nsIContentViewer.h"
68
#include "nsIWyciwygChannel.h"
69
#include "nsIScriptElement.h"
70
#include "nsIScriptError.h"
71
#include "nsIMutableArray.h"
72
#include "nsArrayUtils.h"
73
#include "nsIEffectiveTLDService.h"
74
75
//AHMED 12-2
76
#include "nsBidiUtils.h"
77
78
#include "mozilla/dom/FallbackEncoding.h"
79
#include "mozilla/Encoding.h"
80
#include "mozilla/HTMLEditor.h"
81
#include "mozilla/LoadInfo.h"
82
#include "nsIEditingSession.h"
83
#include "nsNodeInfoManager.h"
84
#include "nsIPlaintextEditor.h"
85
#include "nsIEditorStyleSheets.h"
86
#include "nsIInlineSpellChecker.h"
87
#include "nsRange.h"
88
#include "mozAutoDocUpdate.h"
89
#include "nsCCUncollectableMarker.h"
90
#include "nsHtml5Module.h"
91
#include "mozilla/dom/Element.h"
92
#include "mozilla/Preferences.h"
93
#include "nsMimeTypes.h"
94
#include "nsIRequest.h"
95
#include "nsHtml5TreeOpExecutor.h"
96
#include "nsHtml5Parser.h"
97
#include "nsSandboxFlags.h"
98
#include "nsIImageDocument.h"
99
#include "mozilla/dom/HTMLBodyElement.h"
100
#include "mozilla/dom/HTMLDocumentBinding.h"
101
#include "mozilla/dom/Selection.h"
102
#include "nsCharsetSource.h"
103
#include "nsIStringBundle.h"
104
#include "nsFocusManager.h"
105
#include "nsIFrame.h"
106
#include "nsIContent.h"
107
#include "nsLayoutStylesheetCache.h"
108
#include "mozilla/StyleSheet.h"
109
#include "mozilla/StyleSheetInlines.h"
110
#include "mozilla/Unused.h"
111
#include "nsCommandParams.h"
112
113
using namespace mozilla;
114
using namespace mozilla::dom;
115
116
0
#define NS_MAX_DOCUMENT_WRITE_DEPTH 20
117
118
#include "prtime.h"
119
120
//#define DEBUG_charset
121
122
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
123
124
uint32_t       nsHTMLDocument::gWyciwygSessionCnt = 0;
125
126
// this function will return false if the command is not recognized
127
// inCommandID will be converted as necessary for internal operations
128
// inParam will be converted as necessary for internal operations
129
// outParam will be Empty if no parameter is needed or if returning a boolean
130
// outIsBoolean will determine whether to send param as a boolean or string
131
// outBooleanParam will not be set unless outIsBoolean
132
static bool ConvertToMidasInternalCommand(const nsAString & inCommandID,
133
                                            const nsAString & inParam,
134
                                            nsACString& outCommandID,
135
                                            nsACString& outParam,
136
                                            bool& isBoolean,
137
                                            bool& boolValue);
138
139
static bool ConvertToMidasInternalCommand(const nsAString & inCommandID,
140
                                            nsACString& outCommandID);
141
142
// ==================================================================
143
// =
144
// ==================================================================
145
146
static bool
147
IsAsciiCompatible(const Encoding* aEncoding)
148
0
{
149
0
  return aEncoding->IsAsciiCompatible() || aEncoding == ISO_2022_JP_ENCODING;
150
0
}
151
152
nsresult
153
NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData)
154
0
{
155
0
  RefPtr<nsHTMLDocument> doc = new nsHTMLDocument();
156
0
157
0
  nsresult rv = doc->Init();
158
0
159
0
  if (NS_FAILED(rv)) {
160
0
    *aInstancePtrResult = nullptr;
161
0
    return rv;
162
0
  }
163
0
164
0
  doc->SetLoadedAsData(aLoadedAsData);
165
0
  doc.forget(aInstancePtrResult);
166
0
167
0
  return NS_OK;
168
0
}
169
170
nsHTMLDocument::nsHTMLDocument()
171
  : nsDocument("text/html")
172
  , mContentListHolder(nullptr)
173
  , mNumForms(0)
174
  , mWriteLevel(0)
175
  , mLoadFlags(0)
176
  , mTooDeepWriteRecursion(false)
177
  , mDisableDocWrite(false)
178
  , mWarnedWidthHeight(false)
179
  , mContentEditableCount(0)
180
  , mEditingState(EditingState::eOff)
181
  , mDisableCookieAccess(false)
182
  , mPendingMaybeEditingStateChanged(false)
183
0
{
184
0
  mType = eHTML;
185
0
  mDefaultElementType = kNameSpaceID_XHTML;
186
0
  mCompatMode = eCompatibility_NavQuirks;
187
0
}
188
189
nsHTMLDocument::~nsHTMLDocument()
190
0
{
191
0
}
192
193
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsHTMLDocument, nsDocument,
194
                                   mAll,
195
                                   mWyciwygChannel,
196
                                   mMidasCommandManager)
197
198
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(nsHTMLDocument,
199
                                             nsDocument,
200
                                             nsIHTMLDocument)
201
202
JSObject*
203
nsHTMLDocument::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
204
0
{
205
0
  return HTMLDocument_Binding::Wrap(aCx, this, aGivenProto);
206
0
}
207
208
nsresult
209
nsHTMLDocument::Init()
210
0
{
211
0
  nsresult rv = nsDocument::Init();
212
0
  NS_ENSURE_SUCCESS(rv, rv);
213
0
214
0
  // Now reset the compatibility mode of the CSSLoader
215
0
  // to match our compat mode.
216
0
  CSSLoader()->SetCompatibilityMode(mCompatMode);
217
0
218
0
  return NS_OK;
219
0
}
220
221
222
void
223
nsHTMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
224
0
{
225
0
  nsDocument::Reset(aChannel, aLoadGroup);
226
0
227
0
  if (aChannel) {
228
0
    aChannel->GetLoadFlags(&mLoadFlags);
229
0
  }
230
0
}
231
232
void
233
nsHTMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
234
                           nsIPrincipal* aPrincipal)
235
0
{
236
0
  mLoadFlags = nsIRequest::LOAD_NORMAL;
237
0
238
0
  nsDocument::ResetToURI(aURI, aLoadGroup, aPrincipal);
239
0
240
0
  mImages = nullptr;
241
0
  mApplets = nullptr;
242
0
  mEmbeds = nullptr;
243
0
  mLinks = nullptr;
244
0
  mAnchors = nullptr;
245
0
  mScripts = nullptr;
246
0
247
0
  mForms = nullptr;
248
0
249
0
  NS_ASSERTION(!mWyciwygChannel,
250
0
               "nsHTMLDocument::Reset() - Wyciwyg Channel  still exists!");
251
0
252
0
  mWyciwygChannel = nullptr;
253
0
254
0
  // Make the content type default to "text/html", we are a HTML
255
0
  // document, after all. Once we start getting data, this may be
256
0
  // changed.
257
0
  SetContentTypeInternal(nsDependentCString("text/html"));
258
0
}
259
260
void
261
nsHTMLDocument::TryHintCharset(nsIContentViewer* aCv,
262
                               int32_t& aCharsetSource,
263
                               NotNull<const Encoding*>& aEncoding)
264
0
{
265
0
  if (aCv) {
266
0
    int32_t requestCharsetSource;
267
0
    nsresult rv = aCv->GetHintCharacterSetSource(&requestCharsetSource);
268
0
269
0
    if(NS_SUCCEEDED(rv) && kCharsetUninitialized != requestCharsetSource) {
270
0
      auto requestCharset = aCv->GetHintCharset();
271
0
      aCv->SetHintCharacterSetSource((int32_t)(kCharsetUninitialized));
272
0
273
0
      if (requestCharsetSource <= aCharsetSource)
274
0
        return;
275
0
276
0
      if (requestCharset && IsAsciiCompatible(requestCharset)) {
277
0
        aCharsetSource = requestCharsetSource;
278
0
        aEncoding = WrapNotNull(requestCharset);
279
0
      }
280
0
    }
281
0
  }
282
0
}
283
284
285
void
286
nsHTMLDocument::TryUserForcedCharset(nsIContentViewer* aCv,
287
                                     nsIDocShell*  aDocShell,
288
                                     int32_t& aCharsetSource,
289
                                     NotNull<const Encoding*>& aEncoding)
290
0
{
291
0
  if(kCharsetFromUserForced <= aCharsetSource)
292
0
    return;
293
0
294
0
  // mCharacterSet not updated yet for channel, so check aEncoding, too.
295
0
  if (WillIgnoreCharsetOverride() || !IsAsciiCompatible(aEncoding)) {
296
0
    return;
297
0
  }
298
0
299
0
  const Encoding* forceCharsetFromDocShell = nullptr;
300
0
  if (aCv) {
301
0
    // XXX mailnews-only
302
0
    forceCharsetFromDocShell = aCv->GetForceCharset();
303
0
  }
304
0
305
0
  if(forceCharsetFromDocShell &&
306
0
     IsAsciiCompatible(forceCharsetFromDocShell)) {
307
0
    aEncoding = WrapNotNull(forceCharsetFromDocShell);
308
0
    aCharsetSource = kCharsetFromUserForced;
309
0
    return;
310
0
  }
311
0
312
0
  if (aDocShell) {
313
0
    // This is the Character Encoding menu code path in Firefox
314
0
    auto encoding = nsDocShell::Cast(aDocShell)->GetForcedCharset();
315
0
316
0
    if (encoding) {
317
0
      if (!IsAsciiCompatible(encoding)) {
318
0
        return;
319
0
      }
320
0
      aEncoding = WrapNotNull(encoding);
321
0
      aCharsetSource = kCharsetFromUserForced;
322
0
      aDocShell->SetForcedCharset(NS_LITERAL_CSTRING(""));
323
0
    }
324
0
  }
325
0
}
326
327
void
328
nsHTMLDocument::TryCacheCharset(nsICachingChannel* aCachingChannel,
329
                                int32_t& aCharsetSource,
330
                                NotNull<const Encoding*>& aEncoding)
331
0
{
332
0
  nsresult rv;
333
0
334
0
  if (kCharsetFromCache <= aCharsetSource) {
335
0
    return;
336
0
  }
337
0
338
0
  nsCString cachedCharset;
339
0
  rv = aCachingChannel->GetCacheTokenCachedCharset(cachedCharset);
340
0
  if (NS_FAILED(rv) || cachedCharset.IsEmpty()) {
341
0
    return;
342
0
  }
343
0
  // The canonical names changed, so the cache may have an old name.
344
0
  const Encoding* encoding = Encoding::ForLabelNoReplacement(cachedCharset);
345
0
  if (!encoding) {
346
0
    return;
347
0
  }
348
0
  // Check IsAsciiCompatible() even in the cache case, because the value
349
0
  // might be stale and in the case of a stale charset that is not a rough
350
0
  // ASCII superset, the parser has no way to recover.
351
0
  if (!encoding->IsAsciiCompatible() && encoding != ISO_2022_JP_ENCODING) {
352
0
    return;
353
0
  }
354
0
  aEncoding = WrapNotNull(encoding);
355
0
  aCharsetSource = kCharsetFromCache;
356
0
}
357
358
void
359
nsHTMLDocument::TryParentCharset(nsIDocShell*  aDocShell,
360
                                 int32_t& aCharsetSource,
361
                                 NotNull<const Encoding*>& aEncoding)
362
0
{
363
0
  if (!aDocShell) {
364
0
    return;
365
0
  }
366
0
  if (aCharsetSource >= kCharsetFromParentForced) {
367
0
    return;
368
0
  }
369
0
370
0
  int32_t parentSource;
371
0
  const Encoding* parentCharset;
372
0
  nsCOMPtr<nsIPrincipal> parentPrincipal;
373
0
  aDocShell->GetParentCharset(parentCharset,
374
0
                              &parentSource,
375
0
                              getter_AddRefs(parentPrincipal));
376
0
  if (!parentCharset) {
377
0
    return;
378
0
  }
379
0
  if (kCharsetFromParentForced == parentSource ||
380
0
      kCharsetFromUserForced == parentSource) {
381
0
    if (WillIgnoreCharsetOverride() ||
382
0
        !IsAsciiCompatible(aEncoding) || // if channel said UTF-16
383
0
        !IsAsciiCompatible(parentCharset)) {
384
0
      return;
385
0
    }
386
0
    aEncoding = WrapNotNull(parentCharset);
387
0
    aCharsetSource = kCharsetFromParentForced;
388
0
    return;
389
0
  }
390
0
391
0
  if (aCharsetSource >= kCharsetFromParentFrame) {
392
0
    return;
393
0
  }
394
0
395
0
  if (kCharsetFromCache <= parentSource) {
396
0
    // Make sure that's OK
397
0
    if (!NodePrincipal()->Equals(parentPrincipal) ||
398
0
        !IsAsciiCompatible(parentCharset)) {
399
0
      return;
400
0
    }
401
0
402
0
    aEncoding = WrapNotNull(parentCharset);
403
0
    aCharsetSource = kCharsetFromParentFrame;
404
0
  }
405
0
}
406
407
void
408
nsHTMLDocument::TryTLD(int32_t& aCharsetSource,
409
                       NotNull<const Encoding*>& aEncoding)
410
0
{
411
0
  if (aCharsetSource >= kCharsetFromTopLevelDomain) {
412
0
    return;
413
0
  }
414
0
  if (!FallbackEncoding::sGuessFallbackFromTopLevelDomain) {
415
0
    return;
416
0
  }
417
0
  if (!mDocumentURI) {
418
0
    return;
419
0
  }
420
0
  nsAutoCString host;
421
0
  mDocumentURI->GetAsciiHost(host);
422
0
  if (host.IsEmpty()) {
423
0
    return;
424
0
  }
425
0
  // First let's see if the host is DNS-absolute and ends with a dot and
426
0
  // get rid of that one.
427
0
  if (host.Last() == '.') {
428
0
    host.SetLength(host.Length() - 1);
429
0
    if (host.IsEmpty()) {
430
0
      return;
431
0
    }
432
0
  }
433
0
  // If we still have a dot, the host is weird, so let's continue only
434
0
  // if we have something other than a dot now.
435
0
  if (host.Last() == '.') {
436
0
    return;
437
0
  }
438
0
  int32_t index = host.RFindChar('.');
439
0
  if (index == kNotFound) {
440
0
    // We have an intranet host, Gecko-internal URL or an IPv6 address.
441
0
    return;
442
0
  }
443
0
  // Since the string didn't end with a dot and we found a dot,
444
0
  // there is at least one character between the dot and the end of
445
0
  // the string, so taking the substring below is safe.
446
0
  nsAutoCString tld;
447
0
  ToLowerCase(Substring(host, index + 1, host.Length() - (index + 1)), tld);
448
0
  // Reject generic TLDs and country TLDs that need more research
449
0
  if (!FallbackEncoding::IsParticipatingTopLevelDomain(tld)) {
450
0
    return;
451
0
  }
452
0
  // Check if we have an IPv4 address
453
0
  bool seenNonDigit = false;
454
0
  for (size_t i = 0; i < tld.Length(); ++i) {
455
0
    char c = tld.CharAt(i);
456
0
    if (c < '0' || c > '9') {
457
0
      seenNonDigit = true;
458
0
      break;
459
0
    }
460
0
  }
461
0
  if (!seenNonDigit) {
462
0
    return;
463
0
  }
464
0
  aCharsetSource = kCharsetFromTopLevelDomain;
465
0
  aEncoding = FallbackEncoding::FromTopLevelDomain(tld);
466
0
}
467
468
void
469
nsHTMLDocument::TryFallback(int32_t& aCharsetSource,
470
                            NotNull<const Encoding*>& aEncoding)
471
0
{
472
0
  if (kCharsetFromFallback <= aCharsetSource)
473
0
    return;
474
0
475
0
  aCharsetSource = kCharsetFromFallback;
476
0
  bool isFile = false;
477
0
  if (FallbackEncoding::sFallbackToUTF8ForFile && mDocumentURI &&
478
0
      NS_SUCCEEDED(mDocumentURI->SchemeIs("file", &isFile)) && isFile) {
479
0
    aEncoding = UTF_8_ENCODING;
480
0
    return;
481
0
  }
482
0
  aEncoding = FallbackEncoding::FromLocale();
483
0
}
484
485
void
486
nsHTMLDocument::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding)
487
0
{
488
0
  nsDocument::SetDocumentCharacterSet(aEncoding);
489
0
  // Make sure to stash this charset on our channel as needed if it's a wyciwyg
490
0
  // channel.
491
0
  nsCOMPtr<nsIWyciwygChannel> wyciwygChannel = do_QueryInterface(mChannel);
492
0
  if (wyciwygChannel) {
493
0
    nsAutoCString charset;
494
0
    aEncoding->Name(charset);
495
0
    wyciwygChannel->SetCharsetAndSource(GetDocumentCharacterSetSource(),
496
0
                                        charset);
497
0
  }
498
0
}
499
500
nsresult
501
nsHTMLDocument::StartDocumentLoad(const char* aCommand,
502
                                  nsIChannel* aChannel,
503
                                  nsILoadGroup* aLoadGroup,
504
                                  nsISupports* aContainer,
505
                                  nsIStreamListener **aDocListener,
506
                                  bool aReset,
507
                                  nsIContentSink* aSink)
508
0
{
509
0
  if (!aCommand) {
510
0
    MOZ_ASSERT(false, "Command is mandatory");
511
0
    return NS_ERROR_INVALID_POINTER;
512
0
  }
513
0
  if (aSink) {
514
0
    MOZ_ASSERT(false, "Got a sink override. Should not happen for HTML doc.");
515
0
    return NS_ERROR_INVALID_ARG;
516
0
  }
517
0
  if (mType != eHTML) {
518
0
    MOZ_ASSERT(mType == eXHTML);
519
0
    MOZ_ASSERT(false, "Must not set HTML doc to XHTML mode before load start.");
520
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
521
0
  }
522
0
523
0
  nsAutoCString contentType;
524
0
  aChannel->GetContentType(contentType);
525
0
526
0
  bool view = !strcmp(aCommand, "view") ||
527
0
              !strcmp(aCommand, "external-resource");
528
0
  bool viewSource = !strcmp(aCommand, "view-source");
529
0
  bool asData = !strcmp(aCommand, kLoadAsData);
530
0
  if (!(view || viewSource || asData)) {
531
0
    MOZ_ASSERT(false, "Bad parser command");
532
0
    return NS_ERROR_INVALID_ARG;
533
0
  }
534
0
535
0
  bool html = contentType.EqualsLiteral(TEXT_HTML);
536
0
  bool xhtml = !html && (contentType.EqualsLiteral(APPLICATION_XHTML_XML) || contentType.EqualsLiteral(APPLICATION_WAPXHTML_XML));
537
0
  bool plainText = !html && !xhtml && nsContentUtils::IsPlainTextType(contentType);
538
0
  if (!(html || xhtml || plainText || viewSource)) {
539
0
    MOZ_ASSERT(false, "Channel with bad content type.");
540
0
    return NS_ERROR_INVALID_ARG;
541
0
  }
542
0
543
0
  bool forceUtf8 = plainText &&
544
0
    nsContentUtils::IsUtf8OnlyPlainTextType(contentType);
545
0
546
0
  bool loadAsHtml5 = true;
547
0
548
0
  if (!viewSource && xhtml) {
549
0
      // We're parsing XHTML as XML, remember that.
550
0
      mType = eXHTML;
551
0
      mCompatMode = eCompatibility_FullStandards;
552
0
      loadAsHtml5 = false;
553
0
  }
554
0
555
0
  // TODO: Proper about:blank treatment is bug 543435
556
0
  if (loadAsHtml5 && view) {
557
0
    // mDocumentURI hasn't been set, yet, so get the URI from the channel
558
0
    nsCOMPtr<nsIURI> uri;
559
0
    aChannel->GetOriginalURI(getter_AddRefs(uri));
560
0
    // Adapted from nsDocShell:
561
0
    // GetSpec can be expensive for some URIs, so check the scheme first.
562
0
    bool isAbout = false;
563
0
    if (uri && NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
564
0
      if (uri->GetSpecOrDefault().EqualsLiteral("about:blank")) {
565
0
        loadAsHtml5 = false;
566
0
      }
567
0
    }
568
0
  }
569
0
570
0
  CSSLoader()->SetCompatibilityMode(mCompatMode);
571
0
572
0
  nsresult rv = nsDocument::StartDocumentLoad(aCommand,
573
0
                                              aChannel, aLoadGroup,
574
0
                                              aContainer,
575
0
                                              aDocListener, aReset);
576
0
  if (NS_FAILED(rv)) {
577
0
    return rv;
578
0
  }
579
0
580
0
  // Store the security info for future use with wyciwyg channels.
581
0
  aChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
582
0
583
0
  nsCOMPtr<nsIURI> uri;
584
0
  rv = aChannel->GetURI(getter_AddRefs(uri));
585
0
  if (NS_FAILED(rv)) {
586
0
    return rv;
587
0
  }
588
0
589
0
  nsCOMPtr<nsICachingChannel> cachingChan = do_QueryInterface(aChannel);
590
0
591
0
  if (loadAsHtml5) {
592
0
    mParser = nsHtml5Module::NewHtml5Parser();
593
0
    if (plainText) {
594
0
      if (viewSource) {
595
0
        mParser->MarkAsNotScriptCreated("view-source-plain");
596
0
      } else {
597
0
        mParser->MarkAsNotScriptCreated("plain-text");
598
0
      }
599
0
    } else if (viewSource && !html) {
600
0
      mParser->MarkAsNotScriptCreated("view-source-xml");
601
0
    } else {
602
0
      mParser->MarkAsNotScriptCreated(aCommand);
603
0
    }
604
0
  } else {
605
0
    mParser = do_CreateInstance(kCParserCID, &rv);
606
0
    NS_ENSURE_SUCCESS(rv, rv);
607
0
  }
608
0
609
0
  // Look for the parent document.  Note that at this point we don't have our
610
0
  // content viewer set up yet, and therefore do not have a useful
611
0
  // mParentDocument.
612
0
613
0
  // in this block of code, if we get an error result, we return it
614
0
  // but if we get a null pointer, that's perfectly legal for parent
615
0
  // and parentContentViewer
616
0
  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
617
0
  nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
618
0
  if (docShell) {
619
0
    docShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
620
0
  }
621
0
622
0
  nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
623
0
  nsCOMPtr<nsIContentViewer> parentContentViewer;
624
0
  if (parent) {
625
0
    rv = parent->GetContentViewer(getter_AddRefs(parentContentViewer));
626
0
    NS_ENSURE_SUCCESS(rv, rv);
627
0
  }
628
0
629
0
  nsCOMPtr<nsIContentViewer> cv;
630
0
  if (docShell) {
631
0
    docShell->GetContentViewer(getter_AddRefs(cv));
632
0
  }
633
0
  if (!cv) {
634
0
    cv = parentContentViewer.forget();
635
0
  }
636
0
637
0
  nsAutoCString urlSpec;
638
0
  uri->GetSpec(urlSpec);
639
#ifdef DEBUG_charset
640
  printf("Determining charset for %s\n", urlSpec.get());
641
#endif
642
643
0
  // These are the charset source and charset for our document
644
0
  int32_t charsetSource;
645
0
  auto encoding = UTF_8_ENCODING;
646
0
647
0
  // These are the charset source and charset for the parser.  This can differ
648
0
  // from that for the document if the channel is a wyciwyg channel.
649
0
  int32_t parserCharsetSource;
650
0
  auto parserCharset = UTF_8_ENCODING;
651
0
652
0
  nsCOMPtr<nsIWyciwygChannel> wyciwygChannel;
653
0
654
0
  // For error reporting and referrer policy setting
655
0
  nsHtml5TreeOpExecutor* executor = nullptr;
656
0
  if (loadAsHtml5) {
657
0
    executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
658
0
    if (mReferrerPolicySet) {
659
0
      // CSP may have set the referrer policy, so a speculative parser should
660
0
      // start with the new referrer policy.
661
0
      executor->SetSpeculationReferrerPolicy(static_cast<ReferrerPolicy>(mReferrerPolicy));
662
0
    }
663
0
  }
664
0
665
0
  if (forceUtf8) {
666
0
    charsetSource = kCharsetFromUtf8OnlyMime;
667
0
    parserCharsetSource = charsetSource;
668
0
  } else if (!IsHTMLDocument() || !docShell) { // no docshell for text/html XHR
669
0
    charsetSource = IsHTMLDocument() ? kCharsetFromFallback
670
0
                                     : kCharsetFromDocTypeDefault;
671
0
    TryChannelCharset(aChannel, charsetSource, encoding, executor);
672
0
    parserCharset = encoding;
673
0
    parserCharsetSource = charsetSource;
674
0
  } else {
675
0
    NS_ASSERTION(docShell, "Unexpected null value");
676
0
677
0
    charsetSource = kCharsetUninitialized;
678
0
    wyciwygChannel = do_QueryInterface(aChannel);
679
0
680
0
    // The following will try to get the character encoding from various
681
0
    // sources. Each Try* function will return early if the source is already
682
0
    // at least as large as any of the sources it might look at.  Some of
683
0
    // these functions (like TryHintCharset and TryParentCharset) can set
684
0
    // charsetSource to various values depending on where the charset they
685
0
    // end up finding originally comes from.
686
0
687
0
    // Don't actually get the charset from the channel if this is a
688
0
    // wyciwyg channel; it'll always be UTF-16
689
0
    if (!wyciwygChannel) {
690
0
      // Otherwise, try the channel's charset (e.g., charset from HTTP
691
0
      // "Content-Type" header) first. This way, we get to reject overrides in
692
0
      // TryParentCharset and TryUserForcedCharset if the channel said UTF-16.
693
0
      // This is to avoid socially engineered XSS by adding user-supplied
694
0
      // content to a UTF-16 site such that the byte have a dangerous
695
0
      // interpretation as ASCII and the user can be lured to using the
696
0
      // charset menu.
697
0
      TryChannelCharset(aChannel, charsetSource, encoding, executor);
698
0
    }
699
0
700
0
    TryUserForcedCharset(cv, docShell, charsetSource, encoding);
701
0
702
0
    TryHintCharset(cv, charsetSource, encoding); // XXX mailnews-only
703
0
    TryParentCharset(docShell, charsetSource, encoding);
704
0
705
0
    if (cachingChan && !urlSpec.IsEmpty()) {
706
0
      TryCacheCharset(cachingChan, charsetSource, encoding);
707
0
    }
708
0
709
0
    TryTLD(charsetSource, encoding);
710
0
    TryFallback(charsetSource, encoding);
711
0
712
0
    if (wyciwygChannel) {
713
0
      // We know for sure that the parser needs to be using UTF16.
714
0
      parserCharset = UTF_16LE_ENCODING;
715
0
      parserCharsetSource = charsetSource < kCharsetFromChannel ?
716
0
        kCharsetFromChannel : charsetSource;
717
0
718
0
      nsAutoCString cachedCharset;
719
0
      int32_t cachedSource;
720
0
      rv = wyciwygChannel->GetCharsetAndSource(&cachedSource, cachedCharset);
721
0
      if (NS_SUCCEEDED(rv)) {
722
0
        if (cachedSource > charsetSource) {
723
0
          auto cachedEncoding = Encoding::ForLabel(cachedCharset);
724
0
          if (cachedEncoding) {
725
0
            charsetSource = cachedSource;
726
0
            encoding = WrapNotNull(cachedEncoding);
727
0
          }
728
0
        }
729
0
      } else {
730
0
        // Don't propagate this error.
731
0
        rv = NS_OK;
732
0
      }
733
0
    } else {
734
0
      parserCharset = encoding;
735
0
      parserCharsetSource = charsetSource;
736
0
    }
737
0
  }
738
0
739
0
  SetDocumentCharacterSetSource(charsetSource);
740
0
  SetDocumentCharacterSet(encoding);
741
0
742
0
  if (cachingChan) {
743
0
    NS_ASSERTION(encoding == parserCharset,
744
0
                 "How did those end up different here?  wyciwyg channels are "
745
0
                 "not nsICachingChannel");
746
0
    nsAutoCString charset;
747
0
    encoding->Name(charset);
748
0
    rv = cachingChan->SetCacheTokenCachedCharset(charset);
749
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "cannot SetMetaDataElement");
750
0
    rv = NS_OK; // don't propagate error
751
0
  }
752
0
753
0
  // Set the parser as the stream listener for the document loader...
754
0
  rv = NS_OK;
755
0
  nsCOMPtr<nsIStreamListener> listener = mParser->GetStreamListener();
756
0
  listener.forget(aDocListener);
757
0
758
#ifdef DEBUG_charset
759
  printf(" charset = %s source %d\n",
760
         charset.get(), charsetSource);
761
#endif
762
  mParser->SetDocumentCharset(parserCharset, parserCharsetSource);
763
0
  mParser->SetCommand(aCommand);
764
0
765
0
  if (!IsHTMLDocument()) {
766
0
    MOZ_ASSERT(!loadAsHtml5);
767
0
    nsCOMPtr<nsIXMLContentSink> xmlsink;
768
0
    NS_NewXMLContentSink(getter_AddRefs(xmlsink), this, uri,
769
0
                         docShell, aChannel);
770
0
    mParser->SetContentSink(xmlsink);
771
0
  } else {
772
0
    if (loadAsHtml5) {
773
0
      nsHtml5Module::Initialize(mParser, this, uri, docShell, aChannel);
774
0
    } else {
775
0
      // about:blank *only*
776
0
      nsCOMPtr<nsIHTMLContentSink> htmlsink;
777
0
      NS_NewHTMLContentSink(getter_AddRefs(htmlsink), this, uri,
778
0
                            docShell, aChannel);
779
0
      mParser->SetContentSink(htmlsink);
780
0
    }
781
0
  }
782
0
783
0
  if (plainText && !nsContentUtils::IsChildOfSameType(this) &&
784
0
      Preferences::GetBool("plain_text.wrap_long_lines")) {
785
0
    nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
786
0
    NS_ASSERTION(NS_SUCCEEDED(rv) && bundleService, "The bundle service could not be loaded");
787
0
    nsCOMPtr<nsIStringBundle> bundle;
788
0
    rv = bundleService->CreateBundle("chrome://global/locale/browser.properties",
789
0
                                     getter_AddRefs(bundle));
790
0
    NS_ASSERTION(NS_SUCCEEDED(rv) && bundle, "chrome://global/locale/browser.properties could not be loaded");
791
0
    nsAutoString title;
792
0
    if (bundle) {
793
0
      bundle->GetStringFromName("plainText.wordWrap", title);
794
0
    }
795
0
    SetSelectedStyleSheetSet(title);
796
0
  }
797
0
798
0
  // parser the content of the URI
799
0
  mParser->Parse(uri, nullptr, (void *)this);
800
0
801
0
  return rv;
802
0
}
803
804
void
805
nsHTMLDocument::StopDocumentLoad()
806
0
{
807
0
  BlockOnload();
808
0
809
0
  // Remove the wyciwyg channel request from the document load group
810
0
  // that we added in Open() if Open() was called on this doc.
811
0
  RemoveWyciwygChannel();
812
0
  NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::StopDocumentLoad(): "
813
0
               "nsIWyciwygChannel could not be removed!");
814
0
815
0
  nsDocument::StopDocumentLoad();
816
0
  UnblockOnload(false);
817
0
}
818
819
void
820
nsHTMLDocument::BeginLoad()
821
0
{
822
0
  if (IsEditingOn()) {
823
0
    // Reset() blows away all event listeners in the document, and our
824
0
    // editor relies heavily on those. Midas is turned on, to make it
825
0
    // work, re-initialize it to give it a chance to add its event
826
0
    // listeners again.
827
0
828
0
    TurnEditingOff();
829
0
    EditingStateChanged();
830
0
  }
831
0
  nsDocument::BeginLoad();
832
0
}
833
834
void
835
nsHTMLDocument::EndLoad()
836
0
{
837
0
  bool turnOnEditing =
838
0
    mParser && (HasFlag(NODE_IS_EDITABLE) || mContentEditableCount > 0);
839
0
  // Note: nsDocument::EndLoad nulls out mParser.
840
0
  nsDocument::EndLoad();
841
0
  if (turnOnEditing) {
842
0
    EditingStateChanged();
843
0
  }
844
0
}
845
846
void
847
nsHTMLDocument::SetCompatibilityMode(nsCompatibility aMode)
848
0
{
849
0
  NS_ASSERTION(IsHTMLDocument() || aMode == eCompatibility_FullStandards,
850
0
               "Bad compat mode for XHTML document!");
851
0
852
0
  if (mCompatMode == aMode) {
853
0
    return;
854
0
  }
855
0
  mCompatMode = aMode;
856
0
  CSSLoader()->SetCompatibilityMode(mCompatMode);
857
0
  if (nsPresContext* pc = GetPresContext()) {
858
0
    pc->CompatibilityModeChanged();
859
0
  }
860
0
}
861
862
nsIContent*
863
nsHTMLDocument::GetUnfocusedKeyEventTarget()
864
0
{
865
0
  if (nsGenericHTMLElement* body = GetBody()) {
866
0
    return body;
867
0
  }
868
0
  return nsDocument::GetUnfocusedKeyEventTarget();
869
0
}
870
871
already_AddRefed<nsIURI>
872
nsHTMLDocument::GetDomainURI()
873
0
{
874
0
  nsIPrincipal* principal = NodePrincipal();
875
0
876
0
  nsCOMPtr<nsIURI> uri;
877
0
  principal->GetDomain(getter_AddRefs(uri));
878
0
  if (uri) {
879
0
    return uri.forget();
880
0
  }
881
0
882
0
  principal->GetURI(getter_AddRefs(uri));
883
0
  return uri.forget();
884
0
}
885
886
887
void
888
nsHTMLDocument::GetDomain(nsAString& aDomain)
889
0
{
890
0
  nsCOMPtr<nsIURI> uri = GetDomainURI();
891
0
892
0
  if (!uri) {
893
0
    aDomain.Truncate();
894
0
    return;
895
0
  }
896
0
897
0
  nsAutoCString hostName;
898
0
  nsresult rv = nsContentUtils::GetHostOrIPv6WithBrackets(uri, hostName);
899
0
  if (NS_SUCCEEDED(rv)) {
900
0
    CopyUTF8toUTF16(hostName, aDomain);
901
0
  } else {
902
0
    // If we can't get the host from the URI (e.g. about:, javascript:,
903
0
    // etc), just return an empty string.
904
0
    aDomain.Truncate();
905
0
  }
906
0
}
907
908
already_AddRefed<nsIURI>
909
nsHTMLDocument::CreateInheritingURIForHost(const nsACString& aHostString)
910
0
{
911
0
  if (aHostString.IsEmpty()) {
912
0
    return nullptr;
913
0
  }
914
0
915
0
  // Create new URI
916
0
  nsCOMPtr<nsIURI> uri = GetDomainURI();
917
0
  if (!uri) {
918
0
    return nullptr;
919
0
  }
920
0
921
0
  nsresult rv;
922
0
  rv = NS_MutateURI(uri)
923
0
         .SetUserPass(EmptyCString())
924
0
         .SetPort(-1) // we want to reset the port number if needed.
925
0
         .SetHostPort(aHostString)
926
0
         .Finalize(uri);
927
0
  if (NS_FAILED(rv)) {
928
0
    return nullptr;
929
0
  }
930
0
931
0
  return uri.forget();
932
0
}
933
934
already_AddRefed<nsIURI>
935
nsHTMLDocument::RegistrableDomainSuffixOfInternal(const nsAString& aNewDomain,
936
                                                  nsIURI* aOrigHost)
937
0
{
938
0
  if (NS_WARN_IF(!aOrigHost)) {
939
0
    return nullptr;
940
0
  }
941
0
942
0
  nsCOMPtr<nsIURI> newURI = CreateInheritingURIForHost(NS_ConvertUTF16toUTF8(aNewDomain));
943
0
  if (!newURI) {
944
0
    // Error: failed to parse input domain
945
0
    return nullptr;
946
0
  }
947
0
948
0
  // Check new domain - must be a superdomain of the current host
949
0
  // For example, a page from foo.bar.com may set domain to bar.com,
950
0
  // but not to ar.com, baz.com, or fi.foo.bar.com.
951
0
  nsAutoCString current;
952
0
  nsAutoCString domain;
953
0
  if (NS_FAILED(aOrigHost->GetAsciiHost(current))) {
954
0
    current.Truncate();
955
0
  }
956
0
  if (NS_FAILED(newURI->GetAsciiHost(domain))) {
957
0
    domain.Truncate();
958
0
  }
959
0
960
0
  bool ok = current.Equals(domain);
961
0
  if (current.Length() > domain.Length() &&
962
0
      StringEndsWith(current, domain) &&
963
0
      current.CharAt(current.Length() - domain.Length() - 1) == '.') {
964
0
    // We're golden if the new domain is the current page's base domain or a
965
0
    // subdomain of it.
966
0
    nsCOMPtr<nsIEffectiveTLDService> tldService =
967
0
      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
968
0
    if (!tldService) {
969
0
      return nullptr;
970
0
    }
971
0
972
0
    nsAutoCString currentBaseDomain;
973
0
    ok = NS_SUCCEEDED(tldService->GetBaseDomain(aOrigHost, 0, currentBaseDomain));
974
0
    NS_ASSERTION(StringEndsWith(domain, currentBaseDomain) ==
975
0
                 (domain.Length() >= currentBaseDomain.Length()),
976
0
                 "uh-oh!  slight optimization wasn't valid somehow!");
977
0
    ok = ok && domain.Length() >= currentBaseDomain.Length();
978
0
  }
979
0
980
0
  if (!ok) {
981
0
    // Error: illegal domain
982
0
    return nullptr;
983
0
  }
984
0
985
0
  return CreateInheritingURIForHost(domain);
986
0
}
987
988
bool
989
nsHTMLDocument::IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
990
                                                     const nsACString& aOrigHost)
991
0
{
992
0
  // https://html.spec.whatwg.org/multipage/browsers.html#is-a-registrable-domain-suffix-of-or-is-equal-to
993
0
  if (aHostSuffixString.IsEmpty()) {
994
0
    return false;
995
0
  }
996
0
997
0
  nsCOMPtr<nsIURI> origURI = CreateInheritingURIForHost(aOrigHost);
998
0
  if (!origURI) {
999
0
    // Error: failed to parse input domain
1000
0
    return false;
1001
0
  }
1002
0
1003
0
  nsCOMPtr<nsIURI> newURI = RegistrableDomainSuffixOfInternal(aHostSuffixString, origURI);
1004
0
  if (!newURI) {
1005
0
    // Error: illegal domain
1006
0
    return false;
1007
0
  }
1008
0
  return true;
1009
0
}
1010
1011
1012
void
1013
nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv)
1014
0
{
1015
0
  if (mSandboxFlags & SANDBOXED_DOMAIN) {
1016
0
    // We're sandboxed; disallow setting domain
1017
0
    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1018
0
    return;
1019
0
  }
1020
0
1021
0
  if (aDomain.IsEmpty()) {
1022
0
    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1023
0
    return;
1024
0
  }
1025
0
1026
0
  nsCOMPtr<nsIURI> uri = GetDomainURI();
1027
0
  if (!uri) {
1028
0
    rv.Throw(NS_ERROR_FAILURE);
1029
0
    return;
1030
0
  }
1031
0
1032
0
  // Check new domain - must be a superdomain of the current host
1033
0
  // For example, a page from foo.bar.com may set domain to bar.com,
1034
0
  // but not to ar.com, baz.com, or fi.foo.bar.com.
1035
0
1036
0
  nsCOMPtr<nsIURI> newURI = RegistrableDomainSuffixOfInternal(aDomain, uri);
1037
0
  if (!newURI) {
1038
0
    // Error: illegal domain
1039
0
    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1040
0
    return;
1041
0
  }
1042
0
1043
0
  rv = NodePrincipal()->SetDomain(newURI);
1044
0
}
1045
1046
already_AddRefed<nsIChannel>
1047
nsHTMLDocument::CreateDummyChannelForCookies(nsIURI* aCodebaseURI)
1048
0
{
1049
0
  // The cookie service reads the privacy status of the channel we pass to it in
1050
0
  // order to determine which cookie database to query.  In some cases we don't
1051
0
  // have a proper channel to hand it to the cookie service though.  This
1052
0
  // function creates a dummy channel that is not used to load anything, for the
1053
0
  // sole purpose of handing it to the cookie service.  DO NOT USE THIS CHANNEL
1054
0
  // FOR ANY OTHER PURPOSE.
1055
0
  MOZ_ASSERT(!mChannel);
1056
0
1057
0
  // The following channel is never openend, so it does not matter what
1058
0
  // securityFlags we pass; let's follow the principle of least privilege.
1059
0
  nsCOMPtr<nsIChannel> channel;
1060
0
  NS_NewChannel(getter_AddRefs(channel), aCodebaseURI, this,
1061
0
                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
1062
0
                nsIContentPolicy::TYPE_INVALID);
1063
0
  nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel =
1064
0
    do_QueryInterface(channel);
1065
0
  nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
1066
0
  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
1067
0
  if (!pbChannel || !loadContext) {
1068
0
    return nullptr;
1069
0
  }
1070
0
  pbChannel->SetPrivate(loadContext->UsePrivateBrowsing());
1071
0
1072
0
  nsCOMPtr<nsIHttpChannel> docHTTPChannel =
1073
0
    do_QueryInterface(GetChannel());
1074
0
  if (docHTTPChannel) {
1075
0
    bool isTracking = docHTTPChannel->GetIsTrackingResource();
1076
0
    if (isTracking) {
1077
0
      // If our document channel is from a tracking resource, we must
1078
0
      // override our channel's tracking status.
1079
0
      nsCOMPtr<nsIHttpChannel> httpChannel =
1080
0
        do_QueryInterface(channel);
1081
0
      MOZ_ASSERT(httpChannel, "How come we're coming from an HTTP doc but "
1082
0
                              "we don't have an HTTP channel here?");
1083
0
      if (httpChannel) {
1084
0
        httpChannel->OverrideTrackingFlagsForDocumentCookieAccessor(docHTTPChannel);
1085
0
      }
1086
0
    }
1087
0
  }
1088
0
1089
0
  return channel.forget();
1090
0
}
1091
1092
void
1093
nsHTMLDocument::GetCookie(nsAString& aCookie, ErrorResult& rv)
1094
0
{
1095
0
  aCookie.Truncate(); // clear current cookie in case service fails;
1096
0
                      // no cookie isn't an error condition.
1097
0
1098
0
  if (mDisableCookieAccess) {
1099
0
    return;
1100
0
  }
1101
0
1102
0
  // If the document's sandboxed origin flag is set, access to read cookies
1103
0
  // is prohibited.
1104
0
  if (mSandboxFlags & SANDBOXED_ORIGIN) {
1105
0
    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1106
0
    return;
1107
0
  }
1108
0
1109
0
  if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {
1110
0
    return;
1111
0
  }
1112
0
1113
0
  // If the document is a cookie-averse Document... return the empty string.
1114
0
  if (IsCookieAverse()) {
1115
0
    return;
1116
0
  }
1117
0
1118
0
  // not having a cookie service isn't an error
1119
0
  nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
1120
0
  if (service) {
1121
0
    // Get a URI from the document principal. We use the original
1122
0
    // codebase in case the codebase was changed by SetDomain
1123
0
    nsCOMPtr<nsIURI> codebaseURI;
1124
0
    NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
1125
0
1126
0
    if (!codebaseURI) {
1127
0
      // Document's principal is not a codebase (may be system), so
1128
0
      // can't set cookies
1129
0
1130
0
      return;
1131
0
    }
1132
0
1133
0
    nsCOMPtr<nsIChannel> channel(mChannel);
1134
0
    if (!channel) {
1135
0
      channel = CreateDummyChannelForCookies(codebaseURI);
1136
0
      if (!channel) {
1137
0
        return;
1138
0
      }
1139
0
    }
1140
0
1141
0
    nsCString cookie;
1142
0
    service->GetCookieString(codebaseURI, channel, getter_Copies(cookie));
1143
0
    // CopyUTF8toUTF16 doesn't handle error
1144
0
    // because it assumes that the input is valid.
1145
0
    UTF_8_ENCODING->DecodeWithoutBOMHandling(cookie, aCookie);
1146
0
  }
1147
0
}
1148
1149
void
1150
nsHTMLDocument::SetCookie(const nsAString& aCookie, ErrorResult& rv)
1151
0
{
1152
0
  if (mDisableCookieAccess) {
1153
0
    return;
1154
0
  }
1155
0
1156
0
  // If the document's sandboxed origin flag is set, access to write cookies
1157
0
  // is prohibited.
1158
0
  if (mSandboxFlags & SANDBOXED_ORIGIN) {
1159
0
    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1160
0
    return;
1161
0
  }
1162
0
1163
0
  if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {
1164
0
    return;
1165
0
  }
1166
0
1167
0
  // If the document is a cookie-averse Document... do nothing.
1168
0
  if (IsCookieAverse()) {
1169
0
    return;
1170
0
  }
1171
0
1172
0
  // not having a cookie service isn't an error
1173
0
  nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
1174
0
  if (service && mDocumentURI) {
1175
0
    // The for getting the URI matches nsNavigator::GetCookieEnabled
1176
0
    nsCOMPtr<nsIURI> codebaseURI;
1177
0
    NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
1178
0
1179
0
    if (!codebaseURI) {
1180
0
      // Document's principal is not a codebase (may be system), so
1181
0
      // can't set cookies
1182
0
1183
0
      return;
1184
0
    }
1185
0
1186
0
    nsCOMPtr<nsIChannel> channel(mChannel);
1187
0
    if (!channel) {
1188
0
      channel = CreateDummyChannelForCookies(codebaseURI);
1189
0
      if (!channel) {
1190
0
        return;
1191
0
      }
1192
0
    }
1193
0
1194
0
    NS_ConvertUTF16toUTF8 cookie(aCookie);
1195
0
    service->SetCookieString(codebaseURI, nullptr, cookie.get(), channel);
1196
0
  }
1197
0
}
1198
1199
already_AddRefed<nsPIDOMWindowOuter>
1200
nsHTMLDocument::Open(JSContext* /* unused */,
1201
                     const nsAString& aURL,
1202
                     const nsAString& aName,
1203
                     const nsAString& aFeatures,
1204
                     bool aReplace,
1205
                     ErrorResult& rv)
1206
0
{
1207
0
  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
1208
0
             "XOW should have caught this!");
1209
0
1210
0
  nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
1211
0
  if (!window) {
1212
0
    rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
1213
0
    return nullptr;
1214
0
  }
1215
0
  nsCOMPtr<nsPIDOMWindowOuter> outer =
1216
0
    nsPIDOMWindowOuter::GetFromCurrentInner(window);
1217
0
  if (!outer) {
1218
0
    rv.Throw(NS_ERROR_NOT_INITIALIZED);
1219
0
    return nullptr;
1220
0
  }
1221
0
  RefPtr<nsGlobalWindowOuter> win = nsGlobalWindowOuter::Cast(outer);
1222
0
  nsCOMPtr<nsPIDOMWindowOuter> newWindow;
1223
0
  // XXXbz We ignore aReplace for now.
1224
0
  rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newWindow));
1225
0
  return newWindow.forget();
1226
0
}
1227
1228
already_AddRefed<nsIDocument>
1229
nsHTMLDocument::Open(JSContext* cx,
1230
                     const Optional<nsAString>& /* unused */,
1231
                     const nsAString& aReplace,
1232
                     ErrorResult& aError)
1233
0
{
1234
0
  // Implements the "When called with two arguments (or fewer)" steps here:
1235
0
  // https://html.spec.whatwg.org/multipage/webappapis.html#opening-the-input-stream
1236
0
1237
0
  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
1238
0
             "XOW should have caught this!");
1239
0
  if (!IsHTMLDocument() || mDisableDocWrite) {
1240
0
    // No calling document.open() on XHTML
1241
0
    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1242
0
    return nullptr;
1243
0
  }
1244
0
1245
0
  if (ShouldThrowOnDynamicMarkupInsertion()) {
1246
0
    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1247
0
    return nullptr;
1248
0
  }
1249
0
1250
0
  // If we already have a parser we ignore the document.open call.
1251
0
  if (mParser || mParserAborted) {
1252
0
    // The WHATWG spec says: "If the document has an active parser that isn't
1253
0
    // a script-created parser, and the insertion point associated with that
1254
0
    // parser's input stream is not undefined (that is, it does point to
1255
0
    // somewhere in the input stream), then the method does nothing. Abort
1256
0
    // these steps and return the Document object on which the method was
1257
0
    // invoked."
1258
0
    // Note that aborting a parser leaves the parser "active" with its
1259
0
    // insertion point "not undefined". We track this using mParserAborted,
1260
0
    // because aborting a parser nulls out mParser.
1261
0
    nsCOMPtr<nsIDocument> ret = this;
1262
0
    return ret.forget();
1263
0
  }
1264
0
1265
0
  // Implement Step 6 of:
1266
0
  // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps
1267
0
  if (ShouldIgnoreOpens()) {
1268
0
    nsCOMPtr<nsIDocument> ret = this;
1269
0
    return ret.forget();
1270
0
  }
1271
0
1272
0
  // No calling document.open() without a script global object
1273
0
  if (!mScriptGlobalObject) {
1274
0
    nsCOMPtr<nsIDocument> ret = this;
1275
0
    return ret.forget();
1276
0
  }
1277
0
1278
0
  nsPIDOMWindowOuter* outer = GetWindow();
1279
0
  if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
1280
0
    nsCOMPtr<nsIDocument> ret = this;
1281
0
    return ret.forget();
1282
0
  }
1283
0
1284
0
  // check whether we're in the middle of unload.  If so, ignore this call.
1285
0
  nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
1286
0
  if (!shell) {
1287
0
    // We won't be able to create a parser anyway.
1288
0
    nsCOMPtr<nsIDocument> ret = this;
1289
0
    return ret.forget();
1290
0
  }
1291
0
1292
0
  bool inUnload;
1293
0
  shell->GetIsInUnload(&inUnload);
1294
0
  if (inUnload) {
1295
0
    nsCOMPtr<nsIDocument> ret = this;
1296
0
    return ret.forget();
1297
0
  }
1298
0
1299
0
  // Note: We want to use GetEntryDocument here because this document
1300
0
  // should inherit the security information of the document that's opening us,
1301
0
  // (since if it's secure, then it's presumably trusted).
1302
0
  nsCOMPtr<nsIDocument> callerDoc = GetEntryDocument();
1303
0
  if (!callerDoc) {
1304
0
    // If we're called from C++ or in some other way without an originating
1305
0
    // document we can't do a document.open w/o changing the principal of the
1306
0
    // document to something like about:blank (as that's the only sane thing to
1307
0
    // do when we don't know the origin of this call), and since we can't
1308
0
    // change the principals of a document for security reasons we'll have to
1309
0
    // refuse to go ahead with this call.
1310
0
1311
0
    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
1312
0
    return nullptr;
1313
0
  }
1314
0
1315
0
  // Grab a reference to the calling documents security info (if any)
1316
0
  // and URIs as they may be lost in the call to Reset().
1317
0
  nsCOMPtr<nsISupports> securityInfo = callerDoc->GetSecurityInfo();
1318
0
  nsCOMPtr<nsIURI> uri = callerDoc->GetDocumentURI();
1319
0
  nsCOMPtr<nsIURI> baseURI = callerDoc->GetBaseURI();
1320
0
  nsCOMPtr<nsIPrincipal> callerPrincipal = callerDoc->NodePrincipal();
1321
0
  nsCOMPtr<nsIChannel> callerChannel = callerDoc->GetChannel();
1322
0
1323
0
  // We're called from script. Make sure the script is from the same
1324
0
  // origin, not just that the caller can access the document. This is
1325
0
  // needed to keep document principals from ever changing, which is
1326
0
  // needed because of the way we use our XOW code, and is a sane
1327
0
  // thing to do anyways.
1328
0
1329
0
  bool equals = false;
1330
0
  if (NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &equals)) ||
1331
0
      !equals) {
1332
0
1333
#ifdef DEBUG
1334
    nsCOMPtr<nsIURI> callerDocURI = callerDoc->GetDocumentURI();
1335
    nsCOMPtr<nsIURI> thisURI = nsIDocument::GetDocumentURI();
1336
    printf("nsHTMLDocument::Open callerDoc %s this %s\n",
1337
           callerDocURI ? callerDocURI->GetSpecOrDefault().get() : "",
1338
           thisURI ? thisURI->GetSpecOrDefault().get() : "");
1339
#endif
1340
1341
0
    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
1342
0
    return nullptr;
1343
0
  }
1344
0
1345
0
  // At this point we know this is a valid-enough document.open() call
1346
0
  // and not a no-op.  Increment our use counters.
1347
0
  SetDocumentAndPageUseCounter(eUseCounter_custom_DocumentOpen);
1348
0
  bool isReplace = aReplace.LowerCaseEqualsLiteral("replace");
1349
0
  if (isReplace) {
1350
0
    SetDocumentAndPageUseCounter(eUseCounter_custom_DocumentOpenReplace);
1351
0
  }
1352
0
1353
0
  // Stop current loads targeted at the window this document is in.
1354
0
  if (mScriptGlobalObject) {
1355
0
    nsCOMPtr<nsIContentViewer> cv;
1356
0
    shell->GetContentViewer(getter_AddRefs(cv));
1357
0
1358
0
    if (cv) {
1359
0
      bool okToUnload;
1360
0
      if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
1361
0
        // We don't want to unload, so stop here, but don't throw an
1362
0
        // exception.
1363
0
        nsCOMPtr<nsIDocument> ret = this;
1364
0
        return ret.forget();
1365
0
      }
1366
0
1367
0
      // Now double-check that our invariants still hold.
1368
0
      if (!mScriptGlobalObject) {
1369
0
        nsCOMPtr<nsIDocument> ret = this;
1370
0
        return ret.forget();
1371
0
      }
1372
0
1373
0
      nsPIDOMWindowOuter* outer = GetWindow();
1374
0
      if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
1375
0
        nsCOMPtr<nsIDocument> ret = this;
1376
0
        return ret.forget();
1377
0
      }
1378
0
    }
1379
0
1380
0
    nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
1381
0
    webnav->Stop(nsIWebNavigation::STOP_NETWORK);
1382
0
1383
0
    // The Stop call may have cancelled the onload blocker request or prevented
1384
0
    // it from getting added, so we need to make sure it gets added to the
1385
0
    // document again otherwise the document could have a non-zero onload block
1386
0
    // count without the onload blocker request being in the loadgroup.
1387
0
    EnsureOnloadBlocker();
1388
0
  }
1389
0
1390
0
  // The open occurred after the document finished loading.
1391
0
  // So we reset the document and then reinitialize it.
1392
0
  nsCOMPtr<nsIDocShell> curDocShell = GetDocShell();
1393
0
  nsCOMPtr<nsIDocShellTreeItem> parent;
1394
0
  if (curDocShell) {
1395
0
    curDocShell->GetSameTypeParent(getter_AddRefs(parent));
1396
0
  }
1397
0
1398
0
  // We are using the same technique as in nsDocShell to figure
1399
0
  // out the content policy type. If there is no same type parent,
1400
0
  // we know we are loading a new top level document.
1401
0
  nsContentPolicyType policyType;
1402
0
  if (!parent) {
1403
0
    policyType = nsIContentPolicy::TYPE_DOCUMENT;
1404
0
  } else {
1405
0
    Element* requestingElement = nullptr;
1406
0
    nsPIDOMWindowInner* window = GetInnerWindow();
1407
0
    if (window) {
1408
0
      nsPIDOMWindowOuter* outer =
1409
0
        nsPIDOMWindowOuter::GetFromCurrentInner(window);
1410
0
      if (outer) {
1411
0
        nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(outer);
1412
0
        requestingElement = win->AsOuter()->GetFrameElementInternal();
1413
0
      }
1414
0
    }
1415
0
    if (requestingElement) {
1416
0
      policyType = requestingElement->IsHTMLElement(nsGkAtoms::iframe) ?
1417
0
        nsIContentPolicy::TYPE_INTERNAL_IFRAME : nsIContentPolicy::TYPE_INTERNAL_FRAME;
1418
0
    } else {
1419
0
      // If we have lost our frame element by now, just assume we're
1420
0
      // an iframe since that's more common.
1421
0
      policyType = nsIContentPolicy::TYPE_INTERNAL_IFRAME;
1422
0
    }
1423
0
  }
1424
0
1425
0
  nsCOMPtr<nsIChannel> channel;
1426
0
  nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
1427
0
  aError = NS_NewChannel(getter_AddRefs(channel),
1428
0
                         uri,
1429
0
                         callerDoc,
1430
0
                         nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
1431
0
                         policyType,
1432
0
                         nullptr, // PerformanceStorage
1433
0
                         group);
1434
0
1435
0
  if (aError.Failed()) {
1436
0
    return nullptr;
1437
0
  }
1438
0
1439
0
  if (callerChannel) {
1440
0
    nsLoadFlags callerLoadFlags;
1441
0
    aError = callerChannel->GetLoadFlags(&callerLoadFlags);
1442
0
    if (aError.Failed()) {
1443
0
      return nullptr;
1444
0
    }
1445
0
1446
0
    nsLoadFlags loadFlags;
1447
0
    aError = channel->GetLoadFlags(&loadFlags);
1448
0
    if (aError.Failed()) {
1449
0
      return nullptr;
1450
0
    }
1451
0
1452
0
    loadFlags |= callerLoadFlags & nsIRequest::INHIBIT_PERSISTENT_CACHING;
1453
0
1454
0
    aError = channel->SetLoadFlags(loadFlags);
1455
0
    if (aError.Failed()) {
1456
0
      return nullptr;
1457
0
    }
1458
0
1459
0
    // If the user has allowed mixed content on the rootDoc, then we should propogate it
1460
0
    // down to the new document channel.
1461
0
    bool rootHasSecureConnection = false;
1462
0
    bool allowMixedContent = false;
1463
0
    bool isDocShellRoot = false;
1464
0
    nsresult rvalue = shell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isDocShellRoot);
1465
0
    if (NS_SUCCEEDED(rvalue) && allowMixedContent && isDocShellRoot) {
1466
0
       shell->SetMixedContentChannel(channel);
1467
0
    }
1468
0
  }
1469
0
1470
0
  // Before we reset the doc notify the globalwindow of the change,
1471
0
  // but only if we still have a window (i.e. our window object the
1472
0
  // current inner window in our outer window).
1473
0
1474
0
  // Hold onto ourselves on the offchance that we're down to one ref
1475
0
  nsCOMPtr<nsIDocument> kungFuDeathGrip = this;
1476
0
1477
0
  if (nsPIDOMWindowInner *window = GetInnerWindow()) {
1478
0
    // Remember the old scope in case the call to SetNewDocument changes it.
1479
0
    nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject));
1480
0
1481
#ifdef DEBUG
1482
    bool willReparent = mWillReparent;
1483
    mWillReparent = true;
1484
1485
    nsIDocument* templateContentsOwner = mTemplateContentsOwner.get();
1486
1487
    if (templateContentsOwner) {
1488
      templateContentsOwner->mWillReparent = true;
1489
    }
1490
#endif
1491
1492
0
    // Set our ready state to uninitialized before setting the new document so
1493
0
    // that window creation listeners don't use the document in its intermediate
1494
0
    // state prior to reset.
1495
0
    SetReadyStateInternal(READYSTATE_UNINITIALIZED);
1496
0
1497
0
    // Per spec, we pass false here so that a new Window is created.
1498
0
    aError = window->SetNewDocument(this, nullptr,
1499
0
                                    /* aForceReuseInnerWindow */ false);
1500
0
    if (aError.Failed()) {
1501
0
      return nullptr;
1502
0
    }
1503
0
1504
#ifdef DEBUG
1505
    if (templateContentsOwner) {
1506
      templateContentsOwner->mWillReparent = willReparent;
1507
    }
1508
1509
    mWillReparent = willReparent;
1510
#endif
1511
1512
0
    // Now make sure we're not flagged as the initial document anymore, now
1513
0
    // that we've had stuff done to us.  From now on, if anyone tries to
1514
0
    // document.open() us, they get a new inner window.
1515
0
    SetIsInitialDocument(false);
1516
0
1517
0
    nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
1518
0
    JS::Rooted<JSObject*> wrapper(cx, GetWrapper());
1519
0
    if (oldScope && newScope != oldScope && wrapper) {
1520
0
      JSAutoRealm ar(cx, wrapper);
1521
0
      mozilla::dom::ReparentWrapper(cx, wrapper, aError);
1522
0
      if (aError.Failed()) {
1523
0
        return nullptr;
1524
0
      }
1525
0
1526
0
      // Also reparent the template contents owner document
1527
0
      // because its global is set to the same as this document.
1528
0
      if (mTemplateContentsOwner) {
1529
0
        JS::Rooted<JSObject*> contentsOwnerWrapper(cx,
1530
0
          mTemplateContentsOwner->GetWrapper());
1531
0
        if (contentsOwnerWrapper) {
1532
0
          mozilla::dom::ReparentWrapper(cx, contentsOwnerWrapper, aError);
1533
0
          if (aError.Failed()) {
1534
0
            return nullptr;
1535
0
          }
1536
0
        }
1537
0
      }
1538
0
    }
1539
0
  }
1540
0
1541
0
  mDidDocumentOpen = true;
1542
0
1543
0
  nsAutoCString contentType(GetContentTypeInternal());
1544
0
1545
0
  // Call Reset(), this will now do the full reset
1546
0
  Reset(channel, group);
1547
0
  if (baseURI) {
1548
0
    mDocumentBaseURI = baseURI;
1549
0
  }
1550
0
1551
0
  // Restore our type, since Reset() resets it.
1552
0
  SetContentTypeInternal(contentType);
1553
0
1554
0
  // Store the security info of the caller now that we're done
1555
0
  // resetting the document.
1556
0
  mSecurityInfo = securityInfo;
1557
0
1558
0
  mParserAborted = false;
1559
0
  mParser = nsHtml5Module::NewHtml5Parser();
1560
0
  nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
1561
0
  if (mReferrerPolicySet) {
1562
0
    // CSP may have set the referrer policy, so a speculative parser should
1563
0
    // start with the new referrer policy.
1564
0
    nsHtml5TreeOpExecutor* executor = nullptr;
1565
0
    executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
1566
0
    if (executor && mReferrerPolicySet) {
1567
0
      executor->SetSpeculationReferrerPolicy(static_cast<ReferrerPolicy>(mReferrerPolicy));
1568
0
    }
1569
0
  }
1570
0
1571
0
  mContentTypeForWriteCalls.AssignLiteral("text/html");
1572
0
1573
0
  // Prepare the docshell and the document viewer for the impending
1574
0
  // out of band document.write()
1575
0
  shell->PrepareForNewContentModel();
1576
0
1577
0
  // Now check whether we were opened with a "replace" argument.  If
1578
0
  // so, we need to tell the docshell to not create a new history
1579
0
  // entry for this load. Otherwise, make sure that we're doing a normal load,
1580
0
  // not whatever type of load was previously done on this docshell.
1581
0
  shell->SetLoadType(isReplace ? LOAD_NORMAL_REPLACE : LOAD_NORMAL);
1582
0
1583
0
  nsCOMPtr<nsIContentViewer> cv;
1584
0
  shell->GetContentViewer(getter_AddRefs(cv));
1585
0
  if (cv) {
1586
0
    cv->LoadStart(this);
1587
0
  }
1588
0
1589
0
  // Add a wyciwyg channel request into the document load group
1590
0
  NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Open(): wyciwyg "
1591
0
               "channel already exists!");
1592
0
1593
0
  // In case the editor is listening and will see the new channel
1594
0
  // being added, make sure mWriteLevel is non-zero so that the editor
1595
0
  // knows that document.open/write/close() is being called on this
1596
0
  // document.
1597
0
  ++mWriteLevel;
1598
0
1599
0
  CreateAndAddWyciwygChannel();
1600
0
1601
0
  --mWriteLevel;
1602
0
1603
0
  SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
1604
0
1605
0
  // After changing everything around, make sure that the principal on the
1606
0
  // document's realm exactly matches NodePrincipal().
1607
0
  DebugOnly<JSObject*> wrapper = GetWrapperPreserveColor();
1608
0
  MOZ_ASSERT_IF(wrapper,
1609
0
                JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(wrapper)) ==
1610
0
                nsJSPrincipals::get(NodePrincipal()));
1611
0
1612
0
  return kungFuDeathGrip.forget();
1613
0
}
1614
1615
void
1616
nsHTMLDocument::Close(ErrorResult& rv)
1617
0
{
1618
0
  if (!IsHTMLDocument()) {
1619
0
    // No calling document.close() on XHTML!
1620
0
1621
0
    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1622
0
    return;
1623
0
  }
1624
0
1625
0
  if (ShouldThrowOnDynamicMarkupInsertion()) {
1626
0
    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1627
0
    return;
1628
0
  }
1629
0
1630
0
  if (!mParser || !mParser->IsScriptCreated()) {
1631
0
    return;
1632
0
  }
1633
0
1634
0
  ++mWriteLevel;
1635
0
  rv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
1636
0
    EmptyString(), nullptr, mContentTypeForWriteCalls, true);
1637
0
  --mWriteLevel;
1638
0
1639
0
  // Even if that Parse() call failed, do the rest of this method
1640
0
1641
0
  // XXX Make sure that all the document.written content is
1642
0
  // reflowed.  We should remove this call once we change
1643
0
  // nsHTMLDocument::OpenCommon() so that it completely destroys the
1644
0
  // earlier document's content and frame hierarchy.  Right now, it
1645
0
  // re-uses the earlier document's root content object and
1646
0
  // corresponding frame objects.  These re-used frame objects think
1647
0
  // that they have already been reflowed, so they drop initial
1648
0
  // reflows.  For certain cases of document.written content, like a
1649
0
  // frameset document, the dropping of the initial reflow means
1650
0
  // that we end up in document.close() without appended any reflow
1651
0
  // commands to the reflow queue and, consequently, without adding
1652
0
  // the dummy layout request to the load group.  Since the dummy
1653
0
  // layout request is not added to the load group, the onload
1654
0
  // handler of the frameset fires before the frames get reflowed
1655
0
  // and loaded.  That is the long explanation for why we need this
1656
0
  // one line of code here!
1657
0
  // XXXbz as far as I can tell this may not be needed anymore; all
1658
0
  // the testcases in bug 57636 pass without this line...  Leaving
1659
0
  // it be for now, though.  In any case, there's no reason to do
1660
0
  // this if we have no presshell, since in that case none of the
1661
0
  // above about reusing frames applies.
1662
0
  //
1663
0
  // XXXhsivonen keeping this around for bug 577508 / 253951 still :-(
1664
0
  if (GetShell()) {
1665
0
    FlushPendingNotifications(FlushType::Layout);
1666
0
  }
1667
0
1668
0
  // Removing the wyciwygChannel here is wrong when document.close() is
1669
0
  // called from within the document itself. However, legacy requires the
1670
0
  // channel to be removed here. Otherwise, the load event never fires.
1671
0
  NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::Close(): Trying to remove "
1672
0
               "nonexistent wyciwyg channel!");
1673
0
  RemoveWyciwygChannel();
1674
0
  NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Close(): "
1675
0
               "nsIWyciwygChannel could not be removed!");
1676
0
}
1677
1678
void
1679
nsHTMLDocument::WriteCommon(JSContext *cx,
1680
                            const Sequence<nsString>& aText,
1681
                            bool aNewlineTerminate,
1682
                            mozilla::ErrorResult& rv)
1683
0
{
1684
0
  // Fast path the common case
1685
0
  if (aText.Length() == 1) {
1686
0
    WriteCommon(cx, aText[0], aNewlineTerminate, rv);
1687
0
  } else {
1688
0
    // XXXbz it would be nice if we could pass all the strings to the parser
1689
0
    // without having to do all this copying and then ask it to start
1690
0
    // parsing....
1691
0
    nsString text;
1692
0
    for (uint32_t i = 0; i < aText.Length(); ++i) {
1693
0
      text.Append(aText[i]);
1694
0
    }
1695
0
    WriteCommon(cx, text, aNewlineTerminate, rv);
1696
0
  }
1697
0
}
1698
1699
void
1700
nsHTMLDocument::WriteCommon(JSContext *cx,
1701
                            const nsAString& aText,
1702
                            bool aNewlineTerminate,
1703
                            ErrorResult& aRv)
1704
0
{
1705
0
  mTooDeepWriteRecursion =
1706
0
    (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
1707
0
  if (NS_WARN_IF(mTooDeepWriteRecursion)) {
1708
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
1709
0
    return;
1710
0
  }
1711
0
1712
0
  if (!IsHTMLDocument() || mDisableDocWrite) {
1713
0
    // No calling document.write*() on XHTML!
1714
0
1715
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1716
0
    return;
1717
0
  }
1718
0
1719
0
1720
0
  if (ShouldThrowOnDynamicMarkupInsertion()) {
1721
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1722
0
    return;
1723
0
  }
1724
0
1725
0
  if (mParserAborted) {
1726
0
    // Hixie says aborting the parser doesn't undefine the insertion point.
1727
0
    // However, since we null out mParser in that case, we track the
1728
0
    // theoretically defined insertion point using mParserAborted.
1729
0
    return;
1730
0
  }
1731
0
1732
0
  // Implement Step 4.1 of:
1733
0
  // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
1734
0
  if (ShouldIgnoreOpens()) {
1735
0
    return;
1736
0
  }
1737
0
1738
0
  void *key = GenerateParserKey();
1739
0
  if (mParser && !mParser->IsInsertionPointDefined()) {
1740
0
    if (mIgnoreDestructiveWritesCounter) {
1741
0
      // Instead of implying a call to document.open(), ignore the call.
1742
0
      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1743
0
                                      NS_LITERAL_CSTRING("DOM Events"), this,
1744
0
                                      nsContentUtils::eDOM_PROPERTIES,
1745
0
                                      "DocumentWriteIgnored",
1746
0
                                      nullptr, 0,
1747
0
                                      mDocumentURI);
1748
0
      return;
1749
0
    }
1750
0
    // The spec doesn't tell us to ignore opens from here, but we need to
1751
0
    // ensure opens are ignored here.
1752
0
    IgnoreOpensDuringUnload ignoreOpenGuard(this);
1753
0
    mParser->Terminate();
1754
0
    MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
1755
0
  }
1756
0
1757
0
  if (!mParser) {
1758
0
    if (mIgnoreDestructiveWritesCounter) {
1759
0
      // Instead of implying a call to document.open(), ignore the call.
1760
0
      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1761
0
                                      NS_LITERAL_CSTRING("DOM Events"), this,
1762
0
                                      nsContentUtils::eDOM_PROPERTIES,
1763
0
                                      "DocumentWriteIgnored",
1764
0
                                      nullptr, 0,
1765
0
                                      mDocumentURI);
1766
0
      return;
1767
0
    }
1768
0
    nsCOMPtr<nsIDocument> ignored  = Open(cx, Optional<nsAString>(),
1769
0
                                          EmptyString(), aRv);
1770
0
1771
0
    // If Open() fails, or if it didn't create a parser (as it won't
1772
0
    // if the user chose to not discard the current document through
1773
0
    // onbeforeunload), don't write anything.
1774
0
    if (aRv.Failed() || !mParser) {
1775
0
      return;
1776
0
    }
1777
0
    MOZ_ASSERT(!JS_IsExceptionPending(cx),
1778
0
               "Open() succeeded but JS exception is pending");
1779
0
  }
1780
0
1781
0
  static NS_NAMED_LITERAL_STRING(new_line, "\n");
1782
0
1783
0
  // Save the data in cache if the write isn't from within the doc
1784
0
  if (mWyciwygChannel && !key) {
1785
0
    if (!aText.IsEmpty()) {
1786
0
      mWyciwygChannel->WriteToCacheEntry(aText);
1787
0
    }
1788
0
1789
0
    if (aNewlineTerminate) {
1790
0
      mWyciwygChannel->WriteToCacheEntry(new_line);
1791
0
    }
1792
0
  }
1793
0
1794
0
  ++mWriteLevel;
1795
0
1796
0
  // This could be done with less code, but for performance reasons it
1797
0
  // makes sense to have the code for two separate Parse() calls here
1798
0
  // since the concatenation of strings costs more than we like. And
1799
0
  // why pay that price when we don't need to?
1800
0
  if (aNewlineTerminate) {
1801
0
    aRv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
1802
0
      aText + new_line, key, mContentTypeForWriteCalls, false);
1803
0
  } else {
1804
0
    aRv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
1805
0
      aText, key, mContentTypeForWriteCalls, false);
1806
0
  }
1807
0
1808
0
  --mWriteLevel;
1809
0
1810
0
  mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
1811
0
}
1812
1813
void
1814
nsHTMLDocument::Write(JSContext* cx, const Sequence<nsString>& aText,
1815
                      ErrorResult& rv)
1816
0
{
1817
0
  WriteCommon(cx, aText, false, rv);
1818
0
}
1819
1820
void
1821
nsHTMLDocument::Writeln(JSContext* cx, const Sequence<nsString>& aText,
1822
                        ErrorResult& rv)
1823
0
{
1824
0
  WriteCommon(cx, aText, true, rv);
1825
0
}
1826
1827
void
1828
nsHTMLDocument::AddedForm()
1829
0
{
1830
0
  ++mNumForms;
1831
0
}
1832
1833
void
1834
nsHTMLDocument::RemovedForm()
1835
0
{
1836
0
  --mNumForms;
1837
0
}
1838
1839
int32_t
1840
nsHTMLDocument::GetNumFormsSynchronous()
1841
0
{
1842
0
  return mNumForms;
1843
0
}
1844
1845
void
1846
nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor)
1847
0
{
1848
0
  aAlinkColor.Truncate();
1849
0
1850
0
  HTMLBodyElement* body = GetBodyElement();
1851
0
  if (body) {
1852
0
    body->GetALink(aAlinkColor);
1853
0
  }
1854
0
}
1855
1856
void
1857
nsHTMLDocument::SetAlinkColor(const nsAString& aAlinkColor)
1858
0
{
1859
0
  HTMLBodyElement* body = GetBodyElement();
1860
0
  if (body) {
1861
0
    body->SetALink(aAlinkColor);
1862
0
  }
1863
0
}
1864
1865
void
1866
nsHTMLDocument::GetLinkColor(nsAString& aLinkColor)
1867
0
{
1868
0
  aLinkColor.Truncate();
1869
0
1870
0
  HTMLBodyElement* body = GetBodyElement();
1871
0
  if (body) {
1872
0
    body->GetLink(aLinkColor);
1873
0
  }
1874
0
}
1875
1876
void
1877
nsHTMLDocument::SetLinkColor(const nsAString& aLinkColor)
1878
0
{
1879
0
  HTMLBodyElement* body = GetBodyElement();
1880
0
  if (body) {
1881
0
    body->SetLink(aLinkColor);
1882
0
  }
1883
0
}
1884
1885
void
1886
nsHTMLDocument::GetVlinkColor(nsAString& aVlinkColor)
1887
0
{
1888
0
  aVlinkColor.Truncate();
1889
0
1890
0
  HTMLBodyElement* body = GetBodyElement();
1891
0
  if (body) {
1892
0
    body->GetVLink(aVlinkColor);
1893
0
  }
1894
0
}
1895
1896
void
1897
nsHTMLDocument::SetVlinkColor(const nsAString& aVlinkColor)
1898
0
{
1899
0
  HTMLBodyElement* body = GetBodyElement();
1900
0
  if (body) {
1901
0
    body->SetVLink(aVlinkColor);
1902
0
  }
1903
0
}
1904
1905
void
1906
nsHTMLDocument::GetBgColor(nsAString& aBgColor)
1907
0
{
1908
0
  aBgColor.Truncate();
1909
0
1910
0
  HTMLBodyElement* body = GetBodyElement();
1911
0
  if (body) {
1912
0
    body->GetBgColor(aBgColor);
1913
0
  }
1914
0
}
1915
1916
void
1917
nsHTMLDocument::SetBgColor(const nsAString& aBgColor)
1918
0
{
1919
0
  HTMLBodyElement* body = GetBodyElement();
1920
0
  if (body) {
1921
0
    body->SetBgColor(aBgColor);
1922
0
  }
1923
0
}
1924
1925
void
1926
nsHTMLDocument::GetFgColor(nsAString& aFgColor)
1927
0
{
1928
0
  aFgColor.Truncate();
1929
0
1930
0
  HTMLBodyElement* body = GetBodyElement();
1931
0
  if (body) {
1932
0
    body->GetText(aFgColor);
1933
0
  }
1934
0
}
1935
1936
void
1937
nsHTMLDocument::SetFgColor(const nsAString& aFgColor)
1938
0
{
1939
0
  HTMLBodyElement* body = GetBodyElement();
1940
0
  if (body) {
1941
0
    body->SetText(aFgColor);
1942
0
  }
1943
0
}
1944
1945
void
1946
nsHTMLDocument::CaptureEvents()
1947
0
{
1948
0
  WarnOnceAbout(nsIDocument::eUseOfCaptureEvents);
1949
0
}
1950
1951
void
1952
nsHTMLDocument::ReleaseEvents()
1953
0
{
1954
0
  WarnOnceAbout(nsIDocument::eUseOfReleaseEvents);
1955
0
}
1956
1957
bool
1958
nsHTMLDocument::ResolveName(JSContext* aCx, const nsAString& aName,
1959
                            JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
1960
0
{
1961
0
  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
1962
0
  if (!entry) {
1963
0
    return false;
1964
0
  }
1965
0
1966
0
  nsBaseContentList *list = entry->GetNameContentList();
1967
0
  uint32_t length = list ? list->Length() : 0;
1968
0
1969
0
  nsIContent *node;
1970
0
  if (length > 0) {
1971
0
    if (length > 1) {
1972
0
      // The list contains more than one element, return the whole list.
1973
0
      if (!ToJSValue(aCx, list, aRetval)) {
1974
0
        aError.NoteJSContextException(aCx);
1975
0
        return false;
1976
0
      }
1977
0
      return true;
1978
0
    }
1979
0
1980
0
    // Only one element in the list, return the element instead of returning
1981
0
    // the list.
1982
0
    node = list->Item(0);
1983
0
  } else {
1984
0
    // No named items were found, see if there's one registerd by id for aName.
1985
0
    Element *e = entry->GetIdElement();
1986
0
1987
0
    if (!e || !nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(e)) {
1988
0
      return false;
1989
0
    }
1990
0
1991
0
    node = e;
1992
0
  }
1993
0
1994
0
  if (!ToJSValue(aCx, node, aRetval)) {
1995
0
    aError.NoteJSContextException(aCx);
1996
0
    return false;
1997
0
  }
1998
0
1999
0
  return true;
2000
0
}
2001
2002
void
2003
nsHTMLDocument::GetSupportedNames(nsTArray<nsString>& aNames)
2004
0
{
2005
0
  for (auto iter = mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
2006
0
    nsIdentifierMapEntry* entry = iter.Get();
2007
0
    if (entry->HasNameElement() ||
2008
0
        entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2009
0
      aNames.AppendElement(entry->GetKeyAsString());
2010
0
    }
2011
0
  }
2012
0
}
2013
2014
//----------------------------
2015
2016
// forms related stuff
2017
2018
bool
2019
nsHTMLDocument::MatchFormControls(Element* aElement, int32_t aNamespaceID,
2020
                                  nsAtom* aAtom, void* aData)
2021
0
{
2022
0
  return aElement->IsNodeOfType(nsIContent::eHTML_FORM_CONTROL);
2023
0
}
2024
2025
nsresult
2026
nsHTMLDocument::CreateAndAddWyciwygChannel(void)
2027
0
{
2028
0
  nsresult rv = NS_OK;
2029
0
  nsAutoCString url, originalSpec;
2030
0
2031
0
  mDocumentURI->GetSpec(originalSpec);
2032
0
2033
0
  // Generate the wyciwyg url
2034
0
  url = NS_LITERAL_CSTRING("wyciwyg://")
2035
0
      + nsPrintfCString("%d", gWyciwygSessionCnt++)
2036
0
      + NS_LITERAL_CSTRING("/")
2037
0
      + originalSpec;
2038
0
2039
0
  nsCOMPtr<nsIURI> wcwgURI;
2040
0
  NS_NewURI(getter_AddRefs(wcwgURI), url);
2041
0
2042
0
  // Create the nsIWyciwygChannel to store out-of-band
2043
0
  // document.write() script to cache
2044
0
  nsCOMPtr<nsIChannel> channel;
2045
0
  // Create a wyciwyg Channel
2046
0
  rv = NS_NewChannel(getter_AddRefs(channel),
2047
0
                     wcwgURI,
2048
0
                     NodePrincipal(),
2049
0
                     nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
2050
0
                     nsIContentPolicy::TYPE_OTHER);
2051
0
  NS_ENSURE_SUCCESS(rv, rv);
2052
0
  nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
2053
0
  NS_ENSURE_STATE(loadInfo);
2054
0
  loadInfo->SetPrincipalToInherit(NodePrincipal());
2055
0
2056
0
2057
0
  mWyciwygChannel = do_QueryInterface(channel);
2058
0
2059
0
  mWyciwygChannel->SetSecurityInfo(mSecurityInfo);
2060
0
2061
0
  // Note: we want to treat this like a "previous document" hint so that,
2062
0
  // e.g. a <meta> tag in the document.write content can override it.
2063
0
  SetDocumentCharacterSetSource(kCharsetFromHintPrevDoc);
2064
0
  nsAutoCString charset;
2065
0
  GetDocumentCharacterSet()->Name(charset);
2066
0
  mWyciwygChannel->SetCharsetAndSource(kCharsetFromHintPrevDoc, charset);
2067
0
2068
0
  // Inherit load flags from the original document's channel
2069
0
  channel->SetLoadFlags(mLoadFlags);
2070
0
2071
0
  nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
2072
0
2073
0
  // Use the Parent document's loadgroup to trigger load notifications
2074
0
  if (loadGroup && channel) {
2075
0
    rv = channel->SetLoadGroup(loadGroup);
2076
0
    NS_ENSURE_SUCCESS(rv, rv);
2077
0
2078
0
    nsLoadFlags loadFlags = 0;
2079
0
    channel->GetLoadFlags(&loadFlags);
2080
0
    loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
2081
0
    if (nsDocShell::SandboxFlagsImplyCookies(mSandboxFlags)) {
2082
0
      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
2083
0
    }
2084
0
    channel->SetLoadFlags(loadFlags);
2085
0
2086
0
    channel->SetOriginalURI(wcwgURI);
2087
0
2088
0
    rv = loadGroup->AddRequest(mWyciwygChannel, nullptr);
2089
0
    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to add request to load group.");
2090
0
  }
2091
0
2092
0
  return rv;
2093
0
}
2094
2095
nsresult
2096
nsHTMLDocument::RemoveWyciwygChannel(void)
2097
0
{
2098
0
  nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
2099
0
2100
0
  // note there can be a write request without a load group if
2101
0
  // this is a synchronously constructed about:blank document
2102
0
  if (loadGroup && mWyciwygChannel) {
2103
0
    mWyciwygChannel->CloseCacheEntry(NS_OK);
2104
0
    loadGroup->RemoveRequest(mWyciwygChannel, nullptr, NS_OK);
2105
0
  }
2106
0
2107
0
  mWyciwygChannel = nullptr;
2108
0
2109
0
  return NS_OK;
2110
0
}
2111
2112
void *
2113
nsHTMLDocument::GenerateParserKey(void)
2114
0
{
2115
0
  if (!mScriptLoader) {
2116
0
    // If we don't have a script loader, then the parser probably isn't parsing
2117
0
    // anything anyway, so just return null.
2118
0
    return nullptr;
2119
0
  }
2120
0
2121
0
  // The script loader provides us with the currently executing script element,
2122
0
  // which is guaranteed to be unique per script.
2123
0
  nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
2124
0
  if (script && mParser && mParser->IsScriptCreated()) {
2125
0
    nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
2126
0
    if (creatorParser != mParser) {
2127
0
      // Make scripts that aren't inserted by the active parser of this document
2128
0
      // participate in the context of the script that document.open()ed
2129
0
      // this document.
2130
0
      return nullptr;
2131
0
    }
2132
0
  }
2133
0
  return script;
2134
0
}
2135
2136
void
2137
nsHTMLDocument::GetDesignMode(nsAString& aDesignMode)
2138
0
{
2139
0
  if (HasFlag(NODE_IS_EDITABLE)) {
2140
0
    aDesignMode.AssignLiteral("on");
2141
0
  }
2142
0
  else {
2143
0
    aDesignMode.AssignLiteral("off");
2144
0
  }
2145
0
}
2146
2147
void
2148
nsHTMLDocument::MaybeEditingStateChanged()
2149
0
{
2150
0
  if (!mPendingMaybeEditingStateChanged && mMayStartLayout &&
2151
0
      mUpdateNestLevel == 0 && (mContentEditableCount > 0) != IsEditingOn()) {
2152
0
    if (nsContentUtils::IsSafeToRunScript()) {
2153
0
      EditingStateChanged();
2154
0
    } else if (!mInDestructor) {
2155
0
      nsContentUtils::AddScriptRunner(
2156
0
        NewRunnableMethod("nsHTMLDocument::MaybeEditingStateChanged",
2157
0
                          this,
2158
0
                          &nsHTMLDocument::MaybeEditingStateChanged));
2159
0
    }
2160
0
  }
2161
0
}
2162
2163
void
2164
nsHTMLDocument::EndUpdate()
2165
0
{
2166
0
  const bool reset = !mPendingMaybeEditingStateChanged;
2167
0
  mPendingMaybeEditingStateChanged = true;
2168
0
  nsDocument::EndUpdate();
2169
0
  if (reset) {
2170
0
    mPendingMaybeEditingStateChanged = false;
2171
0
  }
2172
0
  MaybeEditingStateChanged();
2173
0
}
2174
2175
void
2176
nsHTMLDocument::SetMayStartLayout(bool aMayStartLayout)
2177
0
{
2178
0
  nsIDocument::SetMayStartLayout(aMayStartLayout);
2179
0
2180
0
  MaybeEditingStateChanged();
2181
0
}
2182
2183
2184
2185
// Helper class, used below in ChangeContentEditableCount().
2186
class DeferredContentEditableCountChangeEvent : public Runnable
2187
{
2188
public:
2189
  DeferredContentEditableCountChangeEvent(nsHTMLDocument* aDoc,
2190
                                          nsIContent* aElement)
2191
    : mozilla::Runnable("DeferredContentEditableCountChangeEvent")
2192
    , mDoc(aDoc)
2193
    , mElement(aElement)
2194
0
  {
2195
0
  }
2196
2197
0
  NS_IMETHOD Run() override {
2198
0
    if (mElement && mElement->OwnerDoc() == mDoc) {
2199
0
      mDoc->DeferredContentEditableCountChange(mElement);
2200
0
    }
2201
0
    return NS_OK;
2202
0
  }
2203
2204
private:
2205
  RefPtr<nsHTMLDocument> mDoc;
2206
  nsCOMPtr<nsIContent> mElement;
2207
};
2208
2209
nsresult
2210
nsHTMLDocument::ChangeContentEditableCount(nsIContent *aElement,
2211
                                           int32_t aChange)
2212
0
{
2213
0
  NS_ASSERTION(int32_t(mContentEditableCount) + aChange >= 0,
2214
0
               "Trying to decrement too much.");
2215
0
2216
0
  mContentEditableCount += aChange;
2217
0
2218
0
  nsContentUtils::AddScriptRunner(
2219
0
    new DeferredContentEditableCountChangeEvent(this, aElement));
2220
0
2221
0
  return NS_OK;
2222
0
}
2223
2224
void
2225
nsHTMLDocument::DeferredContentEditableCountChange(nsIContent *aElement)
2226
0
{
2227
0
  if (mParser ||
2228
0
      (mUpdateNestLevel > 0 && (mContentEditableCount > 0) != IsEditingOn())) {
2229
0
    return;
2230
0
  }
2231
0
2232
0
  EditingState oldState = mEditingState;
2233
0
2234
0
  nsresult rv = EditingStateChanged();
2235
0
  NS_ENSURE_SUCCESS_VOID(rv);
2236
0
2237
0
  if (oldState == mEditingState && mEditingState == eContentEditable) {
2238
0
    // We just changed the contentEditable state of a node, we need to reset
2239
0
    // the spellchecking state of that node.
2240
0
    if (aElement) {
2241
0
      nsPIDOMWindowOuter *window = GetWindow();
2242
0
      if (!window)
2243
0
        return;
2244
0
2245
0
      nsIDocShell *docshell = window->GetDocShell();
2246
0
      if (!docshell)
2247
0
        return;
2248
0
2249
0
      RefPtr<HTMLEditor> htmlEditor = docshell->GetHTMLEditor();
2250
0
      if (htmlEditor) {
2251
0
        RefPtr<nsRange> range = new nsRange(aElement);
2252
0
        IgnoredErrorResult res;
2253
0
        range->SelectNode(*aElement, res);
2254
0
        if (res.Failed()) {
2255
0
          // The node might be detached from the document at this point,
2256
0
          // which would cause this call to fail.  In this case, we can
2257
0
          // safely ignore the contenteditable count change.
2258
0
          return;
2259
0
        }
2260
0
2261
0
        nsCOMPtr<nsIInlineSpellChecker> spellChecker;
2262
0
        rv = htmlEditor->GetInlineSpellChecker(false,
2263
0
                                               getter_AddRefs(spellChecker));
2264
0
        NS_ENSURE_SUCCESS_VOID(rv);
2265
0
2266
0
        if (spellChecker) {
2267
0
          rv = spellChecker->SpellCheckRange(range);
2268
0
        }
2269
0
      }
2270
0
    }
2271
0
  }
2272
0
}
2273
2274
HTMLAllCollection*
2275
nsHTMLDocument::All()
2276
0
{
2277
0
  if (!mAll) {
2278
0
    mAll = new HTMLAllCollection(this);
2279
0
  }
2280
0
  return mAll;
2281
0
}
2282
2283
static void
2284
NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument)
2285
0
{
2286
0
  for (nsIContent* child = aNode->GetFirstChild();
2287
0
       child;
2288
0
       child = child->GetNextSibling()) {
2289
0
    if (child->IsElement()) {
2290
0
      child->AsElement()->UpdateState(true);
2291
0
    }
2292
0
    NotifyEditableStateChange(child, aDocument);
2293
0
  }
2294
0
}
2295
2296
void
2297
nsHTMLDocument::TearingDownEditor()
2298
0
{
2299
0
  if (IsEditingOn()) {
2300
0
    EditingState oldState = mEditingState;
2301
0
    mEditingState = eTearingDown;
2302
0
2303
0
    nsCOMPtr<nsIPresShell> presShell = GetShell();
2304
0
    if (!presShell)
2305
0
      return;
2306
0
2307
0
    nsTArray<RefPtr<StyleSheet>> agentSheets;
2308
0
    presShell->GetAgentStyleSheets(agentSheets);
2309
0
2310
0
    auto cache = nsLayoutStylesheetCache::Singleton();
2311
0
2312
0
    agentSheets.RemoveElement(cache->ContentEditableSheet());
2313
0
    if (oldState == eDesignMode)
2314
0
      agentSheets.RemoveElement(cache->DesignModeSheet());
2315
0
2316
0
    presShell->SetAgentStyleSheets(agentSheets);
2317
0
2318
0
    presShell->ApplicableStylesChanged();
2319
0
  }
2320
0
}
2321
2322
nsresult
2323
nsHTMLDocument::TurnEditingOff()
2324
0
{
2325
0
  NS_ASSERTION(mEditingState != eOff, "Editing is already off.");
2326
0
2327
0
  nsPIDOMWindowOuter *window = GetWindow();
2328
0
  if (!window)
2329
0
    return NS_ERROR_FAILURE;
2330
0
2331
0
  nsIDocShell *docshell = window->GetDocShell();
2332
0
  if (!docshell)
2333
0
    return NS_ERROR_FAILURE;
2334
0
2335
0
  nsCOMPtr<nsIEditingSession> editSession;
2336
0
  nsresult rv = docshell->GetEditingSession(getter_AddRefs(editSession));
2337
0
  NS_ENSURE_SUCCESS(rv, rv);
2338
0
2339
0
  // turn editing off
2340
0
  rv = editSession->TearDownEditorOnWindow(window);
2341
0
  NS_ENSURE_SUCCESS(rv, rv);
2342
0
2343
0
  mEditingState = eOff;
2344
0
2345
0
  // Editor resets selection since it is being destroyed.  But if focus is
2346
0
  // still into editable control, we have to initialize selection again.
2347
0
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
2348
0
  if (fm) {
2349
0
    Element* element = fm->GetFocusedElement();
2350
0
    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(element);
2351
0
    if (txtCtrl) {
2352
0
      RefPtr<TextEditor> textEditor = txtCtrl->GetTextEditor();
2353
0
      if (textEditor) {
2354
0
        textEditor->ReinitializeSelection(*element);
2355
0
      }
2356
0
    }
2357
0
  }
2358
0
2359
0
  return NS_OK;
2360
0
}
2361
2362
static bool HasPresShell(nsPIDOMWindowOuter *aWindow)
2363
0
{
2364
0
  nsIDocShell *docShell = aWindow->GetDocShell();
2365
0
  if (!docShell)
2366
0
    return false;
2367
0
  return docShell->GetPresShell() != nullptr;
2368
0
}
2369
2370
nsresult
2371
nsHTMLDocument::SetEditingState(EditingState aState)
2372
0
{
2373
0
  mEditingState = aState;
2374
0
  return NS_OK;
2375
0
}
2376
2377
nsresult
2378
nsHTMLDocument::EditingStateChanged()
2379
0
{
2380
0
  if (mRemovedFromDocShell) {
2381
0
    return NS_OK;
2382
0
  }
2383
0
2384
0
  if (mEditingState == eSettingUp || mEditingState == eTearingDown) {
2385
0
    // XXX We shouldn't recurse
2386
0
    return NS_OK;
2387
0
  }
2388
0
2389
0
  bool designMode = HasFlag(NODE_IS_EDITABLE);
2390
0
  EditingState newState = designMode ? eDesignMode :
2391
0
                          (mContentEditableCount > 0 ? eContentEditable : eOff);
2392
0
  if (mEditingState == newState) {
2393
0
    // No changes in editing mode.
2394
0
    return NS_OK;
2395
0
  }
2396
0
2397
0
  if (newState == eOff) {
2398
0
    // Editing is being turned off.
2399
0
    nsAutoScriptBlocker scriptBlocker;
2400
0
    NotifyEditableStateChange(this, this);
2401
0
    return TurnEditingOff();
2402
0
  }
2403
0
2404
0
  // Flush out style changes on our _parent_ document, if any, so that
2405
0
  // our check for a presshell won't get stale information.
2406
0
  if (mParentDocument) {
2407
0
    mParentDocument->FlushPendingNotifications(FlushType::Style);
2408
0
  }
2409
0
2410
0
  // get editing session, make sure this is a strong reference so the
2411
0
  // window can't get deleted during the rest of this call.
2412
0
  nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
2413
0
  if (!window)
2414
0
    return NS_ERROR_FAILURE;
2415
0
2416
0
  nsIDocShell *docshell = window->GetDocShell();
2417
0
  if (!docshell)
2418
0
    return NS_ERROR_FAILURE;
2419
0
2420
0
  // FlushPendingNotifications might destroy our docshell.
2421
0
  bool isBeingDestroyed = false;
2422
0
  docshell->IsBeingDestroyed(&isBeingDestroyed);
2423
0
  if (isBeingDestroyed) {
2424
0
    return NS_ERROR_FAILURE;
2425
0
  }
2426
0
2427
0
  nsCOMPtr<nsIEditingSession> editSession;
2428
0
  nsresult rv = docshell->GetEditingSession(getter_AddRefs(editSession));
2429
0
  NS_ENSURE_SUCCESS(rv, rv);
2430
0
2431
0
  RefPtr<HTMLEditor> htmlEditor = editSession->GetHTMLEditorForWindow(window);
2432
0
  if (htmlEditor) {
2433
0
    // We might already have an editor if it was set up for mail, let's see
2434
0
    // if this is actually the case.
2435
0
    uint32_t flags = 0;
2436
0
    htmlEditor->GetFlags(&flags);
2437
0
    if (flags & nsIPlaintextEditor::eEditorMailMask) {
2438
0
      // We already have a mail editor, then we should not attempt to create
2439
0
      // another one.
2440
0
      return NS_OK;
2441
0
    }
2442
0
  }
2443
0
2444
0
  if (!HasPresShell(window)) {
2445
0
    // We should not make the window editable or setup its editor.
2446
0
    // It's probably style=display:none.
2447
0
    return NS_OK;
2448
0
  }
2449
0
2450
0
  bool makeWindowEditable = mEditingState == eOff;
2451
0
  bool updateState = false;
2452
0
  bool spellRecheckAll = false;
2453
0
  bool putOffToRemoveScriptBlockerUntilModifyingEditingState = false;
2454
0
  htmlEditor = nullptr;
2455
0
2456
0
  {
2457
0
    EditingState oldState = mEditingState;
2458
0
    nsAutoEditingState push(this, eSettingUp);
2459
0
2460
0
    nsCOMPtr<nsIPresShell> presShell = GetShell();
2461
0
    NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
2462
0
2463
0
    // Before making this window editable, we need to modify UA style sheet
2464
0
    // because new style may change whether focused element will be focusable
2465
0
    // or not.
2466
0
    nsTArray<RefPtr<StyleSheet>> agentSheets;
2467
0
    rv = presShell->GetAgentStyleSheets(agentSheets);
2468
0
    NS_ENSURE_SUCCESS(rv, rv);
2469
0
2470
0
    auto cache = nsLayoutStylesheetCache::Singleton();
2471
0
2472
0
    StyleSheet* contentEditableSheet = cache->ContentEditableSheet();
2473
0
2474
0
    if (!agentSheets.Contains(contentEditableSheet)) {
2475
0
      agentSheets.AppendElement(contentEditableSheet);
2476
0
    }
2477
0
2478
0
    // Should we update the editable state of all the nodes in the document? We
2479
0
    // need to do this when the designMode value changes, as that overrides
2480
0
    // specific states on the elements.
2481
0
    if (designMode) {
2482
0
      // designMode is being turned on (overrides contentEditable).
2483
0
      StyleSheet* designModeSheet = cache->DesignModeSheet();
2484
0
      if (!agentSheets.Contains(designModeSheet)) {
2485
0
        agentSheets.AppendElement(designModeSheet);
2486
0
      }
2487
0
2488
0
      updateState = true;
2489
0
      spellRecheckAll = oldState == eContentEditable;
2490
0
    }
2491
0
    else if (oldState == eDesignMode) {
2492
0
      // designMode is being turned off (contentEditable is still on).
2493
0
      agentSheets.RemoveElement(cache->DesignModeSheet());
2494
0
      updateState = true;
2495
0
    }
2496
0
2497
0
    rv = presShell->SetAgentStyleSheets(agentSheets);
2498
0
    NS_ENSURE_SUCCESS(rv, rv);
2499
0
2500
0
    presShell->ApplicableStylesChanged();
2501
0
2502
0
    // Adjust focused element with new style but blur event shouldn't be fired
2503
0
    // until mEditingState is modified with newState.
2504
0
    nsAutoScriptBlocker scriptBlocker;
2505
0
    if (designMode) {
2506
0
      nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
2507
0
      nsIContent* focusedContent =
2508
0
        nsFocusManager::GetFocusedDescendant(window,
2509
0
                                             nsFocusManager::eOnlyCurrentWindow,
2510
0
                                             getter_AddRefs(focusedWindow));
2511
0
      if (focusedContent) {
2512
0
        nsIFrame* focusedFrame = focusedContent->GetPrimaryFrame();
2513
0
        bool clearFocus = focusedFrame ? !focusedFrame->IsFocusable() :
2514
0
                                         !focusedContent->IsFocusable();
2515
0
        if (clearFocus) {
2516
0
          nsFocusManager* fm = nsFocusManager::GetFocusManager();
2517
0
          if (fm) {
2518
0
            fm->ClearFocus(window);
2519
0
            // If we need to dispatch blur event, we should put off after
2520
0
            // modifying mEditingState since blur event handler may change
2521
0
            // designMode state again.
2522
0
            putOffToRemoveScriptBlockerUntilModifyingEditingState = true;
2523
0
          }
2524
0
        }
2525
0
      }
2526
0
    }
2527
0
2528
0
    if (makeWindowEditable) {
2529
0
      // Editing is being turned on (through designMode or contentEditable)
2530
0
      // Turn on editor.
2531
0
      // XXX This can cause flushing which can change the editing state, so make
2532
0
      //     sure to avoid recursing.
2533
0
      rv = editSession->MakeWindowEditable(window, "html", false, false,
2534
0
                                           true);
2535
0
      NS_ENSURE_SUCCESS(rv, rv);
2536
0
    }
2537
0
2538
0
    // XXX Need to call TearDownEditorOnWindow for all failures.
2539
0
    htmlEditor = docshell->GetHTMLEditor();
2540
0
    if (!htmlEditor) {
2541
0
      return NS_ERROR_FAILURE;
2542
0
    }
2543
0
2544
0
    // If we're entering the design mode, put the selection at the beginning of
2545
0
    // the document for compatibility reasons.
2546
0
    if (designMode && oldState == eOff) {
2547
0
      htmlEditor->BeginningOfDocument();
2548
0
    }
2549
0
2550
0
    if (putOffToRemoveScriptBlockerUntilModifyingEditingState) {
2551
0
      nsContentUtils::AddScriptBlocker();
2552
0
    }
2553
0
  }
2554
0
2555
0
  mEditingState = newState;
2556
0
  if (putOffToRemoveScriptBlockerUntilModifyingEditingState) {
2557
0
    nsContentUtils::RemoveScriptBlocker();
2558
0
    // If mEditingState is overwritten by another call and already disabled
2559
0
    // the editing, we shouldn't keep making window editable.
2560
0
    if (mEditingState == eOff) {
2561
0
      return NS_OK;
2562
0
    }
2563
0
  }
2564
0
2565
0
  if (makeWindowEditable) {
2566
0
    // Set the editor to not insert br's on return when in p
2567
0
    // elements by default.
2568
0
    // XXX Do we only want to do this for designMode?
2569
0
    // Note that it doesn't matter what CallerType we pass, because the callee
2570
0
    // doesn't use it for this command.  Play it safe and pass the more
2571
0
    // restricted one.
2572
0
    ErrorResult errorResult;
2573
0
    Unused << ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), false,
2574
0
                          NS_LITERAL_STRING("false"),
2575
0
                          // Principal doesn't matter here, because the
2576
0
                          // insertBrOnReturn command doesn't use it.   Still
2577
0
                          // it's too bad we can't easily grab a nullprincipal
2578
0
                          // from somewhere without allocating one..
2579
0
                          *NodePrincipal(),
2580
0
                          errorResult);
2581
0
2582
0
    if (errorResult.Failed()) {
2583
0
      // Editor setup failed. Editing is not on after all.
2584
0
      // XXX Should we reset the editable flag on nodes?
2585
0
      editSession->TearDownEditorOnWindow(window);
2586
0
      mEditingState = eOff;
2587
0
2588
0
      return errorResult.StealNSResult();
2589
0
    }
2590
0
  }
2591
0
2592
0
  if (updateState) {
2593
0
    nsAutoScriptBlocker scriptBlocker;
2594
0
    NotifyEditableStateChange(this, this);
2595
0
  }
2596
0
2597
0
  // Resync the editor's spellcheck state.
2598
0
  if (spellRecheckAll) {
2599
0
    nsCOMPtr<nsISelectionController> selectionController =
2600
0
      htmlEditor->GetSelectionController();
2601
0
    if (NS_WARN_IF(!selectionController)) {
2602
0
      return NS_ERROR_FAILURE;
2603
0
    }
2604
0
2605
0
    RefPtr<Selection> spellCheckSelection =
2606
0
      selectionController->GetSelection(
2607
0
        nsISelectionController::SELECTION_SPELLCHECK);
2608
0
    if (spellCheckSelection) {
2609
0
      spellCheckSelection->RemoveAllRanges(IgnoreErrors());
2610
0
    }
2611
0
  }
2612
0
  htmlEditor->SyncRealTimeSpell();
2613
0
2614
0
  return NS_OK;
2615
0
}
2616
2617
void
2618
nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode,
2619
                              nsIPrincipal& aSubjectPrincipal,
2620
                              ErrorResult& rv)
2621
0
{
2622
0
  SetDesignMode(aDesignMode, Some(&aSubjectPrincipal), rv);
2623
0
}
2624
2625
void
2626
nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode,
2627
                              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
2628
                              ErrorResult& rv)
2629
0
{
2630
0
  if (aSubjectPrincipal.isSome() &&
2631
0
      !aSubjectPrincipal.value()->Subsumes(NodePrincipal())) {
2632
0
    rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
2633
0
    return;
2634
0
  }
2635
0
  bool editableMode = HasFlag(NODE_IS_EDITABLE);
2636
0
  if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
2637
0
    SetEditableFlag(!editableMode);
2638
0
2639
0
    rv = EditingStateChanged();
2640
0
  }
2641
0
}
2642
2643
nsresult
2644
nsHTMLDocument::GetMidasCommandManager(nsICommandManager** aCmdMgr)
2645
0
{
2646
0
  // initialize return value
2647
0
  NS_ENSURE_ARG_POINTER(aCmdMgr);
2648
0
2649
0
  // check if we have it cached
2650
0
  if (mMidasCommandManager) {
2651
0
    NS_ADDREF(*aCmdMgr = mMidasCommandManager);
2652
0
    return NS_OK;
2653
0
  }
2654
0
2655
0
  *aCmdMgr = nullptr;
2656
0
2657
0
  nsPIDOMWindowOuter *window = GetWindow();
2658
0
  if (!window)
2659
0
    return NS_ERROR_FAILURE;
2660
0
2661
0
  nsIDocShell *docshell = window->GetDocShell();
2662
0
  if (!docshell)
2663
0
    return NS_ERROR_FAILURE;
2664
0
2665
0
  mMidasCommandManager = docshell->GetCommandManager();
2666
0
  if (!mMidasCommandManager)
2667
0
    return NS_ERROR_FAILURE;
2668
0
2669
0
  NS_ADDREF(*aCmdMgr = mMidasCommandManager);
2670
0
2671
0
  return NS_OK;
2672
0
}
2673
2674
2675
struct MidasCommand {
2676
  const char*  incomingCommandString;
2677
  const char*  internalCommandString;
2678
  const char*  internalParamString;
2679
  bool useNewParam;
2680
  bool convertToBoolean;
2681
};
2682
2683
static const struct MidasCommand gMidasCommandTable[] = {
2684
  { "bold",          "cmd_bold",            "", true,  false },
2685
  { "italic",        "cmd_italic",          "", true,  false },
2686
  { "underline",     "cmd_underline",       "", true,  false },
2687
  { "strikethrough", "cmd_strikethrough",   "", true,  false },
2688
  { "subscript",     "cmd_subscript",       "", true,  false },
2689
  { "superscript",   "cmd_superscript",     "", true,  false },
2690
  { "cut",           "cmd_cut",             "", true,  false },
2691
  { "copy",          "cmd_copy",            "", true,  false },
2692
  { "paste",         "cmd_paste",           "", true,  false },
2693
  { "delete",        "cmd_deleteCharBackward", "", true,  false },
2694
  { "forwarddelete", "cmd_deleteCharForward", "", true,  false },
2695
  { "selectall",     "cmd_selectAll",       "", true,  false },
2696
  { "undo",          "cmd_undo",            "", true,  false },
2697
  { "redo",          "cmd_redo",            "", true,  false },
2698
  { "indent",        "cmd_indent",          "", true,  false },
2699
  { "outdent",       "cmd_outdent",         "", true,  false },
2700
  { "backcolor",     "cmd_highlight",       "", false, false },
2701
  { "forecolor",     "cmd_fontColor",       "", false, false },
2702
  { "hilitecolor",   "cmd_highlight",       "", false, false },
2703
  { "fontname",      "cmd_fontFace",        "", false, false },
2704
  { "fontsize",      "cmd_fontSize",        "", false, false },
2705
  { "increasefontsize", "cmd_increaseFont", "", false, false },
2706
  { "decreasefontsize", "cmd_decreaseFont", "", false, false },
2707
  { "inserthorizontalrule", "cmd_insertHR", "", true,  false },
2708
  { "createlink",    "cmd_insertLinkNoUI",  "", false, false },
2709
  { "insertimage",   "cmd_insertImageNoUI", "", false, false },
2710
  { "inserthtml",    "cmd_insertHTML",      "", false, false },
2711
  { "inserttext",    "cmd_insertText",      "", false, false },
2712
  { "gethtml",       "cmd_getContents",     "", false, false },
2713
  { "justifyleft",   "cmd_align",       "left", true,  false },
2714
  { "justifyright",  "cmd_align",      "right", true,  false },
2715
  { "justifycenter", "cmd_align",     "center", true,  false },
2716
  { "justifyfull",   "cmd_align",    "justify", true,  false },
2717
  { "removeformat",  "cmd_removeStyles",    "", true,  false },
2718
  { "unlink",        "cmd_removeLinks",     "", true,  false },
2719
  { "insertorderedlist",   "cmd_ol",        "", true,  false },
2720
  { "insertunorderedlist", "cmd_ul",        "", true,  false },
2721
  { "insertparagraph", "cmd_insertParagraph", "", true,  false },
2722
  { "insertlinebreak", "cmd_insertLineBreak", "", true,  false },
2723
  { "formatblock",   "cmd_paragraphState",  "", false, false },
2724
  { "heading",       "cmd_paragraphState",  "", false, false },
2725
  { "styleWithCSS",  "cmd_setDocumentUseCSS", "", false, true },
2726
  { "contentReadOnly", "cmd_setDocumentReadOnly", "", false, true },
2727
  { "insertBrOnReturn", "cmd_insertBrOnReturn", "", false, true },
2728
  { "defaultParagraphSeparator", "cmd_defaultParagraphSeparator", "", false, false },
2729
  { "enableObjectResizing", "cmd_enableObjectResizing", "", false, true },
2730
  { "enableInlineTableEditing", "cmd_enableInlineTableEditing", "", false, true },
2731
  { "enableAbsolutePositionEditing", "cmd_enableAbsolutePositionEditing", "", false, true },
2732
#if 0
2733
// no editor support to remove alignments right now
2734
  { "justifynone",   "cmd_align",           "", true,  false },
2735
2736
// the following will need special review before being turned on
2737
  { "saveas",        "cmd_saveAs",          "", true,  false },
2738
  { "print",         "cmd_print",           "", true,  false },
2739
#endif
2740
  { nullptr, nullptr, nullptr, false, false }
2741
};
2742
2743
0
#define MidasCommandCount ((sizeof(gMidasCommandTable) / sizeof(struct MidasCommand)) - 1)
2744
2745
static const char* const gBlocks[] = {
2746
  "ADDRESS",
2747
  "BLOCKQUOTE",
2748
  "DD",
2749
  "DIV",
2750
  "DL",
2751
  "DT",
2752
  "H1",
2753
  "H2",
2754
  "H3",
2755
  "H4",
2756
  "H5",
2757
  "H6",
2758
  "P",
2759
  "PRE"
2760
};
2761
2762
static bool
2763
ConvertToMidasInternalCommandInner(const nsAString& inCommandID,
2764
                                   const nsAString& inParam,
2765
                                   nsACString& outCommandID,
2766
                                   nsACString& outParam,
2767
                                   bool& outIsBoolean,
2768
                                   bool& outBooleanValue,
2769
                                   bool aIgnoreParams)
2770
0
{
2771
0
  NS_ConvertUTF16toUTF8 convertedCommandID(inCommandID);
2772
0
2773
0
  // Hack to support old boolean commands that were backwards (see bug 301490).
2774
0
  bool invertBool = false;
2775
0
  if (convertedCommandID.LowerCaseEqualsLiteral("usecss")) {
2776
0
    convertedCommandID.AssignLiteral("styleWithCSS");
2777
0
    invertBool = true;
2778
0
  } else if (convertedCommandID.LowerCaseEqualsLiteral("readonly")) {
2779
0
    convertedCommandID.AssignLiteral("contentReadOnly");
2780
0
    invertBool = true;
2781
0
  }
2782
0
2783
0
  uint32_t i;
2784
0
  bool found = false;
2785
0
  for (i = 0; i < MidasCommandCount; ++i) {
2786
0
    if (convertedCommandID.Equals(gMidasCommandTable[i].incomingCommandString,
2787
0
                                  nsCaseInsensitiveCStringComparator())) {
2788
0
      found = true;
2789
0
      break;
2790
0
    }
2791
0
  }
2792
0
2793
0
  if (!found) {
2794
0
    // reset results if the command is not found in our table
2795
0
    outCommandID.SetLength(0);
2796
0
    outParam.SetLength(0);
2797
0
    outIsBoolean = false;
2798
0
    return false;
2799
0
  }
2800
0
2801
0
  // set outCommandID (what we use internally)
2802
0
  outCommandID.Assign(gMidasCommandTable[i].internalCommandString);
2803
0
2804
0
  // set outParam & outIsBoolean based on flags from the table
2805
0
  outIsBoolean = gMidasCommandTable[i].convertToBoolean;
2806
0
2807
0
  if (aIgnoreParams) {
2808
0
    // No further work to do
2809
0
    return true;
2810
0
  }
2811
0
2812
0
  if (gMidasCommandTable[i].useNewParam) {
2813
0
    // Just have to copy it, no checking
2814
0
    outParam.Assign(gMidasCommandTable[i].internalParamString);
2815
0
    return true;
2816
0
  }
2817
0
2818
0
  // handle checking of param passed in
2819
0
  if (outIsBoolean) {
2820
0
    // If this is a boolean value and it's not explicitly false (e.g. no value)
2821
0
    // we default to "true". For old backwards commands we invert the check (see
2822
0
    // bug 301490).
2823
0
    if (invertBool) {
2824
0
      outBooleanValue = inParam.LowerCaseEqualsLiteral("false");
2825
0
    } else {
2826
0
      outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
2827
0
    }
2828
0
    outParam.Truncate();
2829
0
2830
0
    return true;
2831
0
  }
2832
0
2833
0
  // String parameter -- see if we need to convert it (necessary for
2834
0
  // cmd_paragraphState and cmd_fontSize)
2835
0
  if (outCommandID.EqualsLiteral("cmd_paragraphState")) {
2836
0
    const char16_t* start = inParam.BeginReading();
2837
0
    const char16_t* end = inParam.EndReading();
2838
0
    if (start != end && *start == '<' && *(end - 1) == '>') {
2839
0
      ++start;
2840
0
      --end;
2841
0
    }
2842
0
2843
0
    NS_ConvertUTF16toUTF8 convertedParam(Substring(start, end));
2844
0
    uint32_t j;
2845
0
    for (j = 0; j < ArrayLength(gBlocks); ++j) {
2846
0
      if (convertedParam.Equals(gBlocks[j],
2847
0
                                nsCaseInsensitiveCStringComparator())) {
2848
0
        outParam.Assign(gBlocks[j]);
2849
0
        break;
2850
0
      }
2851
0
    }
2852
0
2853
0
    if (j == ArrayLength(gBlocks)) {
2854
0
      outParam.Truncate();
2855
0
    }
2856
0
  } else if (outCommandID.EqualsLiteral("cmd_fontSize")) {
2857
0
    // Per editing spec as of April 23, 2012, we need to reject the value if
2858
0
    // it's not a valid floating-point number surrounded by optional whitespace.
2859
0
    // Otherwise, we parse it as a legacy font size.  For now, we just parse as
2860
0
    // a legacy font size regardless (matching WebKit) -- bug 747879.
2861
0
    outParam.Truncate();
2862
0
    int32_t size = nsContentUtils::ParseLegacyFontSize(inParam);
2863
0
    if (size) {
2864
0
      outParam.AppendInt(size);
2865
0
    }
2866
0
  } else {
2867
0
    CopyUTF16toUTF8(inParam, outParam);
2868
0
  }
2869
0
2870
0
  return true;
2871
0
}
2872
2873
static bool
2874
ConvertToMidasInternalCommand(const nsAString & inCommandID,
2875
                              const nsAString & inParam,
2876
                              nsACString& outCommandID,
2877
                              nsACString& outParam,
2878
                              bool& outIsBoolean,
2879
                              bool& outBooleanValue)
2880
0
{
2881
0
  return ConvertToMidasInternalCommandInner(inCommandID, inParam, outCommandID,
2882
0
                                            outParam, outIsBoolean,
2883
0
                                            outBooleanValue, false);
2884
0
}
2885
2886
static bool
2887
ConvertToMidasInternalCommand(const nsAString & inCommandID,
2888
                              nsACString& outCommandID)
2889
0
{
2890
0
  nsAutoCString dummyCString;
2891
0
  nsAutoString dummyString;
2892
0
  bool dummyBool;
2893
0
  return ConvertToMidasInternalCommandInner(inCommandID, dummyString,
2894
0
                                            outCommandID, dummyCString,
2895
0
                                            dummyBool, dummyBool, true);
2896
0
}
2897
2898
bool
2899
nsHTMLDocument::ExecCommand(const nsAString& commandID,
2900
                            bool doShowUI,
2901
                            const nsAString& value,
2902
                            nsIPrincipal& aSubjectPrincipal,
2903
                            ErrorResult& rv)
2904
0
{
2905
0
  //  for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
2906
0
  //  this might add some ugly JS dependencies?
2907
0
2908
0
  nsAutoCString cmdToDispatch, paramStr;
2909
0
  bool isBool, boolVal;
2910
0
  if (!ConvertToMidasInternalCommand(commandID, value,
2911
0
                                     cmdToDispatch, paramStr,
2912
0
                                     isBool, boolVal)) {
2913
0
    return false;
2914
0
  }
2915
0
2916
0
  bool isCutCopy = (commandID.LowerCaseEqualsLiteral("cut") ||
2917
0
                    commandID.LowerCaseEqualsLiteral("copy"));
2918
0
  bool isPaste = commandID.LowerCaseEqualsLiteral("paste");
2919
0
2920
0
  // if editing is not on, bail
2921
0
  if (!isCutCopy && !isPaste && !IsEditingOnAfterFlush()) {
2922
0
    return false;
2923
0
  }
2924
0
2925
0
  // if they are requesting UI from us, let's fail since we have no UI
2926
0
  if (doShowUI) {
2927
0
    return false;
2928
0
  }
2929
0
2930
0
  // special case for cut & copy
2931
0
  // cut & copy are allowed in non editable documents
2932
0
  if (isCutCopy) {
2933
0
    if (!nsContentUtils::IsCutCopyAllowed(&aSubjectPrincipal)) {
2934
0
      // We have rejected the event due to it not being performed in an
2935
0
      // input-driven context therefore, we report the error to the console.
2936
0
      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2937
0
                                      NS_LITERAL_CSTRING("DOM"), this,
2938
0
                                      nsContentUtils::eDOM_PROPERTIES,
2939
0
                                      "ExecCommandCutCopyDeniedNotInputDriven");
2940
0
      return false;
2941
0
    }
2942
0
2943
0
    // For cut & copy commands, we need the behaviour from nsWindowRoot::GetControllers
2944
0
    // which is to look at the focused element, and defer to a focused textbox's controller
2945
0
    // The code past taken by other commands in ExecCommand always uses the window directly,
2946
0
    // rather than deferring to the textbox, which is desireable for most editor commands,
2947
0
    // but not 'cut' and 'copy' (as those should allow copying out of embedded editors).
2948
0
    // This behaviour is invoked if we call DoCommand directly on the docShell.
2949
0
    nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2950
0
    if (docShell) {
2951
0
      nsresult res = docShell->DoCommand(cmdToDispatch.get());
2952
0
      if (res == NS_SUCCESS_DOM_NO_OPERATION) {
2953
0
        return false;
2954
0
      }
2955
0
      return NS_SUCCEEDED(res);
2956
0
    }
2957
0
    return false;
2958
0
  }
2959
0
2960
0
  if (commandID.LowerCaseEqualsLiteral("gethtml")) {
2961
0
    rv.Throw(NS_ERROR_FAILURE);
2962
0
    return false;
2963
0
  }
2964
0
2965
0
  if (isPaste && !nsContentUtils::PrincipalHasPermission(&aSubjectPrincipal,
2966
0
                                                         nsGkAtoms::clipboardRead)) {
2967
0
    return false;
2968
0
  }
2969
0
2970
0
  // get command manager and dispatch command to our window if it's acceptable
2971
0
  nsCOMPtr<nsICommandManager> cmdMgr;
2972
0
  GetMidasCommandManager(getter_AddRefs(cmdMgr));
2973
0
  if (!cmdMgr) {
2974
0
    rv.Throw(NS_ERROR_FAILURE);
2975
0
    return false;
2976
0
  }
2977
0
2978
0
  nsPIDOMWindowOuter* window = GetWindow();
2979
0
  if (!window) {
2980
0
    rv.Throw(NS_ERROR_FAILURE);
2981
0
    return false;
2982
0
  }
2983
0
2984
0
  if ((cmdToDispatch.EqualsLiteral("cmd_fontSize") ||
2985
0
       cmdToDispatch.EqualsLiteral("cmd_insertImageNoUI") ||
2986
0
       cmdToDispatch.EqualsLiteral("cmd_insertLinkNoUI") ||
2987
0
       cmdToDispatch.EqualsLiteral("cmd_paragraphState")) &&
2988
0
      paramStr.IsEmpty()) {
2989
0
    // Invalid value, return false
2990
0
    return false;
2991
0
  }
2992
0
2993
0
  if (cmdToDispatch.EqualsLiteral("cmd_defaultParagraphSeparator") &&
2994
0
      !paramStr.LowerCaseEqualsLiteral("div") &&
2995
0
      !paramStr.LowerCaseEqualsLiteral("p") &&
2996
0
      !paramStr.LowerCaseEqualsLiteral("br")) {
2997
0
    // Invalid value
2998
0
    return false;
2999
0
  }
3000
0
3001
0
  // Return false for disabled commands (bug 760052)
3002
0
  bool enabled = false;
3003
0
  cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, &enabled);
3004
0
  if (!enabled) {
3005
0
    return false;
3006
0
  }
3007
0
3008
0
  if (!isBool && paramStr.IsEmpty()) {
3009
0
    rv = cmdMgr->DoCommand(cmdToDispatch.get(), nullptr, window);
3010
0
  } else {
3011
0
    // we have a command that requires a parameter, create params
3012
0
    RefPtr<nsCommandParams> params = new nsCommandParams();
3013
0
    if (isBool) {
3014
0
      rv = params->SetBool("state_attribute", boolVal);
3015
0
    } else if (cmdToDispatch.EqualsLiteral("cmd_fontFace")) {
3016
0
      rv = params->SetString("state_attribute", value);
3017
0
    } else if (cmdToDispatch.EqualsLiteral("cmd_insertHTML") ||
3018
0
               cmdToDispatch.EqualsLiteral("cmd_insertText")) {
3019
0
      rv = params->SetString("state_data", value);
3020
0
    } else {
3021
0
      rv = params->SetCString("state_attribute", paramStr);
3022
0
    }
3023
0
    if (rv.Failed()) {
3024
0
      return false;
3025
0
    }
3026
0
    rv = cmdMgr->DoCommand(cmdToDispatch.get(), params, window);
3027
0
  }
3028
0
3029
0
  return !rv.Failed();
3030
0
}
3031
3032
bool
3033
nsHTMLDocument::QueryCommandEnabled(const nsAString& commandID,
3034
                                    nsIPrincipal& aSubjectPrincipal,
3035
                                    ErrorResult& rv)
3036
0
{
3037
0
  nsAutoCString cmdToDispatch;
3038
0
  if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch)) {
3039
0
    return false;
3040
0
  }
3041
0
3042
0
  // cut & copy are always allowed
3043
0
  bool isCutCopy = commandID.LowerCaseEqualsLiteral("cut") ||
3044
0
                   commandID.LowerCaseEqualsLiteral("copy");
3045
0
  if (isCutCopy) {
3046
0
    return nsContentUtils::IsCutCopyAllowed(&aSubjectPrincipal);
3047
0
  }
3048
0
3049
0
  // Report false for restricted commands
3050
0
  bool restricted = commandID.LowerCaseEqualsLiteral("paste");
3051
0
  if (restricted && !nsContentUtils::IsSystemPrincipal(&aSubjectPrincipal)) {
3052
0
    return false;
3053
0
  }
3054
0
3055
0
  // if editing is not on, bail
3056
0
  if (!IsEditingOnAfterFlush()) {
3057
0
    return false;
3058
0
  }
3059
0
3060
0
  // get command manager and dispatch command to our window if it's acceptable
3061
0
  nsCOMPtr<nsICommandManager> cmdMgr;
3062
0
  GetMidasCommandManager(getter_AddRefs(cmdMgr));
3063
0
  if (!cmdMgr) {
3064
0
    rv.Throw(NS_ERROR_FAILURE);
3065
0
    return false;
3066
0
  }
3067
0
3068
0
  nsPIDOMWindowOuter* window = GetWindow();
3069
0
  if (!window) {
3070
0
    rv.Throw(NS_ERROR_FAILURE);
3071
0
    return false;
3072
0
  }
3073
0
3074
0
  bool retval;
3075
0
  rv = cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, &retval);
3076
0
  return retval;
3077
0
}
3078
3079
bool
3080
nsHTMLDocument::QueryCommandIndeterm(const nsAString& commandID, ErrorResult& rv)
3081
0
{
3082
0
  nsAutoCString cmdToDispatch;
3083
0
  if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch)) {
3084
0
    return false;
3085
0
  }
3086
0
3087
0
  // if editing is not on, bail
3088
0
  if (!IsEditingOnAfterFlush()) {
3089
0
    return false;
3090
0
  }
3091
0
3092
0
  // get command manager and dispatch command to our window if it's acceptable
3093
0
  nsCOMPtr<nsICommandManager> cmdMgr;
3094
0
  GetMidasCommandManager(getter_AddRefs(cmdMgr));
3095
0
  if (!cmdMgr) {
3096
0
    rv.Throw(NS_ERROR_FAILURE);
3097
0
    return false;
3098
0
  }
3099
0
3100
0
  nsPIDOMWindowOuter* window = GetWindow();
3101
0
  if (!window) {
3102
0
    rv.Throw(NS_ERROR_FAILURE);
3103
0
    return false;
3104
0
  }
3105
0
3106
0
  RefPtr<nsCommandParams> params = new nsCommandParams();
3107
0
  rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, params);
3108
0
  if (rv.Failed()) {
3109
0
    return false;
3110
0
  }
3111
0
3112
0
  // If command does not have a state_mixed value, this call fails and sets
3113
0
  // retval to false.  This is fine -- we want to return false in that case
3114
0
  // anyway (bug 738385), so we just don't throw regardless.
3115
0
  return params->GetBool("state_mixed");
3116
0
}
3117
3118
bool
3119
nsHTMLDocument::QueryCommandState(const nsAString& commandID, ErrorResult& rv)
3120
0
{
3121
0
  nsAutoCString cmdToDispatch, paramToCheck;
3122
0
  bool dummy, dummy2;
3123
0
  if (!ConvertToMidasInternalCommand(commandID, commandID,
3124
0
                                     cmdToDispatch, paramToCheck,
3125
0
                                     dummy, dummy2)) {
3126
0
    return false;
3127
0
  }
3128
0
3129
0
  // if editing is not on, bail
3130
0
  if (!IsEditingOnAfterFlush()) {
3131
0
    return false;
3132
0
  }
3133
0
3134
0
  // get command manager and dispatch command to our window if it's acceptable
3135
0
  nsCOMPtr<nsICommandManager> cmdMgr;
3136
0
  GetMidasCommandManager(getter_AddRefs(cmdMgr));
3137
0
  if (!cmdMgr) {
3138
0
    rv.Throw(NS_ERROR_FAILURE);
3139
0
    return false;
3140
0
  }
3141
0
3142
0
  nsPIDOMWindowOuter* window = GetWindow();
3143
0
  if (!window) {
3144
0
    rv.Throw(NS_ERROR_FAILURE);
3145
0
    return false;
3146
0
  }
3147
0
3148
0
  if (commandID.LowerCaseEqualsLiteral("usecss")) {
3149
0
    // Per spec, state is supported for styleWithCSS but not useCSS, so we just
3150
0
    // return false always.
3151
0
    return false;
3152
0
  }
3153
0
3154
0
  RefPtr<nsCommandParams> params = new nsCommandParams();
3155
0
  rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, params);
3156
0
  if (rv.Failed()) {
3157
0
    return false;
3158
0
  }
3159
0
3160
0
  // handle alignment as a special case (possibly other commands too?)
3161
0
  // Alignment is special because the external api is individual
3162
0
  // commands but internally we use cmd_align with different
3163
0
  // parameters.  When getting the state of this command, we need to
3164
0
  // return the boolean for this particular alignment rather than the
3165
0
  // string of 'which alignment is this?'
3166
0
  if (cmdToDispatch.EqualsLiteral("cmd_align")) {
3167
0
    nsAutoCString actualAlignmentType;
3168
0
    rv = params->GetCString("state_attribute", actualAlignmentType);
3169
0
    return !rv.Failed() && !actualAlignmentType.IsEmpty() &&
3170
0
           paramToCheck == actualAlignmentType;
3171
0
  }
3172
0
3173
0
  // If command does not have a state_all value, this call fails and sets
3174
0
  // retval to false.  This is fine -- we want to return false in that case
3175
0
  // anyway (bug 738385), so we just succeed and return false regardless.
3176
0
  return params->GetBool("state_all");
3177
0
}
3178
3179
bool
3180
nsHTMLDocument::QueryCommandSupported(const nsAString& commandID,
3181
                                      CallerType aCallerType)
3182
0
{
3183
0
  // Gecko technically supports all the clipboard commands including
3184
0
  // cut/copy/paste, but non-privileged content will be unable to call
3185
0
  // paste, and depending on the pref "dom.allow_cut_copy", cut and copy
3186
0
  // may also be disallowed to be called from non-privileged content.
3187
0
  // For that reason, we report the support status of corresponding
3188
0
  // command accordingly.
3189
0
  if (aCallerType != CallerType::System) {
3190
0
    if (commandID.LowerCaseEqualsLiteral("paste")) {
3191
0
      return false;
3192
0
    }
3193
0
    if (nsContentUtils::IsCutCopyRestricted()) {
3194
0
      // XXXbz should we worry about correctly reporting "true" in the
3195
0
      // "restricted, but we're an addon with clipboardWrite permissions" case?
3196
0
      // See also nsContentUtils::IsCutCopyAllowed.
3197
0
      if (commandID.LowerCaseEqualsLiteral("cut") ||
3198
0
          commandID.LowerCaseEqualsLiteral("copy")) {
3199
0
        return false;
3200
0
      }
3201
0
    }
3202
0
  }
3203
0
3204
0
  // commandID is supported if it can be converted to a Midas command
3205
0
  nsAutoCString cmdToDispatch;
3206
0
  return ConvertToMidasInternalCommand(commandID, cmdToDispatch);
3207
0
}
3208
3209
void
3210
nsHTMLDocument::QueryCommandValue(const nsAString& commandID,
3211
                                  nsAString& aValue,
3212
                                  ErrorResult& rv)
3213
0
{
3214
0
  aValue.Truncate();
3215
0
3216
0
  nsAutoCString cmdToDispatch, paramStr;
3217
0
  if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch)) {
3218
0
    // Return empty string
3219
0
    return;
3220
0
  }
3221
0
3222
0
  // if editing is not on, bail
3223
0
  if (!IsEditingOnAfterFlush()) {
3224
0
    return;
3225
0
  }
3226
0
3227
0
  // get command manager and dispatch command to our window if it's acceptable
3228
0
  nsCOMPtr<nsICommandManager> cmdMgr;
3229
0
  GetMidasCommandManager(getter_AddRefs(cmdMgr));
3230
0
  if (!cmdMgr) {
3231
0
    rv.Throw(NS_ERROR_FAILURE);
3232
0
    return;
3233
0
  }
3234
0
3235
0
  nsPIDOMWindowOuter* window = GetWindow();
3236
0
  if (!window) {
3237
0
    rv.Throw(NS_ERROR_FAILURE);
3238
0
    return;
3239
0
  }
3240
0
3241
0
  // this is a special command since we are calling DoCommand rather than
3242
0
  // GetCommandState like the other commands
3243
0
  RefPtr<nsCommandParams> params = new nsCommandParams();
3244
0
  if (cmdToDispatch.EqualsLiteral("cmd_getContents")) {
3245
0
    rv = params->SetBool("selection_only", true);
3246
0
    if (rv.Failed()) {
3247
0
      return;
3248
0
    }
3249
0
    rv = params->SetCString("format", NS_LITERAL_CSTRING("text/html"));
3250
0
    if (rv.Failed()) {
3251
0
      return;
3252
0
    }
3253
0
    rv = cmdMgr->DoCommand(cmdToDispatch.get(), params, window);
3254
0
    if (rv.Failed()) {
3255
0
      return;
3256
0
    }
3257
0
    params->GetString("result", aValue);
3258
0
    return;
3259
0
  }
3260
0
3261
0
  rv = params->SetCString("state_attribute", paramStr);
3262
0
  if (rv.Failed()) {
3263
0
    return;
3264
0
  }
3265
0
3266
0
  rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, params);
3267
0
  if (rv.Failed()) {
3268
0
    return;
3269
0
  }
3270
0
3271
0
  // If command does not have a state_attribute value, this call fails, and
3272
0
  // aValue will wind up being the empty string.  This is fine -- we want to
3273
0
  // return "" in that case anyway (bug 738385), so we just return NS_OK
3274
0
  // regardless.
3275
0
  nsAutoCString result;
3276
0
  params->GetCString("state_attribute", result);
3277
0
  CopyUTF8toUTF16(result, aValue);
3278
0
}
3279
3280
nsresult
3281
nsHTMLDocument::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const
3282
0
{
3283
0
  NS_ASSERTION(aNodeInfo->NodeInfoManager() == mNodeInfoManager,
3284
0
               "Can't import this document into another document!");
3285
0
3286
0
  RefPtr<nsHTMLDocument> clone = new nsHTMLDocument();
3287
0
  nsresult rv = CloneDocHelper(clone.get());
3288
0
  NS_ENSURE_SUCCESS(rv, rv);
3289
0
3290
0
  // State from nsHTMLDocument
3291
0
  clone->mLoadFlags = mLoadFlags;
3292
0
3293
0
  return CallQueryInterface(clone.get(), aResult);
3294
0
}
3295
3296
bool
3297
nsHTMLDocument::IsEditingOnAfterFlush()
3298
0
{
3299
0
  nsIDocument* doc = GetParentDocument();
3300
0
  if (doc) {
3301
0
    // Make sure frames are up to date, since that can affect whether
3302
0
    // we're editable.
3303
0
    doc->FlushPendingNotifications(FlushType::Frames);
3304
0
  }
3305
0
3306
0
  return IsEditingOn();
3307
0
}
3308
3309
void
3310
nsHTMLDocument::RemovedFromDocShell()
3311
0
{
3312
0
  mEditingState = eOff;
3313
0
  nsDocument::RemovedFromDocShell();
3314
0
}
3315
3316
/* virtual */ void
3317
nsHTMLDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
3318
0
{
3319
0
  nsDocument::DocAddSizeOfExcludingThis(aWindowSizes);
3320
0
3321
0
  // Measurement of the following members may be added later if DMD finds it is
3322
0
  // worthwhile:
3323
0
  // - mLinks
3324
0
  // - mAnchors
3325
0
  // - mWyciwygChannel
3326
0
  // - mMidasCommandManager
3327
0
}
3328
3329
bool
3330
nsHTMLDocument::WillIgnoreCharsetOverride()
3331
0
{
3332
0
  if (mEncodingMenuDisabled) {
3333
0
    return true;
3334
0
  }
3335
0
  if (mType != eHTML) {
3336
0
    MOZ_ASSERT(mType == eXHTML);
3337
0
    return true;
3338
0
  }
3339
0
  if (mCharacterSetSource >= kCharsetFromByteOrderMark) {
3340
0
    return true;
3341
0
  }
3342
0
  if (!mCharacterSet->IsAsciiCompatible() &&
3343
0
      mCharacterSet != ISO_2022_JP_ENCODING) {
3344
0
    return true;
3345
0
  }
3346
0
  nsCOMPtr<nsIWyciwygChannel> wyciwyg = do_QueryInterface(mChannel);
3347
0
  if (wyciwyg) {
3348
0
    return true;
3349
0
  }
3350
0
  nsIURI* uri = GetOriginalURI();
3351
0
  if (uri) {
3352
0
    bool schemeIs = false;
3353
0
    uri->SchemeIs("about", &schemeIs);
3354
0
    if (schemeIs) {
3355
0
      return true;
3356
0
    }
3357
0
    bool isResource;
3358
0
    nsresult rv = NS_URIChainHasFlags(uri,
3359
0
                                      nsIProtocolHandler::URI_IS_UI_RESOURCE,
3360
0
                                      &isResource);
3361
0
    if (NS_FAILED(rv) || isResource) {
3362
0
      return true;
3363
0
    }
3364
0
  }
3365
0
  return false;
3366
0
}
3367
3368
void
3369
nsHTMLDocument::GetFormsAndFormControls(nsContentList** aFormList,
3370
                                        nsContentList** aFormControlList)
3371
0
{
3372
0
  RefPtr<ContentListHolder> holder = mContentListHolder;
3373
0
  if (!holder) {
3374
0
    // Flush our content model so it'll be up to date
3375
0
    // If this becomes unnecessary and the following line is removed,
3376
0
    // please also remove the corresponding flush operation from
3377
0
    // nsHtml5TreeBuilderCppSupplement.h. (Look for "See bug 497861." there.)
3378
0
    //XXXsmaug nsHtml5TreeBuilderCppSupplement doesn't seem to have such flush
3379
0
    //         anymore.
3380
0
    FlushPendingNotifications(FlushType::Content);
3381
0
3382
0
    RefPtr<nsContentList> htmlForms = GetExistingForms();
3383
0
    if (!htmlForms) {
3384
0
      // If the document doesn't have an existing forms content list, create a
3385
0
      // new one which will be released soon by ContentListHolder.  The idea is
3386
0
      // that we don't have that list hanging around for a long time and slowing
3387
0
      // down future DOM mutations.
3388
0
      //
3389
0
      // Please keep this in sync with nsIDocument::Forms().
3390
0
      htmlForms = new nsContentList(this, kNameSpaceID_XHTML,
3391
0
                                    nsGkAtoms::form, nsGkAtoms::form,
3392
0
                                    /* aDeep = */ true,
3393
0
                                    /* aLiveList = */ true);
3394
0
    }
3395
0
3396
0
    RefPtr<nsContentList> htmlFormControls =
3397
0
      new nsContentList(this,
3398
0
                        nsHTMLDocument::MatchFormControls,
3399
0
                        nullptr, nullptr,
3400
0
                        /* aDeep = */ true,
3401
0
                        /* aMatchAtom = */ nullptr,
3402
0
                        /* aMatchNameSpaceId = */ kNameSpaceID_None,
3403
0
                        /* aFuncMayDependOnAttr = */ true,
3404
0
                        /* aLiveList = */ true);
3405
0
3406
0
    holder = new ContentListHolder(this, htmlForms, htmlFormControls);
3407
0
    RefPtr<ContentListHolder> runnable = holder;
3408
0
    if (NS_SUCCEEDED(Dispatch(TaskCategory::GarbageCollection,
3409
0
                              runnable.forget()))) {
3410
0
      mContentListHolder = holder;
3411
0
    }
3412
0
  }
3413
0
3414
0
  NS_ADDREF(*aFormList = holder->mFormList);
3415
0
  NS_ADDREF(*aFormControlList = holder->mFormControlList);
3416
0
}
3417
3418
void
3419
nsHTMLDocument::UserInteractionForTesting()
3420
0
{
3421
0
  SetUserHasInteracted();
3422
0
}