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