WebBrowserPersistLocalDocument.cpp (42102B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "WebBrowserPersistLocalDocument.h" 7 8 #include "WebBrowserPersistDocumentParent.h" 9 #include "mozilla/Encoding.h" 10 #include "mozilla/Try.h" 11 #include "mozilla/dom/Attr.h" 12 #include "mozilla/dom/BrowserParent.h" 13 #include "mozilla/dom/BrowsingContext.h" 14 #include "mozilla/dom/Comment.h" 15 #include "mozilla/dom/Document.h" 16 #include "mozilla/dom/Element.h" 17 #include "mozilla/dom/HTMLAnchorElement.h" 18 #include "mozilla/dom/HTMLAreaElement.h" 19 #include "mozilla/dom/HTMLImageElement.h" 20 #include "mozilla/dom/HTMLInputElement.h" 21 #include "mozilla/dom/HTMLLinkElement.h" 22 #include "mozilla/dom/HTMLObjectElement.h" 23 #include "mozilla/dom/HTMLOptionElement.h" 24 #include "mozilla/dom/HTMLSharedElement.h" 25 #include "mozilla/dom/HTMLTextAreaElement.h" 26 #include "mozilla/dom/NodeFilterBinding.h" 27 #include "mozilla/dom/ProcessingInstruction.h" 28 #include "mozilla/dom/ResponsiveImageSelector.h" 29 #include "mozilla/dom/TreeWalker.h" 30 #include "nsComponentManagerUtils.h" 31 #include "nsContentUtils.h" 32 #include "nsCycleCollectionParticipant.h" 33 #include "nsDOMAttributeMap.h" 34 #include "nsFrameLoader.h" 35 #include "nsGlobalWindowOuter.h" 36 #include "nsIContent.h" 37 #include "nsICookieJarSettings.h" 38 #include "nsIDOMWindowUtils.h" 39 #include "nsIDocumentEncoder.h" 40 #include "nsILoadContext.h" 41 #include "nsIProtocolHandler.h" 42 #include "nsISHEntry.h" 43 #include "nsIURIMutator.h" 44 #include "nsIWebBrowserPersist.h" 45 #include "nsIWebNavigation.h" 46 #include "nsIWebPageDescriptor.h" 47 #include "nsNetUtil.h" 48 #include "nsQueryObject.h" 49 50 namespace mozilla { 51 52 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebBrowserPersistLocalDocument) 53 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebBrowserPersistLocalDocument) 54 55 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebBrowserPersistLocalDocument) 56 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistDocument) 57 NS_INTERFACE_MAP_ENTRY(nsISupports) 58 NS_INTERFACE_MAP_END 59 60 NS_IMPL_CYCLE_COLLECTION(WebBrowserPersistLocalDocument, mDocument) 61 62 WebBrowserPersistLocalDocument::WebBrowserPersistLocalDocument( 63 dom::Document* aDocument) 64 : mDocument(aDocument), mPersistFlags(0) { 65 MOZ_ASSERT(mDocument); 66 } 67 68 WebBrowserPersistLocalDocument::~WebBrowserPersistLocalDocument() = default; 69 70 NS_IMETHODIMP 71 WebBrowserPersistLocalDocument::SetPersistFlags(uint32_t aFlags) { 72 mPersistFlags = aFlags; 73 return NS_OK; 74 } 75 76 NS_IMETHODIMP 77 WebBrowserPersistLocalDocument::GetPersistFlags(uint32_t* aFlags) { 78 *aFlags = mPersistFlags; 79 return NS_OK; 80 } 81 82 NS_IMETHODIMP 83 WebBrowserPersistLocalDocument::GetIsClosed(bool* aIsClosed) { 84 *aIsClosed = false; 85 return NS_OK; 86 } 87 88 NS_IMETHODIMP 89 WebBrowserPersistLocalDocument::GetIsPrivate(bool* aIsPrivate) { 90 nsCOMPtr<nsILoadContext> privacyContext = mDocument->GetLoadContext(); 91 *aIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing(); 92 return NS_OK; 93 } 94 95 NS_IMETHODIMP 96 WebBrowserPersistLocalDocument::GetDocumentURI(nsACString& aURISpec) { 97 nsCOMPtr<nsIURI> uri = mDocument->GetDocumentURI(); 98 if (!uri) { 99 return NS_ERROR_UNEXPECTED; 100 } 101 return uri->GetSpec(aURISpec); 102 } 103 104 NS_IMETHODIMP 105 WebBrowserPersistLocalDocument::GetBaseURI(nsACString& aURISpec) { 106 nsCOMPtr<nsIURI> uri = GetBaseURI(); 107 if (!uri) { 108 return NS_ERROR_UNEXPECTED; 109 } 110 return uri->GetSpec(aURISpec); 111 } 112 113 NS_IMETHODIMP 114 WebBrowserPersistLocalDocument::GetContentType(nsACString& aContentType) { 115 nsAutoString utf16Type; 116 mDocument->GetContentType(utf16Type); 117 CopyUTF16toUTF8(utf16Type, aContentType); 118 return NS_OK; 119 } 120 121 NS_IMETHODIMP 122 WebBrowserPersistLocalDocument::GetCharacterSet(nsACString& aCharSet) { 123 GetCharacterSet()->Name(aCharSet); 124 return NS_OK; 125 } 126 127 NS_IMETHODIMP 128 WebBrowserPersistLocalDocument::GetTitle(nsAString& aTitle) { 129 nsAutoString titleBuffer; 130 mDocument->GetTitle(titleBuffer); 131 aTitle = titleBuffer; 132 return NS_OK; 133 } 134 135 NS_IMETHODIMP 136 WebBrowserPersistLocalDocument::GetReferrerInfo( 137 nsIReferrerInfo** aReferrerInfo) { 138 *aReferrerInfo = mDocument->GetReferrerInfo(); 139 NS_IF_ADDREF(*aReferrerInfo); 140 return NS_OK; 141 } 142 143 NS_IMETHODIMP 144 WebBrowserPersistLocalDocument::GetCookieJarSettings( 145 nsICookieJarSettings** aCookieJarSettings) { 146 *aCookieJarSettings = mDocument->CookieJarSettings(); 147 NS_ADDREF(*aCookieJarSettings); 148 return NS_OK; 149 } 150 151 NS_IMETHODIMP 152 WebBrowserPersistLocalDocument::GetContentDisposition(nsAString& aCD) { 153 nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow(); 154 if (NS_WARN_IF(!window)) { 155 aCD.SetIsVoid(true); 156 return NS_OK; 157 } 158 nsCOMPtr<nsIDOMWindowUtils> utils = 159 nsGlobalWindowOuter::Cast(window)->WindowUtils(); 160 nsresult rv = utils->GetDocumentMetadata(u"content-disposition"_ns, aCD); 161 if (NS_WARN_IF(NS_FAILED(rv))) { 162 aCD.SetIsVoid(true); 163 } 164 return NS_OK; 165 } 166 167 NS_IMETHODIMP 168 WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey) { 169 Maybe<uint32_t> cacheKey; 170 171 if (nsDocShell* docShell = nsDocShell::Cast(mDocument->GetDocShell())) { 172 cacheKey = docShell->GetCacheKeyFromCurrentEntry(); 173 } 174 *aKey = cacheKey.valueOr(0); 175 176 return NS_OK; 177 } 178 179 NS_IMETHODIMP 180 WebBrowserPersistLocalDocument::GetPostData(nsIInputStream** aStream) { 181 nsCOMPtr<nsIInputStream> postData; 182 if (nsDocShell* docShell = nsDocShell::Cast(mDocument->GetDocShell())) { 183 postData = docShell->GetPostDataFromCurrentEntry(); 184 } 185 186 postData.forget(aStream); 187 return NS_OK; 188 } 189 190 NS_IMETHODIMP 191 WebBrowserPersistLocalDocument::GetPrincipal(nsIPrincipal** aPrincipal) { 192 nsCOMPtr<nsIPrincipal> nodePrincipal = mDocument->NodePrincipal(); 193 nodePrincipal.forget(aPrincipal); 194 return NS_OK; 195 } 196 197 already_AddRefed<nsISHEntry> WebBrowserPersistLocalDocument::GetHistory() { 198 nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow(); 199 if (NS_WARN_IF(!window)) { 200 return nullptr; 201 } 202 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window); 203 if (NS_WARN_IF(!webNav)) { 204 return nullptr; 205 } 206 nsCOMPtr<nsIWebPageDescriptor> desc = do_QueryInterface(webNav); 207 if (NS_WARN_IF(!desc)) { 208 return nullptr; 209 } 210 nsCOMPtr<nsISupports> curDesc; 211 nsresult rv = desc->GetCurrentDescriptor(getter_AddRefs(curDesc)); 212 // This can fail if, e.g., the document is a Print Preview. 213 if (NS_FAILED(rv) || NS_WARN_IF(!curDesc)) { 214 return nullptr; 215 } 216 nsCOMPtr<nsISHEntry> history = do_QueryInterface(curDesc); 217 return history.forget(); 218 } 219 220 NotNull<const Encoding*> WebBrowserPersistLocalDocument::GetCharacterSet() 221 const { 222 return mDocument->GetDocumentCharacterSet(); 223 } 224 225 uint32_t WebBrowserPersistLocalDocument::GetPersistFlags() const { 226 return mPersistFlags; 227 } 228 229 nsIURI* WebBrowserPersistLocalDocument::GetBaseURI() const { 230 return mDocument->GetBaseURI(); 231 } 232 233 namespace { 234 235 // Helper class for ReadResources(). 236 class ResourceReader final : public nsIWebBrowserPersistDocumentReceiver { 237 public: 238 ResourceReader(WebBrowserPersistLocalDocument* aParent, 239 nsIWebBrowserPersistResourceVisitor* aVisitor); 240 nsresult OnWalkDOMNode(nsINode* aNode); 241 242 // This is called both to indicate the end of the document walk 243 // and when a subdocument is (maybe asynchronously) sent to the 244 // visitor. The call to EndVisit needs to happen after both of 245 // those have finished. 246 void DocumentDone(nsresult aStatus); 247 248 NS_DECL_NSIWEBBROWSERPERSISTDOCUMENTRECEIVER 249 NS_DECL_ISUPPORTS 250 251 private: 252 RefPtr<WebBrowserPersistLocalDocument> mParent; 253 nsCOMPtr<nsIWebBrowserPersistResourceVisitor> mVisitor; 254 nsCOMPtr<nsIURI> mCurrentBaseURI; 255 uint32_t mPersistFlags; 256 257 // The number of DocumentDone calls after which EndVisit will be 258 // called on the visitor. Counts the main document if it's still 259 // being walked and any outstanding asynchronous subdocument 260 // StartPersistence calls. 261 size_t mOutstandingDocuments; 262 // Collects the status parameters to DocumentDone calls. 263 nsresult mEndStatus; 264 265 nsresult OnWalkURI(const nsACString& aURISpec, 266 nsContentPolicyType aContentPolicyType); 267 nsresult OnWalkURI(nsIURI* aURI, nsContentPolicyType aContentPolicyType); 268 nsresult OnWalkAttribute(dom::Element* aElement, 269 nsContentPolicyType aContentPolicyType, 270 const char* aAttribute, 271 const char* aNamespaceURI = ""); 272 nsresult OnWalkSubframe(nsINode* aNode); 273 nsresult OnWalkSrcSet(dom::Element* aElement); 274 275 ~ResourceReader(); 276 277 using IWBP = nsIWebBrowserPersist; 278 }; 279 280 NS_IMPL_ISUPPORTS(ResourceReader, nsIWebBrowserPersistDocumentReceiver) 281 282 ResourceReader::ResourceReader(WebBrowserPersistLocalDocument* aParent, 283 nsIWebBrowserPersistResourceVisitor* aVisitor) 284 : mParent(aParent), 285 mVisitor(aVisitor), 286 mCurrentBaseURI(aParent->GetBaseURI()), 287 mPersistFlags(aParent->GetPersistFlags()), 288 mOutstandingDocuments(1), 289 mEndStatus(NS_OK) { 290 MOZ_ASSERT(mCurrentBaseURI); 291 } 292 293 ResourceReader::~ResourceReader() { MOZ_ASSERT(mOutstandingDocuments == 0); } 294 295 void ResourceReader::DocumentDone(nsresult aStatus) { 296 MOZ_ASSERT(mOutstandingDocuments > 0); 297 if (NS_SUCCEEDED(mEndStatus)) { 298 mEndStatus = aStatus; 299 } 300 if (--mOutstandingDocuments == 0) { 301 mVisitor->EndVisit(mParent, mEndStatus); 302 } 303 } 304 305 nsresult ResourceReader::OnWalkSubframe(nsINode* aNode) { 306 RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(aNode); 307 NS_ENSURE_STATE(loaderOwner); 308 RefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader(); 309 NS_ENSURE_STATE(loader); 310 311 RefPtr<dom::BrowsingContext> context = loader->GetBrowsingContext(); 312 NS_ENSURE_STATE(context); 313 314 if (loader->IsRemoteFrame()) { 315 mVisitor->VisitBrowsingContext(mParent, context); 316 return NS_OK; 317 } 318 319 ++mOutstandingDocuments; 320 ErrorResult err; 321 loader->StartPersistence(context, this, err); 322 nsresult rv = err.StealNSResult(); 323 if (NS_FAILED(rv)) { 324 if (rv == NS_ERROR_NO_CONTENT) { 325 // Just ignore frames with no content document. 326 rv = NS_OK; 327 } 328 // StartPersistence won't eventually call this if it failed, 329 // so this does so (to keep mOutstandingDocuments correct). 330 DocumentDone(rv); 331 } 332 return rv; 333 } 334 335 NS_IMETHODIMP 336 ResourceReader::OnDocumentReady(nsIWebBrowserPersistDocument* aDocument) { 337 mVisitor->VisitDocument(mParent, aDocument); 338 DocumentDone(NS_OK); 339 return NS_OK; 340 } 341 342 NS_IMETHODIMP 343 ResourceReader::OnError(nsresult aFailure) { 344 DocumentDone(aFailure); 345 return NS_OK; 346 } 347 348 nsresult ResourceReader::OnWalkURI(nsIURI* aURI, 349 nsContentPolicyType aContentPolicyType) { 350 // Test if this URI should be persisted. By default 351 // we should assume the URI is persistable. 352 bool doNotPersistURI; 353 nsresult rv = NS_URIChainHasFlags( 354 aURI, nsIProtocolHandler::URI_NON_PERSISTABLE, &doNotPersistURI); 355 if (NS_SUCCEEDED(rv) && doNotPersistURI) { 356 return NS_OK; 357 } 358 359 nsAutoCString stringURI; 360 rv = aURI->GetSpec(stringURI); 361 NS_ENSURE_SUCCESS(rv, rv); 362 return mVisitor->VisitResource(mParent, stringURI, aContentPolicyType); 363 } 364 365 nsresult ResourceReader::OnWalkURI(const nsACString& aURISpec, 366 nsContentPolicyType aContentPolicyType) { 367 nsresult rv; 368 nsCOMPtr<nsIURI> uri; 369 370 rv = NS_NewURI(getter_AddRefs(uri), aURISpec, mParent->GetCharacterSet(), 371 mCurrentBaseURI); 372 if (NS_FAILED(rv)) { 373 // We don't want to break saving a page in case of a malformed URI. 374 return NS_OK; 375 } 376 return OnWalkURI(uri, aContentPolicyType); 377 } 378 379 static void ExtractAttribute(dom::Element* aElement, const char* aAttribute, 380 const char* aNamespaceURI, nsCString& aValue) { 381 // Find the named URI attribute on the (element) node and store 382 // a reference to the URI that maps onto a local file name 383 384 RefPtr<nsDOMAttributeMap> attrMap = aElement->Attributes(); 385 386 NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI); 387 NS_ConvertASCIItoUTF16 attribute(aAttribute); 388 RefPtr<dom::Attr> attr = attrMap->GetNamedItemNS(namespaceURI, attribute); 389 if (attr) { 390 nsAutoString value; 391 attr->GetValue(value); 392 CopyUTF16toUTF8(value, aValue); 393 } else { 394 aValue.Truncate(); 395 } 396 } 397 398 nsresult ResourceReader::OnWalkAttribute(dom::Element* aElement, 399 nsContentPolicyType aContentPolicyType, 400 const char* aAttribute, 401 const char* aNamespaceURI) { 402 nsAutoCString uriSpec; 403 ExtractAttribute(aElement, aAttribute, aNamespaceURI, uriSpec); 404 if (uriSpec.IsEmpty()) { 405 return NS_OK; 406 } 407 return OnWalkURI(uriSpec, aContentPolicyType); 408 } 409 410 nsresult ResourceReader::OnWalkSrcSet(dom::Element* aElement) { 411 nsAutoString srcSet; 412 if (!aElement->GetAttr(nsGkAtoms::srcset, srcSet)) { 413 return NS_OK; 414 } 415 416 nsresult rv = NS_OK; 417 auto eachCandidate = [&](dom::ResponsiveImageCandidate&& aCandidate) { 418 if (!aCandidate.IsValid() || NS_FAILED(rv)) { 419 return; 420 } 421 rv = OnWalkURI(NS_ConvertUTF16toUTF8(aCandidate.URLString()), 422 nsIContentPolicy::TYPE_IMAGE); 423 }; 424 dom::ResponsiveImageSelector::ParseSourceSet(srcSet, eachCandidate); 425 return rv; 426 } 427 428 static nsresult GetXMLStyleSheetLink(dom::ProcessingInstruction* aPI, 429 nsAString& aHref) { 430 nsAutoString data; 431 aPI->GetData(data); 432 433 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref); 434 return NS_OK; 435 } 436 437 nsresult ResourceReader::OnWalkDOMNode(nsINode* aNode) { 438 // Fixup xml-stylesheet processing instructions 439 if (auto nodeAsPI = dom::ProcessingInstruction::FromNode(aNode)) { 440 nsAutoString target; 441 nodeAsPI->GetTarget(target); 442 if (target.EqualsLiteral("xml-stylesheet")) { 443 nsAutoString href; 444 GetXMLStyleSheetLink(nodeAsPI, href); 445 if (!href.IsEmpty()) { 446 return OnWalkURI(NS_ConvertUTF16toUTF8(href), 447 nsIContentPolicy::TYPE_STYLESHEET); 448 } 449 } 450 return NS_OK; 451 } 452 453 // Test the node to see if it's an image, frame, iframe, css, js 454 if (auto* img = dom::HTMLImageElement::FromNode(*aNode)) { 455 MOZ_TRY(OnWalkAttribute(img, nsIContentPolicy::TYPE_IMAGE, "src")); 456 MOZ_TRY(OnWalkSrcSet(img)); 457 return NS_OK; 458 } 459 460 if (aNode->IsSVGElement(nsGkAtoms::image)) { 461 MOZ_TRY(OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE, 462 "href")); 463 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE, 464 "href", "http://www.w3.org/1999/xlink"); 465 } 466 467 if (aNode->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) { 468 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_MEDIA, 469 "src"); 470 } 471 472 if (aNode->IsHTMLElement(nsGkAtoms::source)) { 473 MOZ_TRY(OnWalkSrcSet(aNode->AsElement())); 474 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_MEDIA, 475 "src"); 476 } 477 478 if (aNode->IsHTMLElement(nsGkAtoms::body)) { 479 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE, 480 "background"); 481 } 482 483 if (aNode->IsHTMLElement(nsGkAtoms::table)) { 484 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE, 485 "background"); 486 } 487 488 if (aNode->IsHTMLElement(nsGkAtoms::tr)) { 489 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE, 490 "background"); 491 } 492 493 if (aNode->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) { 494 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE, 495 "background"); 496 } 497 498 if (aNode->IsHTMLElement(nsGkAtoms::script)) { 499 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_SCRIPT, 500 "src"); 501 } 502 503 if (aNode->IsSVGElement(nsGkAtoms::script)) { 504 MOZ_TRY(OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_SCRIPT, 505 "href")); 506 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_SCRIPT, 507 "href", "http://www.w3.org/1999/xlink"); 508 } 509 510 if (aNode->IsHTMLElement(nsGkAtoms::embed)) { 511 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_OBJECT, 512 "src"); 513 } 514 515 if (aNode->IsHTMLElement(nsGkAtoms::object)) { 516 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_OBJECT, 517 "data"); 518 } 519 520 if (auto nodeAsLink = dom::HTMLLinkElement::FromNode(aNode)) { 521 // Test if the link has a rel value indicating it to be a stylesheet 522 nsAutoString linkRel; 523 nodeAsLink->GetRel(linkRel); 524 if (!linkRel.IsEmpty()) { 525 nsReadingIterator<char16_t> start; 526 nsReadingIterator<char16_t> end; 527 nsReadingIterator<char16_t> current; 528 529 linkRel.BeginReading(start); 530 linkRel.EndReading(end); 531 532 // Walk through space delimited string looking for "stylesheet" 533 for (current = start; current != end; ++current) { 534 // Ignore whitespace 535 if (nsCRT::IsAsciiSpace(*current)) { 536 continue; 537 } 538 539 // Grab the next space delimited word 540 nsReadingIterator<char16_t> startWord = current; 541 do { 542 ++current; 543 } while (current != end && !nsCRT::IsAsciiSpace(*current)); 544 545 // Store the link for fix up if it says "stylesheet" 546 if (Substring(startWord, current) 547 .LowerCaseEqualsLiteral("stylesheet")) { 548 OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_STYLESHEET, 549 "href"); 550 return NS_OK; 551 } 552 if (current == end) { 553 break; 554 } 555 } 556 } 557 return NS_OK; 558 } 559 560 if (aNode->IsHTMLElement(nsGkAtoms::frame)) { 561 return OnWalkSubframe(aNode); 562 } 563 564 if (aNode->IsHTMLElement(nsGkAtoms::iframe) && 565 !(mPersistFlags & IWBP::PERSIST_FLAGS_IGNORE_IFRAMES)) { 566 return OnWalkSubframe(aNode); 567 } 568 569 auto nodeAsInput = dom::HTMLInputElement::FromNode(aNode); 570 if (nodeAsInput) { 571 return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE, 572 "src"); 573 } 574 575 return NS_OK; 576 } 577 578 // Helper class for node rewriting in writeContent(). 579 class PersistNodeFixup final : public nsIDocumentEncoderNodeFixup { 580 public: 581 PersistNodeFixup(WebBrowserPersistLocalDocument* aParent, 582 nsIWebBrowserPersistURIMap* aMap, nsIURI* aTargetURI); 583 584 NS_DECL_ISUPPORTS 585 NS_DECL_NSIDOCUMENTENCODERNODEFIXUP 586 private: 587 virtual ~PersistNodeFixup() = default; 588 RefPtr<WebBrowserPersistLocalDocument> mParent; 589 nsClassHashtable<nsCStringHashKey, nsCString> mMap; 590 nsCOMPtr<nsIURI> mCurrentBaseURI; 591 nsCOMPtr<nsIURI> mTargetBaseURI; 592 593 bool IsFlagSet(uint32_t aFlag) const { 594 return mParent->GetPersistFlags() & aFlag; 595 } 596 597 nsresult GetNodeToFixup(nsINode* aNodeIn, nsINode** aNodeOut); 598 nsresult FixupURI(nsAString& aURI); 599 nsresult FixupAttribute(nsINode* aNode, const char* aAttribute, 600 const char* aNamespaceURI = ""); 601 nsresult FixupAnchor(nsINode* aNode); 602 nsresult FixupXMLStyleSheetLink(dom::ProcessingInstruction* aPI, 603 const nsAString& aHref); 604 605 nsresult FixupSrcSet(nsINode*); 606 607 using IWBP = nsIWebBrowserPersist; 608 }; 609 610 NS_IMPL_ISUPPORTS(PersistNodeFixup, nsIDocumentEncoderNodeFixup) 611 612 PersistNodeFixup::PersistNodeFixup(WebBrowserPersistLocalDocument* aParent, 613 nsIWebBrowserPersistURIMap* aMap, 614 nsIURI* aTargetURI) 615 : mParent(aParent), 616 mCurrentBaseURI(aParent->GetBaseURI()), 617 mTargetBaseURI(aTargetURI) { 618 if (aMap) { 619 uint32_t mapSize; 620 nsresult rv = aMap->GetNumMappedURIs(&mapSize); 621 MOZ_ASSERT(NS_SUCCEEDED(rv)); 622 NS_ENSURE_SUCCESS_VOID(rv); 623 for (uint32_t i = 0; i < mapSize; ++i) { 624 nsAutoCString urlFrom; 625 auto urlTo = MakeUnique<nsCString>(); 626 627 rv = aMap->GetURIMapping(i, urlFrom, *urlTo); 628 MOZ_ASSERT(NS_SUCCEEDED(rv)); 629 if (NS_SUCCEEDED(rv)) { 630 mMap.InsertOrUpdate(urlFrom, std::move(urlTo)); 631 } 632 } 633 } 634 } 635 636 nsresult PersistNodeFixup::GetNodeToFixup(nsINode* aNodeIn, 637 nsINode** aNodeOut) { 638 // Avoid mixups in FixupNode that could leak objects; this goes 639 // against the usual out parameter convention, but it's a private 640 // method so shouldn't be a problem. 641 MOZ_ASSERT(!*aNodeOut); 642 643 if (!IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_ORIGINAL_DOM)) { 644 ErrorResult rv; 645 *aNodeOut = aNodeIn->CloneNode(false, rv).take(); 646 return rv.StealNSResult(); 647 } 648 649 NS_ADDREF(*aNodeOut = aNodeIn); 650 return NS_OK; 651 } 652 653 nsresult PersistNodeFixup::FixupURI(nsAString& aURI) { 654 // get the current location of the file (absolutized) 655 nsCOMPtr<nsIURI> uri; 656 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI, mParent->GetCharacterSet(), 657 mCurrentBaseURI); 658 NS_ENSURE_SUCCESS(rv, rv); 659 nsAutoCString spec; 660 rv = uri->GetSpec(spec); 661 NS_ENSURE_SUCCESS(rv, rv); 662 663 const nsCString* replacement = mMap.Get(spec); 664 if (!replacement) { 665 // Note that most callers ignore this "failure". 666 return NS_ERROR_FAILURE; 667 } 668 if (!replacement->IsEmpty()) { 669 CopyUTF8toUTF16(*replacement, aURI); 670 } 671 return NS_OK; 672 } 673 674 nsresult PersistNodeFixup::FixupSrcSet(nsINode* aNode) { 675 dom::Element* element = aNode->AsElement(); 676 nsAutoString originalSrcSet; 677 if (!element->GetAttr(nsGkAtoms::srcset, originalSrcSet)) { 678 return NS_OK; 679 } 680 nsAutoString newSrcSet; 681 bool first = true; 682 auto eachCandidate = [&](dom::ResponsiveImageCandidate&& aCandidate) { 683 if (!aCandidate.IsValid()) { 684 return; 685 } 686 if (!first) { 687 newSrcSet.AppendLiteral(", "); 688 } 689 first = false; 690 nsAutoString uri(aCandidate.URLString()); 691 FixupURI(uri); 692 newSrcSet.Append(uri); 693 aCandidate.AppendDescriptors(newSrcSet); 694 }; 695 dom::ResponsiveImageSelector::ParseSourceSet(originalSrcSet, eachCandidate); 696 element->SetAttr(nsGkAtoms::srcset, newSrcSet, IgnoreErrors()); 697 return NS_OK; 698 } 699 700 nsresult PersistNodeFixup::FixupAttribute(nsINode* aNode, 701 const char* aAttribute, 702 const char* aNamespaceURI) { 703 MOZ_ASSERT(aNode->IsElement()); 704 dom::Element* element = aNode->AsElement(); 705 706 RefPtr<nsDOMAttributeMap> attrMap = element->Attributes(); 707 708 NS_ConvertASCIItoUTF16 attribute(aAttribute); 709 NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI); 710 RefPtr<dom::Attr> attr = attrMap->GetNamedItemNS(namespaceURI, attribute); 711 nsresult rv = NS_OK; 712 if (attr) { 713 nsString uri; 714 attr->GetValue(uri); 715 rv = FixupURI(uri); 716 if (NS_SUCCEEDED(rv)) { 717 attr->SetValueInternal(uri, IgnoreErrors()); 718 } 719 } 720 721 return rv; 722 } 723 724 nsresult PersistNodeFixup::FixupAnchor(nsINode* aNode) { 725 if (IsFlagSet(IWBP::PERSIST_FLAGS_DONT_FIXUP_LINKS)) { 726 return NS_OK; 727 } 728 729 MOZ_ASSERT(aNode->IsElement()); 730 dom::Element* element = aNode->AsElement(); 731 732 RefPtr<nsDOMAttributeMap> attrMap = element->Attributes(); 733 734 // Make all anchor links absolute so they point off onto the Internet 735 nsString attribute(u"href"_ns); 736 RefPtr<dom::Attr> attr = attrMap->GetNamedItem(attribute); 737 if (attr) { 738 nsString oldValue; 739 attr->GetValue(oldValue); 740 NS_ConvertUTF16toUTF8 oldCValue(oldValue); 741 742 // Skip empty values and self-referencing bookmarks 743 if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#') { 744 return NS_OK; 745 } 746 747 // if saving file to same location, we don't need to do any fixup 748 bool isEqual; 749 if (mTargetBaseURI && 750 NS_SUCCEEDED(mCurrentBaseURI->Equals(mTargetBaseURI, &isEqual)) && 751 isEqual) { 752 return NS_OK; 753 } 754 755 nsCOMPtr<nsIURI> relativeURI; 756 relativeURI = IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION) 757 ? mTargetBaseURI 758 : mCurrentBaseURI; 759 // Make a new URI to replace the current one 760 nsCOMPtr<nsIURI> newURI; 761 nsresult rv = NS_NewURI(getter_AddRefs(newURI), oldCValue, 762 mParent->GetCharacterSet(), relativeURI); 763 if (NS_SUCCEEDED(rv) && newURI) { 764 (void)NS_MutateURI(newURI).SetUserPass(""_ns).Finalize(newURI); 765 nsAutoCString uriSpec; 766 rv = newURI->GetSpec(uriSpec); 767 NS_ENSURE_SUCCESS(rv, rv); 768 attr->SetValueInternal(NS_ConvertUTF8toUTF16(uriSpec), IgnoreErrors()); 769 } 770 } 771 772 return NS_OK; 773 } 774 775 static void AppendXMLAttr(const nsAString& key, const nsAString& aValue, 776 nsAString& aBuffer) { 777 if (!aBuffer.IsEmpty()) { 778 aBuffer.Append(' '); 779 } 780 aBuffer.Append(key); 781 aBuffer.AppendLiteral(R"(=")"); 782 for (size_t i = 0; i < aValue.Length(); ++i) { 783 switch (aValue[i]) { 784 case '&': 785 aBuffer.AppendLiteral("&"); 786 break; 787 case '<': 788 aBuffer.AppendLiteral("<"); 789 break; 790 case '>': 791 aBuffer.AppendLiteral(">"); 792 break; 793 case '"': 794 aBuffer.AppendLiteral("""); 795 break; 796 default: 797 aBuffer.Append(aValue[i]); 798 break; 799 } 800 } 801 aBuffer.Append('"'); 802 } 803 804 nsresult PersistNodeFixup::FixupXMLStyleSheetLink( 805 dom::ProcessingInstruction* aPI, const nsAString& aHref) { 806 NS_ENSURE_ARG_POINTER(aPI); 807 808 nsAutoString data; 809 aPI->GetData(data); 810 811 nsAutoString href; 812 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, href); 813 814 // Construct and set a new data value for the xml-stylesheet 815 if (!aHref.IsEmpty() && !href.IsEmpty()) { 816 nsAutoString alternate; 817 nsAutoString charset; 818 nsAutoString title; 819 nsAutoString type; 820 nsAutoString media; 821 822 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::alternate, 823 alternate); 824 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::charset, charset); 825 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::title, title); 826 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type); 827 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::media, media); 828 829 nsAutoString newData; 830 AppendXMLAttr(u"href"_ns, aHref, newData); 831 if (!title.IsEmpty()) { 832 AppendXMLAttr(u"title"_ns, title, newData); 833 } 834 if (!media.IsEmpty()) { 835 AppendXMLAttr(u"media"_ns, media, newData); 836 } 837 if (!type.IsEmpty()) { 838 AppendXMLAttr(u"type"_ns, type, newData); 839 } 840 if (!charset.IsEmpty()) { 841 AppendXMLAttr(u"charset"_ns, charset, newData); 842 } 843 if (!alternate.IsEmpty()) { 844 AppendXMLAttr(u"alternate"_ns, alternate, newData); 845 } 846 aPI->SetData(newData, IgnoreErrors()); 847 } 848 849 return NS_OK; 850 } 851 852 NS_IMETHODIMP 853 PersistNodeFixup::FixupNode(nsINode* aNodeIn, bool* aSerializeCloneKids, 854 nsINode** aNodeOut) { 855 *aNodeOut = nullptr; 856 *aSerializeCloneKids = false; 857 858 uint16_t type = aNodeIn->NodeType(); 859 if (type != nsINode::ELEMENT_NODE && 860 type != nsINode::PROCESSING_INSTRUCTION_NODE) { 861 return NS_OK; 862 } 863 864 MOZ_ASSERT(aNodeIn->IsContent()); 865 866 // Fixup xml-stylesheet processing instructions 867 if (auto nodeAsPI = dom::ProcessingInstruction::FromNode(aNodeIn)) { 868 nsAutoString target; 869 nodeAsPI->GetTarget(target); 870 if (target.EqualsLiteral("xml-stylesheet")) { 871 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 872 if (NS_SUCCEEDED(rv) && *aNodeOut) { 873 MOZ_ASSERT((*aNodeOut)->IsProcessingInstruction()); 874 auto nodeAsPI = static_cast<dom::ProcessingInstruction*>(*aNodeOut); 875 nsAutoString href; 876 GetXMLStyleSheetLink(nodeAsPI, href); 877 if (!href.IsEmpty()) { 878 FixupURI(href); 879 FixupXMLStyleSheetLink(nodeAsPI, href); 880 } 881 } 882 } 883 return NS_OK; 884 } 885 886 nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn); 887 if (!content) { 888 return NS_OK; 889 } 890 891 // BASE elements are replaced by a comment so relative links are not hosed. 892 if (!IsFlagSet(IWBP::PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS) && 893 content->IsHTMLElement(nsGkAtoms::base)) { 894 // Base uses HTMLSharedElement, which would be awkward to implement 895 // FromContent on, since it represents multiple elements. Since we've 896 // already checked IsHTMLElement here, just cast as we were doing. 897 auto* base = static_cast<dom::HTMLSharedElement*>(content.get()); 898 dom::Document* ownerDoc = base->OwnerDoc(); 899 900 nsAutoString href; 901 base->GetHref(href); // Doesn't matter if this fails 902 nsAutoString commentText; 903 commentText.AssignLiteral(" base "); 904 if (!href.IsEmpty()) { 905 commentText += u"href=\""_ns + href + u"\" "_ns; 906 } 907 *aNodeOut = ownerDoc->CreateComment(commentText).take(); 908 return NS_OK; 909 } 910 911 // Fix up href and file links in the elements 912 RefPtr<dom::HTMLAnchorElement> nodeAsAnchor = 913 dom::HTMLAnchorElement::FromNode(content); 914 if (nodeAsAnchor) { 915 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 916 if (NS_SUCCEEDED(rv) && *aNodeOut) { 917 FixupAnchor(*aNodeOut); 918 } 919 return rv; 920 } 921 922 RefPtr<dom::HTMLAreaElement> nodeAsArea = 923 dom::HTMLAreaElement::FromNode(content); 924 if (nodeAsArea) { 925 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 926 if (NS_SUCCEEDED(rv) && *aNodeOut) { 927 FixupAnchor(*aNodeOut); 928 } 929 return rv; 930 } 931 932 if (content->IsHTMLElement(nsGkAtoms::body)) { 933 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 934 if (NS_SUCCEEDED(rv) && *aNodeOut) { 935 FixupAttribute(*aNodeOut, "background"); 936 } 937 return rv; 938 } 939 940 if (content->IsHTMLElement(nsGkAtoms::table)) { 941 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 942 if (NS_SUCCEEDED(rv) && *aNodeOut) { 943 FixupAttribute(*aNodeOut, "background"); 944 } 945 return rv; 946 } 947 948 if (content->IsHTMLElement(nsGkAtoms::tr)) { 949 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 950 if (NS_SUCCEEDED(rv) && *aNodeOut) { 951 FixupAttribute(*aNodeOut, "background"); 952 } 953 return rv; 954 } 955 956 if (content->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) { 957 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 958 if (NS_SUCCEEDED(rv) && *aNodeOut) { 959 FixupAttribute(*aNodeOut, "background"); 960 } 961 return rv; 962 } 963 964 if (content->IsHTMLElement(nsGkAtoms::img)) { 965 MOZ_TRY(GetNodeToFixup(aNodeIn, aNodeOut)); 966 if (!*aNodeOut) { 967 return NS_OK; 968 } 969 970 // Disable image loads 971 nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut); 972 if (imgCon) { 973 imgCon->SetLoadingEnabled(false); 974 } 975 // FIXME(emilio): Why fixing up <img href>? Looks bogus 976 FixupAnchor(*aNodeOut); 977 FixupAttribute(*aNodeOut, "src"); 978 FixupSrcSet(*aNodeOut); 979 return NS_OK; 980 } 981 982 if (content->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) { 983 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 984 if (NS_SUCCEEDED(rv) && *aNodeOut) { 985 FixupAttribute(*aNodeOut, "src"); 986 } 987 return rv; 988 } 989 990 if (content->IsHTMLElement(nsGkAtoms::source)) { 991 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 992 if (NS_SUCCEEDED(rv) && *aNodeOut) { 993 FixupAttribute(*aNodeOut, "src"); 994 FixupSrcSet(*aNodeOut); 995 } 996 return rv; 997 } 998 999 if (content->IsSVGElement(nsGkAtoms::image)) { 1000 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1001 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1002 // Disable image loads 1003 nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut); 1004 if (imgCon) imgCon->SetLoadingEnabled(false); 1005 1006 // FixupAnchor(*aNodeOut); // XXXjwatt: is this line needed? 1007 FixupAttribute(*aNodeOut, "href"); 1008 FixupAttribute(*aNodeOut, "href", "http://www.w3.org/1999/xlink"); 1009 } 1010 return rv; 1011 } 1012 1013 if (content->IsHTMLElement(nsGkAtoms::script)) { 1014 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1015 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1016 FixupAttribute(*aNodeOut, "src"); 1017 } 1018 return rv; 1019 } 1020 1021 if (content->IsSVGElement(nsGkAtoms::script)) { 1022 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1023 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1024 FixupAttribute(*aNodeOut, "href"); 1025 FixupAttribute(*aNodeOut, "href", "http://www.w3.org/1999/xlink"); 1026 } 1027 return rv; 1028 } 1029 1030 if (content->IsHTMLElement(nsGkAtoms::embed)) { 1031 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1032 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1033 FixupAttribute(*aNodeOut, "src"); 1034 } 1035 return rv; 1036 } 1037 1038 if (content->IsHTMLElement(nsGkAtoms::object)) { 1039 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1040 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1041 FixupAttribute(*aNodeOut, "data"); 1042 } 1043 return rv; 1044 } 1045 1046 if (content->IsHTMLElement(nsGkAtoms::link)) { 1047 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1048 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1049 // First see if the link represents linked content 1050 rv = FixupAttribute(*aNodeOut, "href"); 1051 if (NS_FAILED(rv)) { 1052 // Perhaps this link is actually an anchor to related content 1053 FixupAnchor(*aNodeOut); 1054 } 1055 // TODO if "type" attribute == "text/css" 1056 // fixup stylesheet 1057 } 1058 return rv; 1059 } 1060 1061 if (content->IsHTMLElement(nsGkAtoms::frame)) { 1062 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1063 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1064 FixupAttribute(*aNodeOut, "src"); 1065 } 1066 return rv; 1067 } 1068 1069 if (content->IsHTMLElement(nsGkAtoms::iframe)) { 1070 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1071 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1072 FixupAttribute(*aNodeOut, "src"); 1073 } 1074 return rv; 1075 } 1076 1077 RefPtr<dom::HTMLInputElement> nodeAsInput = 1078 dom::HTMLInputElement::FromNodeOrNull(content); 1079 if (nodeAsInput) { 1080 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1081 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1082 // Disable image loads 1083 nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut); 1084 if (imgCon) { 1085 imgCon->SetLoadingEnabled(false); 1086 } 1087 1088 FixupAttribute(*aNodeOut, "src"); 1089 1090 nsAutoString valueStr; 1091 constexpr auto valueAttr = u"value"_ns; 1092 // Update element node attributes with user-entered form state 1093 RefPtr<dom::HTMLInputElement> outElt = 1094 dom::HTMLInputElement::FromNode((*aNodeOut)->AsContent()); 1095 switch (nsIFormControl::FromNode(*aNodeOut)->ControlType()) { 1096 case FormControlType::InputEmail: 1097 case FormControlType::InputSearch: 1098 case FormControlType::InputText: 1099 case FormControlType::InputTel: 1100 case FormControlType::InputUrl: 1101 case FormControlType::InputNumber: 1102 case FormControlType::InputRange: 1103 case FormControlType::InputDate: 1104 case FormControlType::InputTime: 1105 case FormControlType::InputColor: 1106 nodeAsInput->GetValue(valueStr, dom::CallerType::System); 1107 // Avoid superfluous value="" serialization 1108 if (valueStr.IsEmpty()) { 1109 outElt->RemoveAttribute(valueAttr, IgnoreErrors()); 1110 } else { 1111 outElt->SetAttribute(valueAttr, valueStr, IgnoreErrors()); 1112 } 1113 break; 1114 case FormControlType::InputCheckbox: 1115 case FormControlType::InputRadio: 1116 outElt->SetDefaultChecked(nodeAsInput->Checked(), IgnoreErrors()); 1117 break; 1118 default: 1119 break; 1120 } 1121 } 1122 return rv; 1123 } 1124 1125 dom::HTMLTextAreaElement* nodeAsTextArea = 1126 dom::HTMLTextAreaElement::FromNode(content); 1127 if (nodeAsTextArea) { 1128 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1129 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1130 // Tell the document encoder to serialize the text child we create below 1131 *aSerializeCloneKids = true; 1132 1133 nsAutoString valueStr; 1134 nodeAsTextArea->GetValue(valueStr); 1135 1136 (*aNodeOut)->SetTextContent(valueStr, IgnoreErrors()); 1137 } 1138 return rv; 1139 } 1140 1141 dom::HTMLOptionElement* nodeAsOption = 1142 dom::HTMLOptionElement::FromNode(content); 1143 if (nodeAsOption) { 1144 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut); 1145 if (NS_SUCCEEDED(rv) && *aNodeOut) { 1146 dom::HTMLOptionElement* outElt = 1147 dom::HTMLOptionElement::FromNode((*aNodeOut)->AsContent()); 1148 bool selected = nodeAsOption->Selected(); 1149 outElt->SetDefaultSelected(selected, IgnoreErrors()); 1150 } 1151 return rv; 1152 } 1153 1154 return NS_OK; 1155 } 1156 1157 } // unnamed namespace 1158 1159 NS_IMETHODIMP 1160 WebBrowserPersistLocalDocument::ReadResources( 1161 nsIWebBrowserPersistResourceVisitor* aVisitor) { 1162 nsresult rv = NS_OK; 1163 nsCOMPtr<nsIWebBrowserPersistResourceVisitor> visitor = aVisitor; 1164 1165 NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE); 1166 1167 ErrorResult err; 1168 RefPtr<dom::TreeWalker> walker = mDocument->CreateTreeWalker( 1169 *mDocument, 1170 dom::NodeFilter_Binding::SHOW_ELEMENT | 1171 dom::NodeFilter_Binding::SHOW_DOCUMENT | 1172 dom::NodeFilter_Binding::SHOW_PROCESSING_INSTRUCTION, 1173 nullptr, err); 1174 1175 if (NS_WARN_IF(err.Failed())) { 1176 return err.StealNSResult(); 1177 } 1178 MOZ_ASSERT(walker); 1179 1180 RefPtr<ResourceReader> reader = new ResourceReader(this, aVisitor); 1181 nsCOMPtr<nsINode> currentNode = walker->CurrentNode(); 1182 do { 1183 rv = reader->OnWalkDOMNode(currentNode); 1184 if (NS_WARN_IF(NS_FAILED(rv))) { 1185 break; 1186 } 1187 1188 ErrorResult err; 1189 currentNode = walker->NextNode(err); 1190 if (NS_WARN_IF(err.Failed())) { 1191 err.SuppressException(); 1192 break; 1193 } 1194 } while (currentNode); 1195 reader->DocumentDone(rv); 1196 // If NS_FAILED(rv), it was / will be reported by an EndVisit call 1197 // via DocumentDone. This method must return a failure if and 1198 // only if visitor won't be invoked. 1199 return NS_OK; 1200 } 1201 1202 static uint32_t ConvertEncoderFlags(uint32_t aEncoderFlags) { 1203 uint32_t encoderFlags = 0; 1204 1205 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_SELECTION_ONLY) 1206 encoderFlags |= nsIDocumentEncoder::OutputSelectionOnly; 1207 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_FORMATTED) 1208 encoderFlags |= nsIDocumentEncoder::OutputFormatted; 1209 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_RAW) 1210 encoderFlags |= nsIDocumentEncoder::OutputRaw; 1211 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_BODY_ONLY) 1212 encoderFlags |= nsIDocumentEncoder::OutputBodyOnly; 1213 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_PREFORMATTED) 1214 encoderFlags |= nsIDocumentEncoder::OutputPreformatted; 1215 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_WRAP) 1216 encoderFlags |= nsIDocumentEncoder::OutputWrap; 1217 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_FORMAT_FLOWED) 1218 encoderFlags |= nsIDocumentEncoder::OutputFormatFlowed; 1219 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ABSOLUTE_LINKS) 1220 encoderFlags |= nsIDocumentEncoder::OutputAbsoluteLinks; 1221 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ENCODE_BASIC_ENTITIES) 1222 encoderFlags |= nsIDocumentEncoder::OutputEncodeBasicEntities; 1223 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_CR_LINEBREAKS) 1224 encoderFlags |= nsIDocumentEncoder::OutputCRLineBreak; 1225 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_LF_LINEBREAKS) 1226 encoderFlags |= nsIDocumentEncoder::OutputLFLineBreak; 1227 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_NOSCRIPT_CONTENT) 1228 encoderFlags |= nsIDocumentEncoder::OutputNoScriptContent; 1229 if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_NOFRAMES_CONTENT) 1230 encoderFlags |= nsIDocumentEncoder::OutputNoFramesContent; 1231 1232 return encoderFlags; 1233 } 1234 1235 static bool ContentTypeEncoderExists(const nsACString& aType) { 1236 return do_getDocumentTypeSupportedForEncoding( 1237 PromiseFlatCString(aType).get()); 1238 } 1239 1240 void WebBrowserPersistLocalDocument::DecideContentType( 1241 nsACString& aContentType) { 1242 if (aContentType.IsEmpty()) { 1243 if (NS_WARN_IF(NS_FAILED(GetContentType(aContentType)))) { 1244 aContentType.Truncate(); 1245 } 1246 } 1247 if (!aContentType.IsEmpty() && !ContentTypeEncoderExists(aContentType)) { 1248 aContentType.Truncate(); 1249 } 1250 if (aContentType.IsEmpty()) { 1251 aContentType.AssignLiteral("text/html"); 1252 } 1253 } 1254 1255 nsresult WebBrowserPersistLocalDocument::GetDocEncoder( 1256 const nsACString& aContentType, uint32_t aEncoderFlags, 1257 nsIDocumentEncoder** aEncoder) { 1258 nsCOMPtr<nsIDocumentEncoder> encoder = 1259 do_createDocumentEncoder(PromiseFlatCString(aContentType).get()); 1260 NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE); 1261 1262 nsresult rv = encoder->Init(mDocument, NS_ConvertASCIItoUTF16(aContentType), 1263 ConvertEncoderFlags(aEncoderFlags)); 1264 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1265 1266 nsAutoCString charSet; 1267 rv = GetCharacterSet(charSet); 1268 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1269 rv = encoder->SetCharset(charSet); 1270 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1271 1272 encoder.forget(aEncoder); 1273 return NS_OK; 1274 } 1275 1276 NS_IMETHODIMP 1277 WebBrowserPersistLocalDocument::WriteContent( 1278 nsIOutputStream* aStream, nsIWebBrowserPersistURIMap* aMap, 1279 const nsACString& aRequestedContentType, uint32_t aEncoderFlags, 1280 uint32_t aWrapColumn, nsIWebBrowserPersistWriteCompletion* aCompletion) { 1281 NS_ENSURE_ARG_POINTER(aStream); 1282 NS_ENSURE_ARG_POINTER(aCompletion); 1283 nsAutoCString contentType(aRequestedContentType); 1284 DecideContentType(contentType); 1285 1286 nsCOMPtr<nsIDocumentEncoder> encoder; 1287 nsresult rv = 1288 GetDocEncoder(contentType, aEncoderFlags, getter_AddRefs(encoder)); 1289 NS_ENSURE_SUCCESS(rv, rv); 1290 1291 if (aWrapColumn != 0 && 1292 (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_WRAP)) { 1293 encoder->SetWrapColumn(aWrapColumn); 1294 } 1295 1296 nsCOMPtr<nsIURI> targetURI; 1297 if (aMap) { 1298 nsAutoCString targetURISpec; 1299 rv = aMap->GetTargetBaseURI(targetURISpec); 1300 if (NS_SUCCEEDED(rv) && !targetURISpec.IsEmpty()) { 1301 rv = NS_NewURI(getter_AddRefs(targetURI), targetURISpec); 1302 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); 1303 } else if (mPersistFlags & 1304 nsIWebBrowserPersist::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION) { 1305 return NS_ERROR_UNEXPECTED; 1306 } 1307 } 1308 rv = encoder->SetNodeFixup(new PersistNodeFixup(this, aMap, targetURI)); 1309 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1310 1311 rv = encoder->EncodeToStream(aStream); 1312 aCompletion->OnFinish(this, aStream, contentType, rv); 1313 return NS_OK; 1314 } 1315 1316 } // namespace mozilla