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