Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 sw=2 et tw=78: */ |
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 "nsCOMPtr.h" |
michael@0 | 8 | #include "nsXMLContentSink.h" |
michael@0 | 9 | #include "nsIParser.h" |
michael@0 | 10 | #include "nsIDocument.h" |
michael@0 | 11 | #include "nsIDOMDocument.h" |
michael@0 | 12 | #include "nsIDOMDocumentType.h" |
michael@0 | 13 | #include "nsIContent.h" |
michael@0 | 14 | #include "nsIURI.h" |
michael@0 | 15 | #include "nsNetUtil.h" |
michael@0 | 16 | #include "nsIDocShell.h" |
michael@0 | 17 | #include "nsIStyleSheetLinkingElement.h" |
michael@0 | 18 | #include "nsIDOMComment.h" |
michael@0 | 19 | #include "nsIDOMCDATASection.h" |
michael@0 | 20 | #include "DocumentType.h" |
michael@0 | 21 | #include "nsHTMLParts.h" |
michael@0 | 22 | #include "nsCRT.h" |
michael@0 | 23 | #include "nsCSSStyleSheet.h" |
michael@0 | 24 | #include "mozilla/css/Loader.h" |
michael@0 | 25 | #include "nsGkAtoms.h" |
michael@0 | 26 | #include "nsContentUtils.h" |
michael@0 | 27 | #include "nsIScriptContext.h" |
michael@0 | 28 | #include "nsNameSpaceManager.h" |
michael@0 | 29 | #include "nsIServiceManager.h" |
michael@0 | 30 | #include "nsIScriptSecurityManager.h" |
michael@0 | 31 | #include "nsIContentViewer.h" |
michael@0 | 32 | #include "prtime.h" |
michael@0 | 33 | #include "prlog.h" |
michael@0 | 34 | #include "prmem.h" |
michael@0 | 35 | #include "nsRect.h" |
michael@0 | 36 | #include "nsIWebNavigation.h" |
michael@0 | 37 | #include "nsIScriptElement.h" |
michael@0 | 38 | #include "nsScriptLoader.h" |
michael@0 | 39 | #include "nsStyleLinkElement.h" |
michael@0 | 40 | #include "nsReadableUtils.h" |
michael@0 | 41 | #include "nsUnicharUtils.h" |
michael@0 | 42 | #include "nsICookieService.h" |
michael@0 | 43 | #include "nsIPrompt.h" |
michael@0 | 44 | #include "nsIChannel.h" |
michael@0 | 45 | #include "nsIPrincipal.h" |
michael@0 | 46 | #include "nsXMLPrettyPrinter.h" |
michael@0 | 47 | #include "nsNodeInfoManager.h" |
michael@0 | 48 | #include "nsContentCreatorFunctions.h" |
michael@0 | 49 | #include "nsIContentPolicy.h" |
michael@0 | 50 | #include "nsContentPolicyUtils.h" |
michael@0 | 51 | #include "nsError.h" |
michael@0 | 52 | #include "nsIDOMProcessingInstruction.h" |
michael@0 | 53 | #include "nsNodeUtils.h" |
michael@0 | 54 | #include "nsIScriptGlobalObject.h" |
michael@0 | 55 | #include "nsIHTMLDocument.h" |
michael@0 | 56 | #include "mozAutoDocUpdate.h" |
michael@0 | 57 | #include "nsMimeTypes.h" |
michael@0 | 58 | #include "nsHtml5SVGLoadDispatcher.h" |
michael@0 | 59 | #include "nsTextNode.h" |
michael@0 | 60 | #include "mozilla/dom/CDATASection.h" |
michael@0 | 61 | #include "mozilla/dom/Comment.h" |
michael@0 | 62 | #include "mozilla/dom/Element.h" |
michael@0 | 63 | #include "mozilla/dom/ProcessingInstruction.h" |
michael@0 | 64 | |
michael@0 | 65 | using namespace mozilla::dom; |
michael@0 | 66 | |
michael@0 | 67 | // XXX Open Issues: |
michael@0 | 68 | // 1) what's not allowed - We need to figure out which HTML tags |
michael@0 | 69 | // (prefixed with a HTML namespace qualifier) are explicitly not |
michael@0 | 70 | // allowed (if any). |
michael@0 | 71 | // 2) factoring code with nsHTMLContentSink - There's some amount of |
michael@0 | 72 | // common code between this and the HTML content sink. This will |
michael@0 | 73 | // increase as we support more and more HTML elements. How can code |
michael@0 | 74 | // from the code be factored? |
michael@0 | 75 | |
michael@0 | 76 | nsresult |
michael@0 | 77 | NS_NewXMLContentSink(nsIXMLContentSink** aResult, |
michael@0 | 78 | nsIDocument* aDoc, |
michael@0 | 79 | nsIURI* aURI, |
michael@0 | 80 | nsISupports* aContainer, |
michael@0 | 81 | nsIChannel* aChannel) |
michael@0 | 82 | { |
michael@0 | 83 | NS_PRECONDITION(nullptr != aResult, "null ptr"); |
michael@0 | 84 | if (nullptr == aResult) { |
michael@0 | 85 | return NS_ERROR_NULL_POINTER; |
michael@0 | 86 | } |
michael@0 | 87 | nsXMLContentSink* it = new nsXMLContentSink(); |
michael@0 | 88 | |
michael@0 | 89 | nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it; |
michael@0 | 90 | nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel); |
michael@0 | 91 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 92 | |
michael@0 | 93 | return CallQueryInterface(it, aResult); |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | nsXMLContentSink::nsXMLContentSink() |
michael@0 | 97 | : mConstrainSize(true), |
michael@0 | 98 | mPrettyPrintXML(true) |
michael@0 | 99 | { |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | nsXMLContentSink::~nsXMLContentSink() |
michael@0 | 103 | { |
michael@0 | 104 | if (mText) { |
michael@0 | 105 | PR_Free(mText); // Doesn't null out, unlike PR_FREEIF |
michael@0 | 106 | } |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | nsresult |
michael@0 | 110 | nsXMLContentSink::Init(nsIDocument* aDoc, |
michael@0 | 111 | nsIURI* aURI, |
michael@0 | 112 | nsISupports* aContainer, |
michael@0 | 113 | nsIChannel* aChannel) |
michael@0 | 114 | { |
michael@0 | 115 | nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel); |
michael@0 | 116 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 117 | |
michael@0 | 118 | aDoc->AddObserver(this); |
michael@0 | 119 | mIsDocumentObserver = true; |
michael@0 | 120 | |
michael@0 | 121 | if (!mDocShell) { |
michael@0 | 122 | mPrettyPrintXML = false; |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | mState = eXMLContentSinkState_InProlog; |
michael@0 | 126 | mDocElement = nullptr; |
michael@0 | 127 | |
michael@0 | 128 | return NS_OK; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLContentSink) |
michael@0 | 132 | NS_INTERFACE_MAP_ENTRY(nsIContentSink) |
michael@0 | 133 | NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink) |
michael@0 | 134 | NS_INTERFACE_MAP_ENTRY(nsIExpatSink) |
michael@0 | 135 | NS_INTERFACE_MAP_ENTRY(nsITransformObserver) |
michael@0 | 136 | NS_INTERFACE_MAP_END_INHERITING(nsContentSink) |
michael@0 | 137 | |
michael@0 | 138 | NS_IMPL_ADDREF_INHERITED(nsXMLContentSink, nsContentSink) |
michael@0 | 139 | NS_IMPL_RELEASE_INHERITED(nsXMLContentSink, nsContentSink) |
michael@0 | 140 | |
michael@0 | 141 | NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLContentSink) |
michael@0 | 142 | |
michael@0 | 143 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLContentSink, |
michael@0 | 144 | nsContentSink) |
michael@0 | 145 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentHead) |
michael@0 | 146 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocElement) |
michael@0 | 147 | for (uint32_t i = 0, count = tmp->mContentStack.Length(); i < count; i++) { |
michael@0 | 148 | const StackNode& node = tmp->mContentStack.ElementAt(i); |
michael@0 | 149 | cb.NoteXPCOMChild(node.mContent); |
michael@0 | 150 | } |
michael@0 | 151 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 152 | |
michael@0 | 153 | // nsIContentSink |
michael@0 | 154 | NS_IMETHODIMP |
michael@0 | 155 | nsXMLContentSink::WillParse(void) |
michael@0 | 156 | { |
michael@0 | 157 | return WillParseImpl(); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | NS_IMETHODIMP |
michael@0 | 161 | nsXMLContentSink::WillBuildModel(nsDTDMode aDTDMode) |
michael@0 | 162 | { |
michael@0 | 163 | WillBuildModelImpl(); |
michael@0 | 164 | |
michael@0 | 165 | // Notify document that the load is beginning |
michael@0 | 166 | mDocument->BeginLoad(); |
michael@0 | 167 | |
michael@0 | 168 | // Check for correct load-command for maybe prettyprinting |
michael@0 | 169 | if (mPrettyPrintXML) { |
michael@0 | 170 | nsAutoCString command; |
michael@0 | 171 | GetParser()->GetCommand(command); |
michael@0 | 172 | if (!command.EqualsLiteral("view")) { |
michael@0 | 173 | mPrettyPrintXML = false; |
michael@0 | 174 | } |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | return NS_OK; |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | bool |
michael@0 | 181 | nsXMLContentSink::CanStillPrettyPrint() |
michael@0 | 182 | { |
michael@0 | 183 | return mPrettyPrintXML && |
michael@0 | 184 | (!mPrettyPrintHasFactoredElements || mPrettyPrintHasSpecialRoot); |
michael@0 | 185 | } |
michael@0 | 186 | |
michael@0 | 187 | nsresult |
michael@0 | 188 | nsXMLContentSink::MaybePrettyPrint() |
michael@0 | 189 | { |
michael@0 | 190 | if (!CanStillPrettyPrint()) { |
michael@0 | 191 | mPrettyPrintXML = false; |
michael@0 | 192 | |
michael@0 | 193 | return NS_OK; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | // stop observing in order to avoid crashing when replacing content |
michael@0 | 197 | mDocument->RemoveObserver(this); |
michael@0 | 198 | mIsDocumentObserver = false; |
michael@0 | 199 | |
michael@0 | 200 | // Reenable the CSSLoader so that the prettyprinting stylesheets can load |
michael@0 | 201 | if (mCSSLoader) { |
michael@0 | 202 | mCSSLoader->SetEnabled(true); |
michael@0 | 203 | } |
michael@0 | 204 | |
michael@0 | 205 | nsRefPtr<nsXMLPrettyPrinter> printer; |
michael@0 | 206 | nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer)); |
michael@0 | 207 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 208 | |
michael@0 | 209 | bool isPrettyPrinting; |
michael@0 | 210 | rv = printer->PrettyPrint(mDocument, &isPrettyPrinting); |
michael@0 | 211 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 212 | |
michael@0 | 213 | mPrettyPrinting = isPrettyPrinting; |
michael@0 | 214 | return NS_OK; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | static void |
michael@0 | 218 | CheckXSLTParamPI(nsIDOMProcessingInstruction* aPi, |
michael@0 | 219 | nsIDocumentTransformer* aProcessor, |
michael@0 | 220 | nsIDocument* aDocument) |
michael@0 | 221 | { |
michael@0 | 222 | nsAutoString target, data; |
michael@0 | 223 | aPi->GetTarget(target); |
michael@0 | 224 | |
michael@0 | 225 | // Check for namespace declarations |
michael@0 | 226 | if (target.EqualsLiteral("xslt-param-namespace")) { |
michael@0 | 227 | aPi->GetData(data); |
michael@0 | 228 | nsAutoString prefix, namespaceAttr; |
michael@0 | 229 | nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::prefix, |
michael@0 | 230 | prefix); |
michael@0 | 231 | if (!prefix.IsEmpty() && |
michael@0 | 232 | nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace, |
michael@0 | 233 | namespaceAttr)) { |
michael@0 | 234 | aProcessor->AddXSLTParamNamespace(prefix, namespaceAttr); |
michael@0 | 235 | } |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | // Check for actual parameters |
michael@0 | 239 | else if (target.EqualsLiteral("xslt-param")) { |
michael@0 | 240 | aPi->GetData(data); |
michael@0 | 241 | nsAutoString name, namespaceAttr, select, value; |
michael@0 | 242 | nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::name, |
michael@0 | 243 | name); |
michael@0 | 244 | nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace, |
michael@0 | 245 | namespaceAttr); |
michael@0 | 246 | if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::select, select)) { |
michael@0 | 247 | select.SetIsVoid(true); |
michael@0 | 248 | } |
michael@0 | 249 | if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::value, value)) { |
michael@0 | 250 | value.SetIsVoid(true); |
michael@0 | 251 | } |
michael@0 | 252 | if (!name.IsEmpty()) { |
michael@0 | 253 | nsCOMPtr<nsIDOMNode> doc = do_QueryInterface(aDocument); |
michael@0 | 254 | aProcessor->AddXSLTParam(name, namespaceAttr, select, value, doc); |
michael@0 | 255 | } |
michael@0 | 256 | } |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | NS_IMETHODIMP |
michael@0 | 260 | nsXMLContentSink::DidBuildModel(bool aTerminated) |
michael@0 | 261 | { |
michael@0 | 262 | if (!mParser) { |
michael@0 | 263 | // If mParser is null, this parse has already been terminated and must |
michael@0 | 264 | // not been terminated again. However, nsDocument may still think that |
michael@0 | 265 | // the parse has not been terminated and call back into here in the case |
michael@0 | 266 | // where the XML parser has finished but the XSLT transform associated |
michael@0 | 267 | // with the document has not. |
michael@0 | 268 | return NS_OK; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | DidBuildModelImpl(aTerminated); |
michael@0 | 272 | |
michael@0 | 273 | if (mXSLTProcessor) { |
michael@0 | 274 | // stop observing in order to avoid crashing when replacing content |
michael@0 | 275 | mDocument->RemoveObserver(this); |
michael@0 | 276 | mIsDocumentObserver = false; |
michael@0 | 277 | |
michael@0 | 278 | // Check for xslt-param and xslt-param-namespace PIs |
michael@0 | 279 | for (nsIContent* child = mDocument->GetFirstChild(); |
michael@0 | 280 | child; |
michael@0 | 281 | child = child->GetNextSibling()) { |
michael@0 | 282 | if (child->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) { |
michael@0 | 283 | nsCOMPtr<nsIDOMProcessingInstruction> pi = do_QueryInterface(child); |
michael@0 | 284 | CheckXSLTParamPI(pi, mXSLTProcessor, mDocument); |
michael@0 | 285 | } |
michael@0 | 286 | else if (child->IsElement()) { |
michael@0 | 287 | // Only honor PIs in the prolog |
michael@0 | 288 | break; |
michael@0 | 289 | } |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | nsCOMPtr<nsIDOMDocument> currentDOMDoc(do_QueryInterface(mDocument)); |
michael@0 | 293 | mXSLTProcessor->SetSourceContentModel(currentDOMDoc); |
michael@0 | 294 | // Since the processor now holds a reference to us we drop our reference |
michael@0 | 295 | // to it to avoid owning cycles |
michael@0 | 296 | mXSLTProcessor = nullptr; |
michael@0 | 297 | } |
michael@0 | 298 | else { |
michael@0 | 299 | // Kick off layout for non-XSLT transformed documents. |
michael@0 | 300 | |
michael@0 | 301 | // Check if we want to prettyprint |
michael@0 | 302 | MaybePrettyPrint(); |
michael@0 | 303 | |
michael@0 | 304 | bool startLayout = true; |
michael@0 | 305 | |
michael@0 | 306 | if (mPrettyPrinting) { |
michael@0 | 307 | NS_ASSERTION(!mPendingSheetCount, "Shouldn't have pending sheets here!"); |
michael@0 | 308 | |
michael@0 | 309 | // We're pretty-printing now. See whether we should wait up on |
michael@0 | 310 | // stylesheet loads |
michael@0 | 311 | if (mDocument->CSSLoader()->HasPendingLoads() && |
michael@0 | 312 | NS_SUCCEEDED(mDocument->CSSLoader()->AddObserver(this))) { |
michael@0 | 313 | // wait for those sheets to load |
michael@0 | 314 | startLayout = false; |
michael@0 | 315 | } |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | if (startLayout) { |
michael@0 | 319 | StartLayout(false); |
michael@0 | 320 | |
michael@0 | 321 | ScrollToRef(); |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | mDocument->RemoveObserver(this); |
michael@0 | 325 | mIsDocumentObserver = false; |
michael@0 | 326 | |
michael@0 | 327 | mDocument->EndLoad(); |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | DropParserAndPerfHint(); |
michael@0 | 331 | |
michael@0 | 332 | return NS_OK; |
michael@0 | 333 | } |
michael@0 | 334 | |
michael@0 | 335 | NS_IMETHODIMP |
michael@0 | 336 | nsXMLContentSink::OnDocumentCreated(nsIDocument* aResultDocument) |
michael@0 | 337 | { |
michael@0 | 338 | NS_ENSURE_ARG(aResultDocument); |
michael@0 | 339 | |
michael@0 | 340 | nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aResultDocument); |
michael@0 | 341 | if (htmlDoc) { |
michael@0 | 342 | htmlDoc->SetDocWriteDisabled(true); |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | nsCOMPtr<nsIContentViewer> contentViewer; |
michael@0 | 346 | mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); |
michael@0 | 347 | if (contentViewer) { |
michael@0 | 348 | return contentViewer->SetDocumentInternal(aResultDocument, true); |
michael@0 | 349 | } |
michael@0 | 350 | return NS_OK; |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | NS_IMETHODIMP |
michael@0 | 354 | nsXMLContentSink::OnTransformDone(nsresult aResult, |
michael@0 | 355 | nsIDocument* aResultDocument) |
michael@0 | 356 | { |
michael@0 | 357 | NS_ASSERTION(NS_FAILED(aResult) || aResultDocument, |
michael@0 | 358 | "Don't notify about transform success without a document."); |
michael@0 | 359 | |
michael@0 | 360 | nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aResultDocument); |
michael@0 | 361 | |
michael@0 | 362 | nsCOMPtr<nsIContentViewer> contentViewer; |
michael@0 | 363 | mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); |
michael@0 | 364 | |
michael@0 | 365 | if (NS_FAILED(aResult) && contentViewer) { |
michael@0 | 366 | // Transform failed. |
michael@0 | 367 | if (domDoc) { |
michael@0 | 368 | aResultDocument->SetMayStartLayout(false); |
michael@0 | 369 | // We have an error document. |
michael@0 | 370 | contentViewer->SetDOMDocument(domDoc); |
michael@0 | 371 | } |
michael@0 | 372 | else { |
michael@0 | 373 | // We don't have an error document, display the |
michael@0 | 374 | // untransformed source document. |
michael@0 | 375 | nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(mDocument); |
michael@0 | 376 | contentViewer->SetDOMDocument(document); |
michael@0 | 377 | } |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | nsCOMPtr<nsIDocument> originalDocument = mDocument; |
michael@0 | 381 | if (NS_SUCCEEDED(aResult) || aResultDocument) { |
michael@0 | 382 | // Transform succeeded or it failed and we have an error |
michael@0 | 383 | // document to display. |
michael@0 | 384 | mDocument = aResultDocument; |
michael@0 | 385 | nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument); |
michael@0 | 386 | if (htmlDoc) { |
michael@0 | 387 | htmlDoc->SetDocWriteDisabled(false); |
michael@0 | 388 | } |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | // Notify document observers that all the content has been stuck |
michael@0 | 392 | // into the document. |
michael@0 | 393 | // XXX do we need to notify for things like PIs? Or just the |
michael@0 | 394 | // documentElement? |
michael@0 | 395 | nsIContent *rootElement = mDocument->GetRootElement(); |
michael@0 | 396 | if (rootElement) { |
michael@0 | 397 | NS_ASSERTION(mDocument->IndexOf(rootElement) != -1, |
michael@0 | 398 | "rootElement not in doc?"); |
michael@0 | 399 | mDocument->BeginUpdate(UPDATE_CONTENT_MODEL); |
michael@0 | 400 | nsNodeUtils::ContentInserted(mDocument, rootElement, |
michael@0 | 401 | mDocument->IndexOf(rootElement)); |
michael@0 | 402 | mDocument->EndUpdate(UPDATE_CONTENT_MODEL); |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | // Start the layout process |
michael@0 | 406 | StartLayout(false); |
michael@0 | 407 | |
michael@0 | 408 | ScrollToRef(); |
michael@0 | 409 | |
michael@0 | 410 | originalDocument->EndLoad(); |
michael@0 | 411 | |
michael@0 | 412 | return NS_OK; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | NS_IMETHODIMP |
michael@0 | 416 | nsXMLContentSink::StyleSheetLoaded(nsCSSStyleSheet* aSheet, |
michael@0 | 417 | bool aWasAlternate, |
michael@0 | 418 | nsresult aStatus) |
michael@0 | 419 | { |
michael@0 | 420 | if (!mPrettyPrinting) { |
michael@0 | 421 | return nsContentSink::StyleSheetLoaded(aSheet, aWasAlternate, aStatus); |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | if (!mDocument->CSSLoader()->HasPendingLoads()) { |
michael@0 | 425 | mDocument->CSSLoader()->RemoveObserver(this); |
michael@0 | 426 | StartLayout(false); |
michael@0 | 427 | ScrollToRef(); |
michael@0 | 428 | } |
michael@0 | 429 | |
michael@0 | 430 | return NS_OK; |
michael@0 | 431 | } |
michael@0 | 432 | |
michael@0 | 433 | NS_IMETHODIMP |
michael@0 | 434 | nsXMLContentSink::WillInterrupt(void) |
michael@0 | 435 | { |
michael@0 | 436 | return WillInterruptImpl(); |
michael@0 | 437 | } |
michael@0 | 438 | |
michael@0 | 439 | NS_IMETHODIMP |
michael@0 | 440 | nsXMLContentSink::WillResume(void) |
michael@0 | 441 | { |
michael@0 | 442 | return WillResumeImpl(); |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | NS_IMETHODIMP |
michael@0 | 446 | nsXMLContentSink::SetParser(nsParserBase* aParser) |
michael@0 | 447 | { |
michael@0 | 448 | NS_PRECONDITION(aParser, "Should have a parser here!"); |
michael@0 | 449 | mParser = aParser; |
michael@0 | 450 | return NS_OK; |
michael@0 | 451 | } |
michael@0 | 452 | |
michael@0 | 453 | nsresult |
michael@0 | 454 | nsXMLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount, |
michael@0 | 455 | nsINodeInfo* aNodeInfo, uint32_t aLineNumber, |
michael@0 | 456 | nsIContent** aResult, bool* aAppendContent, |
michael@0 | 457 | FromParser aFromParser) |
michael@0 | 458 | { |
michael@0 | 459 | NS_ASSERTION(aNodeInfo, "can't create element without nodeinfo"); |
michael@0 | 460 | |
michael@0 | 461 | *aResult = nullptr; |
michael@0 | 462 | *aAppendContent = true; |
michael@0 | 463 | nsresult rv = NS_OK; |
michael@0 | 464 | |
michael@0 | 465 | nsCOMPtr<nsINodeInfo> ni = aNodeInfo; |
michael@0 | 466 | nsCOMPtr<Element> content; |
michael@0 | 467 | rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser); |
michael@0 | 468 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 469 | |
michael@0 | 470 | if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) |
michael@0 | 471 | || aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG) |
michael@0 | 472 | ) { |
michael@0 | 473 | nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content); |
michael@0 | 474 | sele->SetScriptLineNumber(aLineNumber); |
michael@0 | 475 | sele->SetCreatorParser(GetParser()); |
michael@0 | 476 | mConstrainSize = false; |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | // XHTML needs some special attention |
michael@0 | 480 | if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { |
michael@0 | 481 | mPrettyPrintHasFactoredElements = true; |
michael@0 | 482 | } |
michael@0 | 483 | else { |
michael@0 | 484 | // If we care, find out if we just used a special factory. |
michael@0 | 485 | if (!mPrettyPrintHasFactoredElements && !mPrettyPrintHasSpecialRoot && |
michael@0 | 486 | mPrettyPrintXML) { |
michael@0 | 487 | mPrettyPrintHasFactoredElements = |
michael@0 | 488 | nsContentUtils::NameSpaceManager()-> |
michael@0 | 489 | HasElementCreator(aNodeInfo->NamespaceID()); |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | if (!aNodeInfo->NamespaceEquals(kNameSpaceID_SVG)) { |
michael@0 | 493 | content.forget(aResult); |
michael@0 | 494 | |
michael@0 | 495 | return NS_OK; |
michael@0 | 496 | } |
michael@0 | 497 | } |
michael@0 | 498 | |
michael@0 | 499 | if (aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) || |
michael@0 | 500 | aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) || |
michael@0 | 501 | aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) { |
michael@0 | 502 | nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(content)); |
michael@0 | 503 | if (ssle) { |
michael@0 | 504 | ssle->InitStyleLinkElement(false); |
michael@0 | 505 | if (aFromParser) { |
michael@0 | 506 | ssle->SetEnableUpdates(false); |
michael@0 | 507 | } |
michael@0 | 508 | if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) { |
michael@0 | 509 | ssle->SetLineNumber(aFromParser ? aLineNumber : 0); |
michael@0 | 510 | } |
michael@0 | 511 | } |
michael@0 | 512 | } |
michael@0 | 513 | |
michael@0 | 514 | content.forget(aResult); |
michael@0 | 515 | |
michael@0 | 516 | return NS_OK; |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | |
michael@0 | 520 | nsresult |
michael@0 | 521 | nsXMLContentSink::CloseElement(nsIContent* aContent) |
michael@0 | 522 | { |
michael@0 | 523 | NS_ASSERTION(aContent, "missing element to close"); |
michael@0 | 524 | |
michael@0 | 525 | nsINodeInfo *nodeInfo = aContent->NodeInfo(); |
michael@0 | 526 | |
michael@0 | 527 | // Some HTML nodes need DoneAddingChildren() called to initialize |
michael@0 | 528 | // properly (eg form state restoration). |
michael@0 | 529 | if ((nodeInfo->NamespaceID() == kNameSpaceID_XHTML && |
michael@0 | 530 | (nodeInfo->NameAtom() == nsGkAtoms::select || |
michael@0 | 531 | nodeInfo->NameAtom() == nsGkAtoms::textarea || |
michael@0 | 532 | nodeInfo->NameAtom() == nsGkAtoms::video || |
michael@0 | 533 | nodeInfo->NameAtom() == nsGkAtoms::audio || |
michael@0 | 534 | nodeInfo->NameAtom() == nsGkAtoms::object || |
michael@0 | 535 | nodeInfo->NameAtom() == nsGkAtoms::applet)) |
michael@0 | 536 | || nodeInfo->NameAtom() == nsGkAtoms::title |
michael@0 | 537 | ) { |
michael@0 | 538 | aContent->DoneAddingChildren(HaveNotifiedForCurrentContent()); |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | if (IsMonolithicContainer(nodeInfo)) { |
michael@0 | 542 | mInMonolithicContainer--; |
michael@0 | 543 | } |
michael@0 | 544 | |
michael@0 | 545 | if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) && |
michael@0 | 546 | !nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) { |
michael@0 | 547 | return NS_OK; |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | if (nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) |
michael@0 | 551 | || nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG) |
michael@0 | 552 | ) { |
michael@0 | 553 | mConstrainSize = true; |
michael@0 | 554 | nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent); |
michael@0 | 555 | |
michael@0 | 556 | if (mPreventScriptExecution) { |
michael@0 | 557 | sele->PreventExecution(); |
michael@0 | 558 | return NS_OK; |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | // Always check the clock in nsContentSink right after a script |
michael@0 | 562 | StopDeflecting(); |
michael@0 | 563 | |
michael@0 | 564 | // Now tell the script that it's ready to go. This may execute the script |
michael@0 | 565 | // or return true, or neither if the script doesn't need executing. |
michael@0 | 566 | bool block = sele->AttemptToExecute(); |
michael@0 | 567 | |
michael@0 | 568 | // If the parser got blocked, make sure to return the appropriate rv. |
michael@0 | 569 | // I'm not sure if this is actually needed or not. |
michael@0 | 570 | if (mParser && !mParser->IsParserEnabled()) { |
michael@0 | 571 | // XXX The HTML sink doesn't call BlockParser here, why do we? |
michael@0 | 572 | GetParser()->BlockParser(); |
michael@0 | 573 | block = true; |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | return block ? NS_ERROR_HTMLPARSER_BLOCK : NS_OK; |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | nsresult rv = NS_OK; |
michael@0 | 580 | if (nodeInfo->Equals(nsGkAtoms::meta, kNameSpaceID_XHTML) && |
michael@0 | 581 | // Need to check here to make sure this meta tag does not set |
michael@0 | 582 | // mPrettyPrintXML to false when we have a special root! |
michael@0 | 583 | (!mPrettyPrintXML || !mPrettyPrintHasSpecialRoot)) { |
michael@0 | 584 | rv = ProcessMETATag(aContent); |
michael@0 | 585 | } |
michael@0 | 586 | else if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) || |
michael@0 | 587 | nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) || |
michael@0 | 588 | nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) { |
michael@0 | 589 | nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aContent)); |
michael@0 | 590 | if (ssle) { |
michael@0 | 591 | ssle->SetEnableUpdates(true); |
michael@0 | 592 | bool willNotify; |
michael@0 | 593 | bool isAlternate; |
michael@0 | 594 | rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, |
michael@0 | 595 | &willNotify, |
michael@0 | 596 | &isAlternate); |
michael@0 | 597 | if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) { |
michael@0 | 598 | ++mPendingSheetCount; |
michael@0 | 599 | mScriptLoader->AddExecuteBlocker(); |
michael@0 | 600 | } |
michael@0 | 601 | } |
michael@0 | 602 | // Look for <link rel="dns-prefetch" href="hostname"> |
michael@0 | 603 | // and look for <link rel="next" href="hostname"> like in HTML sink |
michael@0 | 604 | if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) { |
michael@0 | 605 | nsAutoString relVal; |
michael@0 | 606 | aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal); |
michael@0 | 607 | if (!relVal.IsEmpty()) { |
michael@0 | 608 | uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(relVal); |
michael@0 | 609 | bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH; |
michael@0 | 610 | if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) { |
michael@0 | 611 | nsAutoString hrefVal; |
michael@0 | 612 | aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); |
michael@0 | 613 | if (!hrefVal.IsEmpty()) { |
michael@0 | 614 | PrefetchHref(hrefVal, aContent, hasPrefetch); |
michael@0 | 615 | } |
michael@0 | 616 | } |
michael@0 | 617 | if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) { |
michael@0 | 618 | nsAutoString hrefVal; |
michael@0 | 619 | aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); |
michael@0 | 620 | if (!hrefVal.IsEmpty()) { |
michael@0 | 621 | PrefetchDNS(hrefVal); |
michael@0 | 622 | } |
michael@0 | 623 | } |
michael@0 | 624 | } |
michael@0 | 625 | } |
michael@0 | 626 | } |
michael@0 | 627 | |
michael@0 | 628 | return rv; |
michael@0 | 629 | } |
michael@0 | 630 | |
michael@0 | 631 | nsresult |
michael@0 | 632 | nsXMLContentSink::AddContentAsLeaf(nsIContent *aContent) |
michael@0 | 633 | { |
michael@0 | 634 | nsresult result = NS_OK; |
michael@0 | 635 | |
michael@0 | 636 | if ((eXMLContentSinkState_InProlog == mState) || |
michael@0 | 637 | (eXMLContentSinkState_InEpilog == mState)) { |
michael@0 | 638 | NS_ASSERTION(mDocument, "Fragments have no prolog or epilog"); |
michael@0 | 639 | mDocument->AppendChildTo(aContent, false); |
michael@0 | 640 | } |
michael@0 | 641 | else { |
michael@0 | 642 | nsCOMPtr<nsIContent> parent = GetCurrentContent(); |
michael@0 | 643 | |
michael@0 | 644 | if (parent) { |
michael@0 | 645 | result = parent->AppendChildTo(aContent, false); |
michael@0 | 646 | } |
michael@0 | 647 | } |
michael@0 | 648 | return result; |
michael@0 | 649 | } |
michael@0 | 650 | |
michael@0 | 651 | // Create an XML parser and an XSL content sink and start parsing |
michael@0 | 652 | // the XSL stylesheet located at the given URI. |
michael@0 | 653 | nsresult |
michael@0 | 654 | nsXMLContentSink::LoadXSLStyleSheet(nsIURI* aUrl) |
michael@0 | 655 | { |
michael@0 | 656 | nsCOMPtr<nsIDocumentTransformer> processor = |
michael@0 | 657 | do_CreateInstance("@mozilla.org/document-transformer;1?type=xslt"); |
michael@0 | 658 | if (!processor) { |
michael@0 | 659 | // No XSLT processor available, continue normal document loading |
michael@0 | 660 | return NS_OK; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | processor->Init(mDocument->NodePrincipal()); |
michael@0 | 664 | processor->SetTransformObserver(this); |
michael@0 | 665 | |
michael@0 | 666 | nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup(); |
michael@0 | 667 | if (!loadGroup) { |
michael@0 | 668 | return NS_ERROR_FAILURE; |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | if (NS_SUCCEEDED(processor->LoadStyleSheet(aUrl, loadGroup))) { |
michael@0 | 672 | mXSLTProcessor.swap(processor); |
michael@0 | 673 | } |
michael@0 | 674 | |
michael@0 | 675 | // Intentionally ignore errors here, we should continue loading the |
michael@0 | 676 | // XML document whether we're able to load the XSLT stylesheet or |
michael@0 | 677 | // not. |
michael@0 | 678 | |
michael@0 | 679 | return NS_OK; |
michael@0 | 680 | } |
michael@0 | 681 | |
michael@0 | 682 | nsresult |
michael@0 | 683 | nsXMLContentSink::ProcessStyleLink(nsIContent* aElement, |
michael@0 | 684 | const nsSubstring& aHref, |
michael@0 | 685 | bool aAlternate, |
michael@0 | 686 | const nsSubstring& aTitle, |
michael@0 | 687 | const nsSubstring& aType, |
michael@0 | 688 | const nsSubstring& aMedia) |
michael@0 | 689 | { |
michael@0 | 690 | nsresult rv = NS_OK; |
michael@0 | 691 | mPrettyPrintXML = false; |
michael@0 | 692 | |
michael@0 | 693 | nsAutoCString cmd; |
michael@0 | 694 | if (mParser) |
michael@0 | 695 | GetParser()->GetCommand(cmd); |
michael@0 | 696 | if (cmd.EqualsASCII(kLoadAsData)) |
michael@0 | 697 | return NS_OK; // Do not load stylesheets when loading as data |
michael@0 | 698 | |
michael@0 | 699 | NS_ConvertUTF16toUTF8 type(aType); |
michael@0 | 700 | if (type.EqualsIgnoreCase(TEXT_XSL) || |
michael@0 | 701 | type.EqualsIgnoreCase(APPLICATION_XSLT_XML) || |
michael@0 | 702 | type.EqualsIgnoreCase(TEXT_XML) || |
michael@0 | 703 | type.EqualsIgnoreCase(APPLICATION_XML)) { |
michael@0 | 704 | if (aAlternate) { |
michael@0 | 705 | // don't load alternate XSLT |
michael@0 | 706 | return NS_OK; |
michael@0 | 707 | } |
michael@0 | 708 | // LoadXSLStyleSheet needs a mDocShell. |
michael@0 | 709 | if (!mDocShell) |
michael@0 | 710 | return NS_OK; |
michael@0 | 711 | |
michael@0 | 712 | nsCOMPtr<nsIURI> url; |
michael@0 | 713 | rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr, |
michael@0 | 714 | mDocument->GetDocBaseURI()); |
michael@0 | 715 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 716 | |
michael@0 | 717 | // Do security check |
michael@0 | 718 | nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager(); |
michael@0 | 719 | rv = secMan-> |
michael@0 | 720 | CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), url, |
michael@0 | 721 | nsIScriptSecurityManager::ALLOW_CHROME); |
michael@0 | 722 | NS_ENSURE_SUCCESS(rv, NS_OK); |
michael@0 | 723 | |
michael@0 | 724 | // Do content policy check |
michael@0 | 725 | int16_t decision = nsIContentPolicy::ACCEPT; |
michael@0 | 726 | rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XSLT, |
michael@0 | 727 | url, |
michael@0 | 728 | mDocument->NodePrincipal(), |
michael@0 | 729 | aElement, |
michael@0 | 730 | type, |
michael@0 | 731 | nullptr, |
michael@0 | 732 | &decision, |
michael@0 | 733 | nsContentUtils::GetContentPolicy(), |
michael@0 | 734 | nsContentUtils::GetSecurityManager()); |
michael@0 | 735 | |
michael@0 | 736 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 737 | |
michael@0 | 738 | if (NS_CP_REJECTED(decision)) { |
michael@0 | 739 | return NS_OK; |
michael@0 | 740 | } |
michael@0 | 741 | |
michael@0 | 742 | return LoadXSLStyleSheet(url); |
michael@0 | 743 | } |
michael@0 | 744 | |
michael@0 | 745 | // Let nsContentSink deal with css. |
michael@0 | 746 | rv = nsContentSink::ProcessStyleLink(aElement, aHref, aAlternate, |
michael@0 | 747 | aTitle, aType, aMedia); |
michael@0 | 748 | |
michael@0 | 749 | // nsContentSink::ProcessStyleLink handles the bookkeeping here wrt |
michael@0 | 750 | // pending sheets. |
michael@0 | 751 | |
michael@0 | 752 | return rv; |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | NS_IMETHODIMP |
michael@0 | 756 | nsXMLContentSink::SetDocumentCharset(nsACString& aCharset) |
michael@0 | 757 | { |
michael@0 | 758 | if (mDocument) { |
michael@0 | 759 | mDocument->SetDocumentCharacterSet(aCharset); |
michael@0 | 760 | } |
michael@0 | 761 | |
michael@0 | 762 | return NS_OK; |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | nsISupports * |
michael@0 | 766 | nsXMLContentSink::GetTarget() |
michael@0 | 767 | { |
michael@0 | 768 | return mDocument; |
michael@0 | 769 | } |
michael@0 | 770 | |
michael@0 | 771 | bool |
michael@0 | 772 | nsXMLContentSink::IsScriptExecuting() |
michael@0 | 773 | { |
michael@0 | 774 | return IsScriptExecutingImpl(); |
michael@0 | 775 | } |
michael@0 | 776 | |
michael@0 | 777 | nsresult |
michael@0 | 778 | nsXMLContentSink::FlushText(bool aReleaseTextNode) |
michael@0 | 779 | { |
michael@0 | 780 | nsresult rv = NS_OK; |
michael@0 | 781 | |
michael@0 | 782 | if (mTextLength != 0) { |
michael@0 | 783 | if (mLastTextNode) { |
michael@0 | 784 | if ((mLastTextNodeSize + mTextLength) > mTextSize && !mXSLTProcessor) { |
michael@0 | 785 | mLastTextNodeSize = 0; |
michael@0 | 786 | mLastTextNode = nullptr; |
michael@0 | 787 | FlushText(aReleaseTextNode); |
michael@0 | 788 | } else { |
michael@0 | 789 | bool notify = HaveNotifiedForCurrentContent(); |
michael@0 | 790 | // We could probably always increase mInNotification here since |
michael@0 | 791 | // if AppendText doesn't notify it shouldn't trigger evil code. |
michael@0 | 792 | // But just in case it does, we don't want to mask any notifications. |
michael@0 | 793 | if (notify) { |
michael@0 | 794 | ++mInNotification; |
michael@0 | 795 | } |
michael@0 | 796 | rv = mLastTextNode->AppendText(mText, mTextLength, notify); |
michael@0 | 797 | if (notify) { |
michael@0 | 798 | --mInNotification; |
michael@0 | 799 | } |
michael@0 | 800 | |
michael@0 | 801 | mLastTextNodeSize += mTextLength; |
michael@0 | 802 | mTextLength = 0; |
michael@0 | 803 | } |
michael@0 | 804 | } else { |
michael@0 | 805 | nsRefPtr<nsTextNode> textContent = new nsTextNode(mNodeInfoManager); |
michael@0 | 806 | |
michael@0 | 807 | mLastTextNode = textContent; |
michael@0 | 808 | |
michael@0 | 809 | // Set the text in the text node |
michael@0 | 810 | textContent->SetText(mText, mTextLength, false); |
michael@0 | 811 | mLastTextNodeSize += mTextLength; |
michael@0 | 812 | mTextLength = 0; |
michael@0 | 813 | |
michael@0 | 814 | // Add text to its parent |
michael@0 | 815 | rv = AddContentAsLeaf(textContent); |
michael@0 | 816 | } |
michael@0 | 817 | } |
michael@0 | 818 | |
michael@0 | 819 | if (aReleaseTextNode) { |
michael@0 | 820 | mLastTextNodeSize = 0; |
michael@0 | 821 | mLastTextNode = nullptr; |
michael@0 | 822 | } |
michael@0 | 823 | |
michael@0 | 824 | return rv; |
michael@0 | 825 | } |
michael@0 | 826 | |
michael@0 | 827 | nsIContent* |
michael@0 | 828 | nsXMLContentSink::GetCurrentContent() |
michael@0 | 829 | { |
michael@0 | 830 | if (mContentStack.Length() == 0) { |
michael@0 | 831 | return nullptr; |
michael@0 | 832 | } |
michael@0 | 833 | return GetCurrentStackNode()->mContent; |
michael@0 | 834 | } |
michael@0 | 835 | |
michael@0 | 836 | StackNode* |
michael@0 | 837 | nsXMLContentSink::GetCurrentStackNode() |
michael@0 | 838 | { |
michael@0 | 839 | int32_t count = mContentStack.Length(); |
michael@0 | 840 | return count != 0 ? &mContentStack[count-1] : nullptr; |
michael@0 | 841 | } |
michael@0 | 842 | |
michael@0 | 843 | |
michael@0 | 844 | nsresult |
michael@0 | 845 | nsXMLContentSink::PushContent(nsIContent *aContent) |
michael@0 | 846 | { |
michael@0 | 847 | NS_PRECONDITION(aContent, "Null content being pushed!"); |
michael@0 | 848 | StackNode *sn = mContentStack.AppendElement(); |
michael@0 | 849 | NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 850 | |
michael@0 | 851 | sn->mContent = aContent; |
michael@0 | 852 | sn->mNumFlushed = 0; |
michael@0 | 853 | return NS_OK; |
michael@0 | 854 | } |
michael@0 | 855 | |
michael@0 | 856 | void |
michael@0 | 857 | nsXMLContentSink::PopContent() |
michael@0 | 858 | { |
michael@0 | 859 | int32_t count = mContentStack.Length(); |
michael@0 | 860 | |
michael@0 | 861 | if (count == 0) { |
michael@0 | 862 | NS_WARNING("Popping empty stack"); |
michael@0 | 863 | return; |
michael@0 | 864 | } |
michael@0 | 865 | |
michael@0 | 866 | mContentStack.RemoveElementAt(count - 1); |
michael@0 | 867 | } |
michael@0 | 868 | |
michael@0 | 869 | bool |
michael@0 | 870 | nsXMLContentSink::HaveNotifiedForCurrentContent() const |
michael@0 | 871 | { |
michael@0 | 872 | uint32_t stackLength = mContentStack.Length(); |
michael@0 | 873 | if (stackLength) { |
michael@0 | 874 | const StackNode& stackNode = mContentStack[stackLength - 1]; |
michael@0 | 875 | nsIContent* parent = stackNode.mContent; |
michael@0 | 876 | return stackNode.mNumFlushed == parent->GetChildCount(); |
michael@0 | 877 | } |
michael@0 | 878 | return true; |
michael@0 | 879 | } |
michael@0 | 880 | |
michael@0 | 881 | void |
michael@0 | 882 | nsXMLContentSink::MaybeStartLayout(bool aIgnorePendingSheets) |
michael@0 | 883 | { |
michael@0 | 884 | // XXXbz if aIgnorePendingSheets is true, what should we do when |
michael@0 | 885 | // mXSLTProcessor or CanStillPrettyPrint()? |
michael@0 | 886 | if (mLayoutStarted || mXSLTProcessor || CanStillPrettyPrint()) { |
michael@0 | 887 | return; |
michael@0 | 888 | } |
michael@0 | 889 | StartLayout(aIgnorePendingSheets); |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | //////////////////////////////////////////////////////////////////////// |
michael@0 | 893 | |
michael@0 | 894 | bool |
michael@0 | 895 | nsXMLContentSink::SetDocElement(int32_t aNameSpaceID, |
michael@0 | 896 | nsIAtom* aTagName, |
michael@0 | 897 | nsIContent *aContent) |
michael@0 | 898 | { |
michael@0 | 899 | if (mDocElement) |
michael@0 | 900 | return false; |
michael@0 | 901 | |
michael@0 | 902 | // check for root elements that needs special handling for |
michael@0 | 903 | // prettyprinting |
michael@0 | 904 | if ((aNameSpaceID == kNameSpaceID_XBL && |
michael@0 | 905 | aTagName == nsGkAtoms::bindings) || |
michael@0 | 906 | (aNameSpaceID == kNameSpaceID_XSLT && |
michael@0 | 907 | (aTagName == nsGkAtoms::stylesheet || |
michael@0 | 908 | aTagName == nsGkAtoms::transform))) { |
michael@0 | 909 | mPrettyPrintHasSpecialRoot = true; |
michael@0 | 910 | if (mPrettyPrintXML) { |
michael@0 | 911 | // In this case, disable script execution, stylesheet |
michael@0 | 912 | // loading, and auto XLinks since we plan to prettyprint. |
michael@0 | 913 | mDocument->ScriptLoader()->SetEnabled(false); |
michael@0 | 914 | if (mCSSLoader) { |
michael@0 | 915 | mCSSLoader->SetEnabled(false); |
michael@0 | 916 | } |
michael@0 | 917 | } |
michael@0 | 918 | } |
michael@0 | 919 | |
michael@0 | 920 | mDocElement = aContent; |
michael@0 | 921 | nsresult rv = mDocument->AppendChildTo(mDocElement, NotifyForDocElement()); |
michael@0 | 922 | if (NS_FAILED(rv)) { |
michael@0 | 923 | // If we return false here, the caller will bail out because it won't |
michael@0 | 924 | // find a parent content node to append to, which is fine. |
michael@0 | 925 | return false; |
michael@0 | 926 | } |
michael@0 | 927 | |
michael@0 | 928 | if (aTagName == nsGkAtoms::html && |
michael@0 | 929 | aNameSpaceID == kNameSpaceID_XHTML) { |
michael@0 | 930 | ProcessOfflineManifest(aContent); |
michael@0 | 931 | } |
michael@0 | 932 | |
michael@0 | 933 | return true; |
michael@0 | 934 | } |
michael@0 | 935 | |
michael@0 | 936 | NS_IMETHODIMP |
michael@0 | 937 | nsXMLContentSink::HandleStartElement(const char16_t *aName, |
michael@0 | 938 | const char16_t **aAtts, |
michael@0 | 939 | uint32_t aAttsCount, |
michael@0 | 940 | int32_t aIndex, |
michael@0 | 941 | uint32_t aLineNumber) |
michael@0 | 942 | { |
michael@0 | 943 | return HandleStartElement(aName, aAtts, aAttsCount, aIndex, aLineNumber, |
michael@0 | 944 | true); |
michael@0 | 945 | } |
michael@0 | 946 | |
michael@0 | 947 | nsresult |
michael@0 | 948 | nsXMLContentSink::HandleStartElement(const char16_t *aName, |
michael@0 | 949 | const char16_t **aAtts, |
michael@0 | 950 | uint32_t aAttsCount, |
michael@0 | 951 | int32_t aIndex, |
michael@0 | 952 | uint32_t aLineNumber, |
michael@0 | 953 | bool aInterruptable) |
michael@0 | 954 | { |
michael@0 | 955 | NS_PRECONDITION(aIndex >= -1, "Bogus aIndex"); |
michael@0 | 956 | NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount"); |
michael@0 | 957 | // Adjust aAttsCount so it's the actual number of attributes |
michael@0 | 958 | aAttsCount /= 2; |
michael@0 | 959 | |
michael@0 | 960 | nsresult result = NS_OK; |
michael@0 | 961 | bool appendContent = true; |
michael@0 | 962 | nsCOMPtr<nsIContent> content; |
michael@0 | 963 | |
michael@0 | 964 | // XXX Hopefully the parser will flag this before we get |
michael@0 | 965 | // here. If we're in the epilog, there should be no |
michael@0 | 966 | // new elements |
michael@0 | 967 | PR_ASSERT(eXMLContentSinkState_InEpilog != mState); |
michael@0 | 968 | |
michael@0 | 969 | FlushText(); |
michael@0 | 970 | DidAddContent(); |
michael@0 | 971 | |
michael@0 | 972 | mState = eXMLContentSinkState_InDocumentElement; |
michael@0 | 973 | |
michael@0 | 974 | int32_t nameSpaceID; |
michael@0 | 975 | nsCOMPtr<nsIAtom> prefix, localName; |
michael@0 | 976 | nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), |
michael@0 | 977 | getter_AddRefs(localName), &nameSpaceID); |
michael@0 | 978 | |
michael@0 | 979 | if (!OnOpenContainer(aAtts, aAttsCount, nameSpaceID, localName, aLineNumber)) { |
michael@0 | 980 | return NS_OK; |
michael@0 | 981 | } |
michael@0 | 982 | |
michael@0 | 983 | nsCOMPtr<nsINodeInfo> nodeInfo; |
michael@0 | 984 | nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, |
michael@0 | 985 | nsIDOMNode::ELEMENT_NODE); |
michael@0 | 986 | |
michael@0 | 987 | result = CreateElement(aAtts, aAttsCount, nodeInfo, aLineNumber, |
michael@0 | 988 | getter_AddRefs(content), &appendContent, |
michael@0 | 989 | FROM_PARSER_NETWORK); |
michael@0 | 990 | NS_ENSURE_SUCCESS(result, result); |
michael@0 | 991 | |
michael@0 | 992 | // Have to do this before we push the new content on the stack... and have to |
michael@0 | 993 | // do that before we set attributes, call BindToTree, etc. Ideally we'd push |
michael@0 | 994 | // on the stack inside CreateElement (which is effectively what the HTML sink |
michael@0 | 995 | // does), but that's hard with all the subclass overrides going on. |
michael@0 | 996 | nsCOMPtr<nsIContent> parent = GetCurrentContent(); |
michael@0 | 997 | |
michael@0 | 998 | result = PushContent(content); |
michael@0 | 999 | NS_ENSURE_SUCCESS(result, result); |
michael@0 | 1000 | |
michael@0 | 1001 | // Set the ID attribute atom on the node info object for this node |
michael@0 | 1002 | // This must occur before the attributes are added so the name |
michael@0 | 1003 | // of the id attribute is known. |
michael@0 | 1004 | if (aIndex != -1 && NS_SUCCEEDED(result)) { |
michael@0 | 1005 | nsCOMPtr<nsIAtom> IDAttr = do_GetAtom(aAtts[aIndex]); |
michael@0 | 1006 | |
michael@0 | 1007 | if (IDAttr) { |
michael@0 | 1008 | nodeInfo->SetIDAttributeAtom(IDAttr); |
michael@0 | 1009 | } |
michael@0 | 1010 | } |
michael@0 | 1011 | |
michael@0 | 1012 | // Set the attributes on the new content element |
michael@0 | 1013 | result = AddAttributes(aAtts, content); |
michael@0 | 1014 | |
michael@0 | 1015 | if (NS_OK == result) { |
michael@0 | 1016 | // Store the element |
michael@0 | 1017 | if (!SetDocElement(nameSpaceID, localName, content) && appendContent) { |
michael@0 | 1018 | NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED); |
michael@0 | 1019 | |
michael@0 | 1020 | parent->AppendChildTo(content, false); |
michael@0 | 1021 | } |
michael@0 | 1022 | } |
michael@0 | 1023 | |
michael@0 | 1024 | // Some HTML nodes need DoneCreatingElement() called to initialize |
michael@0 | 1025 | // properly (eg form state restoration). |
michael@0 | 1026 | if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML) { |
michael@0 | 1027 | if (nodeInfo->NameAtom() == nsGkAtoms::input || |
michael@0 | 1028 | nodeInfo->NameAtom() == nsGkAtoms::button || |
michael@0 | 1029 | nodeInfo->NameAtom() == nsGkAtoms::menuitem || |
michael@0 | 1030 | nodeInfo->NameAtom() == nsGkAtoms::audio || |
michael@0 | 1031 | nodeInfo->NameAtom() == nsGkAtoms::video) { |
michael@0 | 1032 | content->DoneCreatingElement(); |
michael@0 | 1033 | } else if (nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) { |
michael@0 | 1034 | mCurrentHead = content; |
michael@0 | 1035 | } |
michael@0 | 1036 | } |
michael@0 | 1037 | |
michael@0 | 1038 | if (IsMonolithicContainer(nodeInfo)) { |
michael@0 | 1039 | mInMonolithicContainer++; |
michael@0 | 1040 | } |
michael@0 | 1041 | |
michael@0 | 1042 | if (content != mDocElement && !mCurrentHead) { |
michael@0 | 1043 | // This isn't the root and we're not inside an XHTML <head>. |
michael@0 | 1044 | // Might need to start layout |
michael@0 | 1045 | MaybeStartLayout(false); |
michael@0 | 1046 | } |
michael@0 | 1047 | |
michael@0 | 1048 | if (content == mDocElement) { |
michael@0 | 1049 | NotifyDocElementCreated(mDocument); |
michael@0 | 1050 | } |
michael@0 | 1051 | |
michael@0 | 1052 | return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() : |
michael@0 | 1053 | result; |
michael@0 | 1054 | } |
michael@0 | 1055 | |
michael@0 | 1056 | NS_IMETHODIMP |
michael@0 | 1057 | nsXMLContentSink::HandleEndElement(const char16_t *aName) |
michael@0 | 1058 | { |
michael@0 | 1059 | return HandleEndElement(aName, true); |
michael@0 | 1060 | } |
michael@0 | 1061 | |
michael@0 | 1062 | nsresult |
michael@0 | 1063 | nsXMLContentSink::HandleEndElement(const char16_t *aName, |
michael@0 | 1064 | bool aInterruptable) |
michael@0 | 1065 | { |
michael@0 | 1066 | nsresult result = NS_OK; |
michael@0 | 1067 | |
michael@0 | 1068 | // XXX Hopefully the parser will flag this before we get |
michael@0 | 1069 | // here. If we're in the prolog or epilog, there should be |
michael@0 | 1070 | // no close tags for elements. |
michael@0 | 1071 | PR_ASSERT(eXMLContentSinkState_InDocumentElement == mState); |
michael@0 | 1072 | |
michael@0 | 1073 | FlushText(); |
michael@0 | 1074 | |
michael@0 | 1075 | StackNode* sn = GetCurrentStackNode(); |
michael@0 | 1076 | if (!sn) { |
michael@0 | 1077 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1078 | } |
michael@0 | 1079 | |
michael@0 | 1080 | nsCOMPtr<nsIContent> content; |
michael@0 | 1081 | sn->mContent.swap(content); |
michael@0 | 1082 | uint32_t numFlushed = sn->mNumFlushed; |
michael@0 | 1083 | |
michael@0 | 1084 | PopContent(); |
michael@0 | 1085 | NS_ASSERTION(content, "failed to pop content"); |
michael@0 | 1086 | #ifdef DEBUG |
michael@0 | 1087 | // Check that we're closing the right thing |
michael@0 | 1088 | nsCOMPtr<nsIAtom> debugNameSpacePrefix, debugTagAtom; |
michael@0 | 1089 | int32_t debugNameSpaceID; |
michael@0 | 1090 | nsContentUtils::SplitExpatName(aName, getter_AddRefs(debugNameSpacePrefix), |
michael@0 | 1091 | getter_AddRefs(debugTagAtom), |
michael@0 | 1092 | &debugNameSpaceID); |
michael@0 | 1093 | NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID), |
michael@0 | 1094 | "Wrong element being closed"); |
michael@0 | 1095 | #endif |
michael@0 | 1096 | |
michael@0 | 1097 | result = CloseElement(content); |
michael@0 | 1098 | |
michael@0 | 1099 | if (mCurrentHead == content) { |
michael@0 | 1100 | mCurrentHead = nullptr; |
michael@0 | 1101 | } |
michael@0 | 1102 | |
michael@0 | 1103 | if (mDocElement == content) { |
michael@0 | 1104 | // XXXbz for roots that don't want to be appended on open, we |
michael@0 | 1105 | // probably need to deal here.... (and stop appending them on open). |
michael@0 | 1106 | mState = eXMLContentSinkState_InEpilog; |
michael@0 | 1107 | |
michael@0 | 1108 | // We might have had no occasion to start layout yet. Do so now. |
michael@0 | 1109 | MaybeStartLayout(false); |
michael@0 | 1110 | } |
michael@0 | 1111 | |
michael@0 | 1112 | int32_t stackLen = mContentStack.Length(); |
michael@0 | 1113 | if (mNotifyLevel >= stackLen) { |
michael@0 | 1114 | if (numFlushed < content->GetChildCount()) { |
michael@0 | 1115 | NotifyAppend(content, numFlushed); |
michael@0 | 1116 | } |
michael@0 | 1117 | mNotifyLevel = stackLen - 1; |
michael@0 | 1118 | } |
michael@0 | 1119 | DidAddContent(); |
michael@0 | 1120 | |
michael@0 | 1121 | if (content->IsSVG(nsGkAtoms::svg)) { |
michael@0 | 1122 | FlushTags(); |
michael@0 | 1123 | nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(content); |
michael@0 | 1124 | if (NS_FAILED(NS_DispatchToMainThread(event))) { |
michael@0 | 1125 | NS_WARNING("failed to dispatch svg load dispatcher"); |
michael@0 | 1126 | } |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() : |
michael@0 | 1130 | result; |
michael@0 | 1131 | } |
michael@0 | 1132 | |
michael@0 | 1133 | NS_IMETHODIMP |
michael@0 | 1134 | nsXMLContentSink::HandleComment(const char16_t *aName) |
michael@0 | 1135 | { |
michael@0 | 1136 | FlushText(); |
michael@0 | 1137 | |
michael@0 | 1138 | nsRefPtr<Comment> comment = new Comment(mNodeInfoManager); |
michael@0 | 1139 | comment->SetText(nsDependentString(aName), false); |
michael@0 | 1140 | nsresult rv = AddContentAsLeaf(comment); |
michael@0 | 1141 | DidAddContent(); |
michael@0 | 1142 | |
michael@0 | 1143 | return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; |
michael@0 | 1144 | } |
michael@0 | 1145 | |
michael@0 | 1146 | NS_IMETHODIMP |
michael@0 | 1147 | nsXMLContentSink::HandleCDataSection(const char16_t *aData, |
michael@0 | 1148 | uint32_t aLength) |
michael@0 | 1149 | { |
michael@0 | 1150 | // XSLT doesn't differentiate between text and cdata and wants adjacent |
michael@0 | 1151 | // textnodes merged, so add as text. |
michael@0 | 1152 | if (mXSLTProcessor) { |
michael@0 | 1153 | return AddText(aData, aLength); |
michael@0 | 1154 | } |
michael@0 | 1155 | |
michael@0 | 1156 | FlushText(); |
michael@0 | 1157 | |
michael@0 | 1158 | nsRefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager); |
michael@0 | 1159 | cdata->SetText(aData, aLength, false); |
michael@0 | 1160 | nsresult rv = AddContentAsLeaf(cdata); |
michael@0 | 1161 | DidAddContent(); |
michael@0 | 1162 | |
michael@0 | 1163 | return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; |
michael@0 | 1164 | } |
michael@0 | 1165 | |
michael@0 | 1166 | NS_IMETHODIMP |
michael@0 | 1167 | nsXMLContentSink::HandleDoctypeDecl(const nsAString & aSubset, |
michael@0 | 1168 | const nsAString & aName, |
michael@0 | 1169 | const nsAString & aSystemId, |
michael@0 | 1170 | const nsAString & aPublicId, |
michael@0 | 1171 | nsISupports* aCatalogData) |
michael@0 | 1172 | { |
michael@0 | 1173 | FlushText(); |
michael@0 | 1174 | |
michael@0 | 1175 | nsresult rv = NS_OK; |
michael@0 | 1176 | |
michael@0 | 1177 | NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment"); |
michael@0 | 1178 | |
michael@0 | 1179 | nsCOMPtr<nsIAtom> name = do_GetAtom(aName); |
michael@0 | 1180 | NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 1181 | |
michael@0 | 1182 | // Create a new doctype node |
michael@0 | 1183 | nsCOMPtr<nsIDOMDocumentType> docType; |
michael@0 | 1184 | rv = NS_NewDOMDocumentType(getter_AddRefs(docType), mNodeInfoManager, |
michael@0 | 1185 | name, aPublicId, aSystemId, aSubset); |
michael@0 | 1186 | if (NS_FAILED(rv) || !docType) { |
michael@0 | 1187 | return rv; |
michael@0 | 1188 | } |
michael@0 | 1189 | |
michael@0 | 1190 | if (aCatalogData && mCSSLoader && mDocument) { |
michael@0 | 1191 | // bug 124570 - we only expect additional agent sheets for now -- ignore |
michael@0 | 1192 | // exit codes, error are not fatal here, just that the stylesheet won't apply |
michael@0 | 1193 | nsCOMPtr<nsIURI> uri(do_QueryInterface(aCatalogData)); |
michael@0 | 1194 | if (uri) { |
michael@0 | 1195 | nsRefPtr<nsCSSStyleSheet> sheet; |
michael@0 | 1196 | mCSSLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet)); |
michael@0 | 1197 | |
michael@0 | 1198 | #ifdef DEBUG |
michael@0 | 1199 | nsAutoCString uriStr; |
michael@0 | 1200 | uri->GetSpec(uriStr); |
michael@0 | 1201 | printf("Loading catalog stylesheet: %s ... %s\n", uriStr.get(), sheet.get() ? "Done" : "Failed"); |
michael@0 | 1202 | #endif |
michael@0 | 1203 | if (sheet) { |
michael@0 | 1204 | mDocument->BeginUpdate(UPDATE_STYLE); |
michael@0 | 1205 | mDocument->AddCatalogStyleSheet(sheet); |
michael@0 | 1206 | mDocument->EndUpdate(UPDATE_STYLE); |
michael@0 | 1207 | } |
michael@0 | 1208 | } |
michael@0 | 1209 | } |
michael@0 | 1210 | |
michael@0 | 1211 | nsCOMPtr<nsIContent> content = do_QueryInterface(docType); |
michael@0 | 1212 | NS_ASSERTION(content, "doctype isn't content?"); |
michael@0 | 1213 | |
michael@0 | 1214 | rv = mDocument->AppendChildTo(content, false); |
michael@0 | 1215 | DidAddContent(); |
michael@0 | 1216 | return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; |
michael@0 | 1217 | } |
michael@0 | 1218 | |
michael@0 | 1219 | NS_IMETHODIMP |
michael@0 | 1220 | nsXMLContentSink::HandleCharacterData(const char16_t *aData, |
michael@0 | 1221 | uint32_t aLength) |
michael@0 | 1222 | { |
michael@0 | 1223 | return HandleCharacterData(aData, aLength, true); |
michael@0 | 1224 | } |
michael@0 | 1225 | |
michael@0 | 1226 | nsresult |
michael@0 | 1227 | nsXMLContentSink::HandleCharacterData(const char16_t *aData, uint32_t aLength, |
michael@0 | 1228 | bool aInterruptable) |
michael@0 | 1229 | { |
michael@0 | 1230 | nsresult rv = NS_OK; |
michael@0 | 1231 | if (aData && mState != eXMLContentSinkState_InProlog && |
michael@0 | 1232 | mState != eXMLContentSinkState_InEpilog) { |
michael@0 | 1233 | rv = AddText(aData, aLength); |
michael@0 | 1234 | } |
michael@0 | 1235 | return aInterruptable && NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; |
michael@0 | 1236 | } |
michael@0 | 1237 | |
michael@0 | 1238 | NS_IMETHODIMP |
michael@0 | 1239 | nsXMLContentSink::HandleProcessingInstruction(const char16_t *aTarget, |
michael@0 | 1240 | const char16_t *aData) |
michael@0 | 1241 | { |
michael@0 | 1242 | FlushText(); |
michael@0 | 1243 | |
michael@0 | 1244 | const nsDependentString target(aTarget); |
michael@0 | 1245 | const nsDependentString data(aData); |
michael@0 | 1246 | |
michael@0 | 1247 | nsCOMPtr<nsIContent> node = |
michael@0 | 1248 | NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data); |
michael@0 | 1249 | |
michael@0 | 1250 | nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(node)); |
michael@0 | 1251 | if (ssle) { |
michael@0 | 1252 | ssle->InitStyleLinkElement(false); |
michael@0 | 1253 | ssle->SetEnableUpdates(false); |
michael@0 | 1254 | mPrettyPrintXML = false; |
michael@0 | 1255 | } |
michael@0 | 1256 | |
michael@0 | 1257 | nsresult rv = AddContentAsLeaf(node); |
michael@0 | 1258 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1259 | DidAddContent(); |
michael@0 | 1260 | |
michael@0 | 1261 | if (ssle) { |
michael@0 | 1262 | // This is an xml-stylesheet processing instruction... but it might not be |
michael@0 | 1263 | // a CSS one if the type is set to something else. |
michael@0 | 1264 | ssle->SetEnableUpdates(true); |
michael@0 | 1265 | bool willNotify; |
michael@0 | 1266 | bool isAlternate; |
michael@0 | 1267 | rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, |
michael@0 | 1268 | &willNotify, |
michael@0 | 1269 | &isAlternate); |
michael@0 | 1270 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1271 | |
michael@0 | 1272 | if (willNotify) { |
michael@0 | 1273 | // Successfully started a stylesheet load |
michael@0 | 1274 | if (!isAlternate && !mRunsToCompletion) { |
michael@0 | 1275 | ++mPendingSheetCount; |
michael@0 | 1276 | mScriptLoader->AddExecuteBlocker(); |
michael@0 | 1277 | } |
michael@0 | 1278 | |
michael@0 | 1279 | return NS_OK; |
michael@0 | 1280 | } |
michael@0 | 1281 | } |
michael@0 | 1282 | |
michael@0 | 1283 | // If it's not a CSS stylesheet PI... |
michael@0 | 1284 | nsAutoString type; |
michael@0 | 1285 | nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type); |
michael@0 | 1286 | |
michael@0 | 1287 | if (mState != eXMLContentSinkState_InProlog || |
michael@0 | 1288 | !target.EqualsLiteral("xml-stylesheet") || |
michael@0 | 1289 | type.IsEmpty() || |
michael@0 | 1290 | type.LowerCaseEqualsLiteral("text/css")) { |
michael@0 | 1291 | return DidProcessATokenImpl(); |
michael@0 | 1292 | } |
michael@0 | 1293 | |
michael@0 | 1294 | nsAutoString href, title, media; |
michael@0 | 1295 | bool isAlternate = false; |
michael@0 | 1296 | |
michael@0 | 1297 | // If there was no href, we can't do anything with this PI |
michael@0 | 1298 | if (!ParsePIData(data, href, title, media, isAlternate)) { |
michael@0 | 1299 | return DidProcessATokenImpl(); |
michael@0 | 1300 | } |
michael@0 | 1301 | |
michael@0 | 1302 | rv = ProcessStyleLink(node, href, isAlternate, title, type, media); |
michael@0 | 1303 | return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; |
michael@0 | 1304 | } |
michael@0 | 1305 | |
michael@0 | 1306 | /* static */ |
michael@0 | 1307 | bool |
michael@0 | 1308 | nsXMLContentSink::ParsePIData(const nsString &aData, nsString &aHref, |
michael@0 | 1309 | nsString &aTitle, nsString &aMedia, |
michael@0 | 1310 | bool &aIsAlternate) |
michael@0 | 1311 | { |
michael@0 | 1312 | // If there was no href, we can't do anything with this PI |
michael@0 | 1313 | if (!nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::href, aHref)) { |
michael@0 | 1314 | return false; |
michael@0 | 1315 | } |
michael@0 | 1316 | |
michael@0 | 1317 | nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::title, aTitle); |
michael@0 | 1318 | |
michael@0 | 1319 | nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::media, aMedia); |
michael@0 | 1320 | |
michael@0 | 1321 | nsAutoString alternate; |
michael@0 | 1322 | nsContentUtils::GetPseudoAttributeValue(aData, |
michael@0 | 1323 | nsGkAtoms::alternate, |
michael@0 | 1324 | alternate); |
michael@0 | 1325 | |
michael@0 | 1326 | aIsAlternate = alternate.EqualsLiteral("yes"); |
michael@0 | 1327 | |
michael@0 | 1328 | return true; |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | NS_IMETHODIMP |
michael@0 | 1332 | nsXMLContentSink::HandleXMLDeclaration(const char16_t *aVersion, |
michael@0 | 1333 | const char16_t *aEncoding, |
michael@0 | 1334 | int32_t aStandalone) |
michael@0 | 1335 | { |
michael@0 | 1336 | mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone); |
michael@0 | 1337 | |
michael@0 | 1338 | return DidProcessATokenImpl(); |
michael@0 | 1339 | } |
michael@0 | 1340 | |
michael@0 | 1341 | NS_IMETHODIMP |
michael@0 | 1342 | nsXMLContentSink::ReportError(const char16_t* aErrorText, |
michael@0 | 1343 | const char16_t* aSourceText, |
michael@0 | 1344 | nsIScriptError *aError, |
michael@0 | 1345 | bool *_retval) |
michael@0 | 1346 | { |
michael@0 | 1347 | NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); |
michael@0 | 1348 | nsresult rv = NS_OK; |
michael@0 | 1349 | |
michael@0 | 1350 | // The expat driver should report the error. We're just cleaning up the mess. |
michael@0 | 1351 | *_retval = true; |
michael@0 | 1352 | |
michael@0 | 1353 | mPrettyPrintXML = false; |
michael@0 | 1354 | |
michael@0 | 1355 | mState = eXMLContentSinkState_InProlog; |
michael@0 | 1356 | |
michael@0 | 1357 | // XXX need to stop scripts here -- hsivonen |
michael@0 | 1358 | |
michael@0 | 1359 | // stop observing in order to avoid crashing when removing content |
michael@0 | 1360 | mDocument->RemoveObserver(this); |
michael@0 | 1361 | mIsDocumentObserver = false; |
michael@0 | 1362 | |
michael@0 | 1363 | // Clear the current content and |
michael@0 | 1364 | // prepare to set <parsererror> as the document root |
michael@0 | 1365 | nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mDocument)); |
michael@0 | 1366 | if (node) { |
michael@0 | 1367 | for (;;) { |
michael@0 | 1368 | nsCOMPtr<nsIDOMNode> child, dummy; |
michael@0 | 1369 | node->GetLastChild(getter_AddRefs(child)); |
michael@0 | 1370 | if (!child) |
michael@0 | 1371 | break; |
michael@0 | 1372 | node->RemoveChild(child, getter_AddRefs(dummy)); |
michael@0 | 1373 | } |
michael@0 | 1374 | } |
michael@0 | 1375 | mDocElement = nullptr; |
michael@0 | 1376 | |
michael@0 | 1377 | // Clear any buffered-up text we have. It's enough to set the length to 0. |
michael@0 | 1378 | // The buffer itself is allocated when we're created and deleted in our |
michael@0 | 1379 | // destructor, so don't mess with it. |
michael@0 | 1380 | mTextLength = 0; |
michael@0 | 1381 | |
michael@0 | 1382 | if (mXSLTProcessor) { |
michael@0 | 1383 | // Get rid of the XSLT processor. |
michael@0 | 1384 | mXSLTProcessor->CancelLoads(); |
michael@0 | 1385 | mXSLTProcessor = nullptr; |
michael@0 | 1386 | } |
michael@0 | 1387 | |
michael@0 | 1388 | // release the nodes on stack |
michael@0 | 1389 | mContentStack.Clear(); |
michael@0 | 1390 | mNotifyLevel = 0; |
michael@0 | 1391 | |
michael@0 | 1392 | rv = HandleProcessingInstruction(MOZ_UTF16("xml-stylesheet"), |
michael@0 | 1393 | MOZ_UTF16("href=\"chrome://global/locale/intl.css\" type=\"text/css\"")); |
michael@0 | 1394 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1395 | |
michael@0 | 1396 | const char16_t* noAtts[] = { 0, 0 }; |
michael@0 | 1397 | |
michael@0 | 1398 | NS_NAMED_LITERAL_STRING(errorNs, |
michael@0 | 1399 | "http://www.mozilla.org/newlayout/xml/parsererror.xml"); |
michael@0 | 1400 | |
michael@0 | 1401 | nsAutoString parsererror(errorNs); |
michael@0 | 1402 | parsererror.Append((char16_t)0xFFFF); |
michael@0 | 1403 | parsererror.AppendLiteral("parsererror"); |
michael@0 | 1404 | |
michael@0 | 1405 | rv = HandleStartElement(parsererror.get(), noAtts, 0, -1, (uint32_t)-1, |
michael@0 | 1406 | false); |
michael@0 | 1407 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1408 | |
michael@0 | 1409 | rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText), false); |
michael@0 | 1410 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1411 | |
michael@0 | 1412 | nsAutoString sourcetext(errorNs); |
michael@0 | 1413 | sourcetext.Append((char16_t)0xFFFF); |
michael@0 | 1414 | sourcetext.AppendLiteral("sourcetext"); |
michael@0 | 1415 | |
michael@0 | 1416 | rv = HandleStartElement(sourcetext.get(), noAtts, 0, -1, (uint32_t)-1, |
michael@0 | 1417 | false); |
michael@0 | 1418 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1419 | |
michael@0 | 1420 | rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText), false); |
michael@0 | 1421 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1422 | |
michael@0 | 1423 | rv = HandleEndElement(sourcetext.get(), false); |
michael@0 | 1424 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1425 | |
michael@0 | 1426 | rv = HandleEndElement(parsererror.get(), false); |
michael@0 | 1427 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1428 | |
michael@0 | 1429 | FlushTags(); |
michael@0 | 1430 | |
michael@0 | 1431 | return NS_OK; |
michael@0 | 1432 | } |
michael@0 | 1433 | |
michael@0 | 1434 | nsresult |
michael@0 | 1435 | nsXMLContentSink::AddAttributes(const char16_t** aAtts, |
michael@0 | 1436 | nsIContent* aContent) |
michael@0 | 1437 | { |
michael@0 | 1438 | // Add tag attributes to the content attributes |
michael@0 | 1439 | nsCOMPtr<nsIAtom> prefix, localName; |
michael@0 | 1440 | while (*aAtts) { |
michael@0 | 1441 | int32_t nameSpaceID; |
michael@0 | 1442 | nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), |
michael@0 | 1443 | getter_AddRefs(localName), &nameSpaceID); |
michael@0 | 1444 | |
michael@0 | 1445 | // Add attribute to content |
michael@0 | 1446 | aContent->SetAttr(nameSpaceID, localName, prefix, |
michael@0 | 1447 | nsDependentString(aAtts[1]), false); |
michael@0 | 1448 | aAtts += 2; |
michael@0 | 1449 | } |
michael@0 | 1450 | |
michael@0 | 1451 | return NS_OK; |
michael@0 | 1452 | } |
michael@0 | 1453 | |
michael@0 | 1454 | #define NS_ACCUMULATION_BUFFER_SIZE 4096 |
michael@0 | 1455 | |
michael@0 | 1456 | nsresult |
michael@0 | 1457 | nsXMLContentSink::AddText(const char16_t* aText, |
michael@0 | 1458 | int32_t aLength) |
michael@0 | 1459 | { |
michael@0 | 1460 | // Create buffer when we first need it |
michael@0 | 1461 | if (0 == mTextSize) { |
michael@0 | 1462 | mText = (char16_t *) PR_MALLOC(sizeof(char16_t) * NS_ACCUMULATION_BUFFER_SIZE); |
michael@0 | 1463 | if (nullptr == mText) { |
michael@0 | 1464 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1465 | } |
michael@0 | 1466 | mTextSize = NS_ACCUMULATION_BUFFER_SIZE; |
michael@0 | 1467 | } |
michael@0 | 1468 | |
michael@0 | 1469 | // Copy data from string into our buffer; flush buffer when it fills up |
michael@0 | 1470 | int32_t offset = 0; |
michael@0 | 1471 | while (0 != aLength) { |
michael@0 | 1472 | int32_t amount = mTextSize - mTextLength; |
michael@0 | 1473 | if (0 == amount) { |
michael@0 | 1474 | // XSLT wants adjacent textnodes merged. |
michael@0 | 1475 | if (mConstrainSize && !mXSLTProcessor) { |
michael@0 | 1476 | nsresult rv = FlushText(); |
michael@0 | 1477 | if (NS_OK != rv) { |
michael@0 | 1478 | return rv; |
michael@0 | 1479 | } |
michael@0 | 1480 | |
michael@0 | 1481 | amount = mTextSize - mTextLength; |
michael@0 | 1482 | } |
michael@0 | 1483 | else { |
michael@0 | 1484 | mTextSize += aLength; |
michael@0 | 1485 | mText = (char16_t *) PR_REALLOC(mText, sizeof(char16_t) * mTextSize); |
michael@0 | 1486 | if (nullptr == mText) { |
michael@0 | 1487 | mTextSize = 0; |
michael@0 | 1488 | |
michael@0 | 1489 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1490 | } |
michael@0 | 1491 | |
michael@0 | 1492 | amount = aLength; |
michael@0 | 1493 | } |
michael@0 | 1494 | } |
michael@0 | 1495 | if (amount > aLength) { |
michael@0 | 1496 | amount = aLength; |
michael@0 | 1497 | } |
michael@0 | 1498 | memcpy(&mText[mTextLength], &aText[offset], sizeof(char16_t) * amount); |
michael@0 | 1499 | mTextLength += amount; |
michael@0 | 1500 | offset += amount; |
michael@0 | 1501 | aLength -= amount; |
michael@0 | 1502 | } |
michael@0 | 1503 | |
michael@0 | 1504 | return NS_OK; |
michael@0 | 1505 | } |
michael@0 | 1506 | |
michael@0 | 1507 | void |
michael@0 | 1508 | nsXMLContentSink::FlushPendingNotifications(mozFlushType aType) |
michael@0 | 1509 | { |
michael@0 | 1510 | // Only flush tags if we're not doing the notification ourselves |
michael@0 | 1511 | // (since we aren't reentrant) |
michael@0 | 1512 | if (!mInNotification) { |
michael@0 | 1513 | if (mIsDocumentObserver) { |
michael@0 | 1514 | // Only flush if we're still a document observer (so that our child |
michael@0 | 1515 | // counts should be correct). |
michael@0 | 1516 | if (aType >= Flush_ContentAndNotify) { |
michael@0 | 1517 | FlushTags(); |
michael@0 | 1518 | } |
michael@0 | 1519 | else { |
michael@0 | 1520 | FlushText(false); |
michael@0 | 1521 | } |
michael@0 | 1522 | } |
michael@0 | 1523 | if (aType >= Flush_InterruptibleLayout) { |
michael@0 | 1524 | // Make sure that layout has started so that the reflow flush |
michael@0 | 1525 | // will actually happen. |
michael@0 | 1526 | MaybeStartLayout(true); |
michael@0 | 1527 | } |
michael@0 | 1528 | } |
michael@0 | 1529 | } |
michael@0 | 1530 | |
michael@0 | 1531 | /** |
michael@0 | 1532 | * NOTE!! Forked from SinkContext. Please keep in sync. |
michael@0 | 1533 | * |
michael@0 | 1534 | * Flush all elements that have been seen so far such that |
michael@0 | 1535 | * they are visible in the tree. Specifically, make sure |
michael@0 | 1536 | * that they are all added to their respective parents. |
michael@0 | 1537 | * Also, do notification at the top for all content that |
michael@0 | 1538 | * has been newly added so that the frame tree is complete. |
michael@0 | 1539 | */ |
michael@0 | 1540 | nsresult |
michael@0 | 1541 | nsXMLContentSink::FlushTags() |
michael@0 | 1542 | { |
michael@0 | 1543 | mDeferredFlushTags = false; |
michael@0 | 1544 | bool oldBeganUpdate = mBeganUpdate; |
michael@0 | 1545 | uint32_t oldUpdates = mUpdatesInNotification; |
michael@0 | 1546 | |
michael@0 | 1547 | mUpdatesInNotification = 0; |
michael@0 | 1548 | ++mInNotification; |
michael@0 | 1549 | { |
michael@0 | 1550 | // Scope so we call EndUpdate before we decrease mInNotification |
michael@0 | 1551 | mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, true); |
michael@0 | 1552 | mBeganUpdate = true; |
michael@0 | 1553 | |
michael@0 | 1554 | // Don't release last text node in case we need to add to it again |
michael@0 | 1555 | FlushText(false); |
michael@0 | 1556 | |
michael@0 | 1557 | // Start from the base of the stack (growing downward) and do |
michael@0 | 1558 | // a notification from the node that is closest to the root of |
michael@0 | 1559 | // tree for any content that has been added. |
michael@0 | 1560 | |
michael@0 | 1561 | int32_t stackPos; |
michael@0 | 1562 | int32_t stackLen = mContentStack.Length(); |
michael@0 | 1563 | bool flushed = false; |
michael@0 | 1564 | uint32_t childCount; |
michael@0 | 1565 | nsIContent* content; |
michael@0 | 1566 | |
michael@0 | 1567 | for (stackPos = 0; stackPos < stackLen; ++stackPos) { |
michael@0 | 1568 | content = mContentStack[stackPos].mContent; |
michael@0 | 1569 | childCount = content->GetChildCount(); |
michael@0 | 1570 | |
michael@0 | 1571 | if (!flushed && (mContentStack[stackPos].mNumFlushed < childCount)) { |
michael@0 | 1572 | NotifyAppend(content, mContentStack[stackPos].mNumFlushed); |
michael@0 | 1573 | flushed = true; |
michael@0 | 1574 | } |
michael@0 | 1575 | |
michael@0 | 1576 | mContentStack[stackPos].mNumFlushed = childCount; |
michael@0 | 1577 | } |
michael@0 | 1578 | mNotifyLevel = stackLen - 1; |
michael@0 | 1579 | } |
michael@0 | 1580 | --mInNotification; |
michael@0 | 1581 | |
michael@0 | 1582 | if (mUpdatesInNotification > 1) { |
michael@0 | 1583 | UpdateChildCounts(); |
michael@0 | 1584 | } |
michael@0 | 1585 | |
michael@0 | 1586 | mUpdatesInNotification = oldUpdates; |
michael@0 | 1587 | mBeganUpdate = oldBeganUpdate; |
michael@0 | 1588 | |
michael@0 | 1589 | return NS_OK; |
michael@0 | 1590 | } |
michael@0 | 1591 | |
michael@0 | 1592 | /** |
michael@0 | 1593 | * NOTE!! Forked from SinkContext. Please keep in sync. |
michael@0 | 1594 | */ |
michael@0 | 1595 | void |
michael@0 | 1596 | nsXMLContentSink::UpdateChildCounts() |
michael@0 | 1597 | { |
michael@0 | 1598 | // Start from the top of the stack (growing upwards) and see if any |
michael@0 | 1599 | // new content has been appended. If so, we recognize that reflows |
michael@0 | 1600 | // have been generated for it and we should make sure that no |
michael@0 | 1601 | // further reflows occur. Note that we have to include stackPos == 0 |
michael@0 | 1602 | // to properly notify on kids of <html>. |
michael@0 | 1603 | int32_t stackLen = mContentStack.Length(); |
michael@0 | 1604 | int32_t stackPos = stackLen - 1; |
michael@0 | 1605 | while (stackPos >= 0) { |
michael@0 | 1606 | StackNode & node = mContentStack[stackPos]; |
michael@0 | 1607 | node.mNumFlushed = node.mContent->GetChildCount(); |
michael@0 | 1608 | |
michael@0 | 1609 | stackPos--; |
michael@0 | 1610 | } |
michael@0 | 1611 | mNotifyLevel = stackLen - 1; |
michael@0 | 1612 | } |
michael@0 | 1613 | |
michael@0 | 1614 | bool |
michael@0 | 1615 | nsXMLContentSink::IsMonolithicContainer(nsINodeInfo* aNodeInfo) |
michael@0 | 1616 | { |
michael@0 | 1617 | return ((aNodeInfo->NamespaceID() == kNameSpaceID_XHTML && |
michael@0 | 1618 | (aNodeInfo->NameAtom() == nsGkAtoms::tr || |
michael@0 | 1619 | aNodeInfo->NameAtom() == nsGkAtoms::select || |
michael@0 | 1620 | aNodeInfo->NameAtom() == nsGkAtoms::object || |
michael@0 | 1621 | aNodeInfo->NameAtom() == nsGkAtoms::applet)) || |
michael@0 | 1622 | (aNodeInfo->NamespaceID() == kNameSpaceID_MathML && |
michael@0 | 1623 | (aNodeInfo->NameAtom() == nsGkAtoms::math)) |
michael@0 | 1624 | ); |
michael@0 | 1625 | } |
michael@0 | 1626 | |
michael@0 | 1627 | void |
michael@0 | 1628 | nsXMLContentSink::ContinueInterruptedParsingIfEnabled() |
michael@0 | 1629 | { |
michael@0 | 1630 | if (mParser && mParser->IsParserEnabled()) { |
michael@0 | 1631 | GetParser()->ContinueInterruptedParsing(); |
michael@0 | 1632 | } |
michael@0 | 1633 | } |
michael@0 | 1634 | |
michael@0 | 1635 | void |
michael@0 | 1636 | nsXMLContentSink::ContinueInterruptedParsingAsync() |
michael@0 | 1637 | { |
michael@0 | 1638 | nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, |
michael@0 | 1639 | &nsXMLContentSink::ContinueInterruptedParsingIfEnabled); |
michael@0 | 1640 | |
michael@0 | 1641 | NS_DispatchToCurrentThread(ev); |
michael@0 | 1642 | } |
michael@0 | 1643 | |
michael@0 | 1644 | nsIParser* |
michael@0 | 1645 | nsXMLContentSink::GetParser() |
michael@0 | 1646 | { |
michael@0 | 1647 | return static_cast<nsIParser*>(mParser.get()); |
michael@0 | 1648 | } |