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