content/html/document/src/nsHTMLDocument.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial