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