Thu, 22 Jan 2015 13:21:57 +0100
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 | } |