Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; 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/. */
6 #include "SVGDocumentWrapper.h"
8 #include "mozilla/dom/Element.h"
9 #include "nsICategoryManager.h"
10 #include "nsIChannel.h"
11 #include "nsIContentViewer.h"
12 #include "nsIDocument.h"
13 #include "nsIDocumentLoaderFactory.h"
14 #include "nsIDOMSVGLength.h"
15 #include "nsIHttpChannel.h"
16 #include "nsIObserverService.h"
17 #include "nsIParser.h"
18 #include "nsIPresShell.h"
19 #include "nsIRequest.h"
20 #include "nsIStreamListener.h"
21 #include "nsIXMLContentSink.h"
22 #include "nsNetCID.h"
23 #include "nsComponentManagerUtils.h"
24 #include "nsSMILAnimationController.h"
25 #include "nsServiceManagerUtils.h"
26 #include "mozilla/dom/SVGSVGElement.h"
27 #include "nsSVGEffects.h"
28 #include "mozilla/dom/SVGAnimatedLength.h"
29 #include "nsMimeTypes.h"
30 #include "DOMSVGLength.h"
32 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
33 #undef GetCurrentTime
35 using namespace mozilla::dom;
37 namespace mozilla {
38 namespace image {
40 NS_IMPL_ISUPPORTS(SVGDocumentWrapper,
41 nsIStreamListener,
42 nsIRequestObserver,
43 nsIObserver,
44 nsISupportsWeakReference)
46 SVGDocumentWrapper::SVGDocumentWrapper()
47 : mIgnoreInvalidation(false),
48 mRegisteredForXPCOMShutdown(false)
49 {
50 }
52 SVGDocumentWrapper::~SVGDocumentWrapper()
53 {
54 DestroyViewer();
55 if (mRegisteredForXPCOMShutdown) {
56 UnregisterForXPCOMShutdown();
57 }
58 }
60 void
61 SVGDocumentWrapper::DestroyViewer()
62 {
63 if (mViewer) {
64 mViewer->GetDocument()->OnPageHide(false, nullptr);
65 mViewer->Close(nullptr);
66 mViewer->Destroy();
67 mViewer = nullptr;
68 }
69 }
71 bool
72 SVGDocumentWrapper::GetWidthOrHeight(Dimension aDimension,
73 int32_t& aResult)
74 {
75 SVGSVGElement* rootElem = GetRootSVGElem();
76 NS_ABORT_IF_FALSE(rootElem, "root elem missing or of wrong type");
78 // Get the width or height SVG object
79 nsRefPtr<SVGAnimatedLength> domAnimLength;
80 if (aDimension == eWidth) {
81 domAnimLength = rootElem->Width();
82 } else {
83 NS_ABORT_IF_FALSE(aDimension == eHeight, "invalid dimension");
84 domAnimLength = rootElem->Height();
85 }
86 NS_ENSURE_TRUE(domAnimLength, false);
88 // Get the animated value from the object
89 nsRefPtr<DOMSVGLength> domLength = domAnimLength->AnimVal();
90 NS_ENSURE_TRUE(domLength, false);
92 // Check if it's a percent value (and fail if so)
93 uint16_t unitType;
94 nsresult rv = domLength->GetUnitType(&unitType);
95 NS_ENSURE_SUCCESS(rv, false);
96 if (unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE) {
97 return false;
98 }
100 // Non-percent value - woot! Grab it & return it.
101 float floatLength;
102 rv = domLength->GetValue(&floatLength);
103 NS_ENSURE_SUCCESS(rv, false);
105 aResult = nsSVGUtils::ClampToInt(floatLength);
107 return true;
108 }
110 nsIFrame*
111 SVGDocumentWrapper::GetRootLayoutFrame()
112 {
113 Element* rootElem = GetRootSVGElem();
114 return rootElem ? rootElem->GetPrimaryFrame() : nullptr;
115 }
117 void
118 SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize)
119 {
120 NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant");
121 mIgnoreInvalidation = true;
123 nsIntRect currentBounds;
124 mViewer->GetBounds(currentBounds);
126 // If the bounds have changed, we need to do a layout flush.
127 if (currentBounds.Size() != aViewportSize) {
128 mViewer->SetBounds(nsIntRect(nsIntPoint(0, 0), aViewportSize));
129 FlushLayout();
130 }
132 mIgnoreInvalidation = false;
133 }
135 void
136 SVGDocumentWrapper::FlushImageTransformInvalidation()
137 {
138 NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant");
140 SVGSVGElement* svgElem = GetRootSVGElem();
141 if (!svgElem)
142 return;
144 mIgnoreInvalidation = true;
145 svgElem->FlushImageTransformInvalidation();
146 FlushLayout();
147 mIgnoreInvalidation = false;
148 }
150 bool
151 SVGDocumentWrapper::IsAnimated()
152 {
153 nsIDocument* doc = mViewer->GetDocument();
154 return doc && doc->HasAnimationController() &&
155 doc->GetAnimationController()->HasRegisteredAnimations();
156 }
158 void
159 SVGDocumentWrapper::StartAnimation()
160 {
161 // Can be called for animated images during shutdown, after we've
162 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
163 if (!mViewer)
164 return;
166 nsIDocument* doc = mViewer->GetDocument();
167 if (doc) {
168 nsSMILAnimationController* controller = doc->GetAnimationController();
169 if (controller) {
170 controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
171 }
172 doc->SetImagesNeedAnimating(true);
173 }
174 }
176 void
177 SVGDocumentWrapper::StopAnimation()
178 {
179 // Can be called for animated images during shutdown, after we've
180 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
181 if (!mViewer)
182 return;
184 nsIDocument* doc = mViewer->GetDocument();
185 if (doc) {
186 nsSMILAnimationController* controller = doc->GetAnimationController();
187 if (controller) {
188 controller->Pause(nsSMILTimeContainer::PAUSE_IMAGE);
189 }
190 doc->SetImagesNeedAnimating(false);
191 }
192 }
194 void
195 SVGDocumentWrapper::ResetAnimation()
196 {
197 SVGSVGElement* svgElem = GetRootSVGElem();
198 if (!svgElem)
199 return;
201 svgElem->SetCurrentTime(0.0f);
202 }
204 float
205 SVGDocumentWrapper::GetCurrentTime()
206 {
207 SVGSVGElement* svgElem = GetRootSVGElem();
208 return svgElem ? svgElem->GetCurrentTime()
209 : 0.0f;
210 }
212 void
213 SVGDocumentWrapper::SetCurrentTime(float aTime)
214 {
215 SVGSVGElement* svgElem = GetRootSVGElem();
216 if (svgElem && svgElem->GetCurrentTime() != aTime) {
217 svgElem->SetCurrentTime(aTime);
218 }
219 }
221 /** nsIStreamListener methods **/
223 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt,
224 in nsIInputStream inStr, in unsigned long sourceOffset,
225 in unsigned long count); */
226 NS_IMETHODIMP
227 SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
228 nsIInputStream* inStr,
229 uint64_t sourceOffset,
230 uint32_t count)
231 {
232 return mListener->OnDataAvailable(aRequest, ctxt, inStr,
233 sourceOffset, count);
234 }
236 /** nsIRequestObserver methods **/
238 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
239 NS_IMETHODIMP
240 SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
241 {
242 nsresult rv = SetupViewer(aRequest,
243 getter_AddRefs(mViewer),
244 getter_AddRefs(mLoadGroup));
246 if (NS_SUCCEEDED(rv) &&
247 NS_SUCCEEDED(mListener->OnStartRequest(aRequest, nullptr))) {
248 mViewer->GetDocument()->SetIsBeingUsedAsImage();
249 StopAnimation(); // otherwise animations start automatically in helper doc
251 rv = mViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
252 if (NS_SUCCEEDED(rv)) {
253 rv = mViewer->Open(nullptr, nullptr);
254 }
255 }
256 return rv;
257 }
260 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
261 in nsresult status); */
262 NS_IMETHODIMP
263 SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsISupports* ctxt,
264 nsresult status)
265 {
266 if (mListener) {
267 mListener->OnStopRequest(aRequest, ctxt, status);
268 mListener = nullptr;
269 }
271 return NS_OK;
272 }
274 /** nsIObserver Methods **/
275 NS_IMETHODIMP
276 SVGDocumentWrapper::Observe(nsISupports* aSubject,
277 const char* aTopic,
278 const char16_t *aData)
279 {
280 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
281 // Sever ties from rendering observers to helper-doc's root SVG node
282 SVGSVGElement* svgElem = GetRootSVGElem();
283 if (svgElem) {
284 nsSVGEffects::RemoveAllRenderingObservers(svgElem);
285 }
287 // Clean up at XPCOM shutdown time.
288 DestroyViewer();
289 if (mListener)
290 mListener = nullptr;
291 if (mLoadGroup)
292 mLoadGroup = nullptr;
294 // Turn off "registered" flag, or else we'll try to unregister when we die.
295 // (No need for that now, and the try would fail anyway -- it's too late.)
296 mRegisteredForXPCOMShutdown = false;
297 } else {
298 NS_ERROR("Unexpected observer topic.");
299 }
300 return NS_OK;
301 }
303 /** Private helper methods **/
305 // This method is largely cribbed from
306 // nsExternalResourceMap::PendingLoad::SetupViewer.
307 nsresult
308 SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
309 nsIContentViewer** aViewer,
310 nsILoadGroup** aLoadGroup)
311 {
312 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
313 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
315 // Check for HTTP error page
316 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
317 if (httpChannel) {
318 bool requestSucceeded;
319 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
320 !requestSucceeded) {
321 return NS_ERROR_FAILURE;
322 }
323 }
325 // Give this document its own loadgroup
326 nsCOMPtr<nsILoadGroup> loadGroup;
327 chan->GetLoadGroup(getter_AddRefs(loadGroup));
329 nsCOMPtr<nsILoadGroup> newLoadGroup =
330 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
331 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
332 newLoadGroup->SetLoadGroup(loadGroup);
334 nsCOMPtr<nsICategoryManager> catMan =
335 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
336 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
337 nsXPIDLCString contractId;
338 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML,
339 getter_Copies(contractId));
340 NS_ENSURE_SUCCESS(rv, rv);
341 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
342 do_GetService(contractId);
343 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
345 nsCOMPtr<nsIContentViewer> viewer;
346 nsCOMPtr<nsIStreamListener> listener;
347 rv = docLoaderFactory->CreateInstance("external-resource", chan,
348 newLoadGroup,
349 IMAGE_SVG_XML, nullptr, nullptr,
350 getter_AddRefs(listener),
351 getter_AddRefs(viewer));
352 NS_ENSURE_SUCCESS(rv, rv);
354 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
356 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
357 NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
359 // XML-only, because this is for SVG content
360 nsIContentSink* sink = parser->GetContentSink();
361 nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
362 NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED);
364 listener.swap(mListener);
365 viewer.forget(aViewer);
366 newLoadGroup.forget(aLoadGroup);
368 RegisterForXPCOMShutdown();
369 return NS_OK;
370 }
372 void
373 SVGDocumentWrapper::RegisterForXPCOMShutdown()
374 {
375 NS_ABORT_IF_FALSE(!mRegisteredForXPCOMShutdown,
376 "re-registering for XPCOM shutdown");
377 // Listen for xpcom-shutdown so that we can drop references to our
378 // helper-document at that point. (Otherwise, we won't get cleaned up
379 // until imgLoader::Shutdown, which can happen after the JAR service
380 // and RDF service have been unregistered.)
381 nsresult rv;
382 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
383 if (NS_FAILED(rv) ||
384 NS_FAILED(obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
385 true))) {
386 NS_WARNING("Failed to register as observer of XPCOM shutdown");
387 } else {
388 mRegisteredForXPCOMShutdown = true;
389 }
390 }
392 void
393 SVGDocumentWrapper::UnregisterForXPCOMShutdown()
394 {
395 NS_ABORT_IF_FALSE(mRegisteredForXPCOMShutdown,
396 "unregistering for XPCOM shutdown w/out being registered");
398 nsresult rv;
399 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
400 if (NS_FAILED(rv) ||
401 NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
402 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
403 } else {
404 mRegisteredForXPCOMShutdown = false;
405 }
406 }
408 void
409 SVGDocumentWrapper::FlushLayout()
410 {
411 nsCOMPtr<nsIPresShell> presShell;
412 mViewer->GetPresShell(getter_AddRefs(presShell));
413 if (presShell) {
414 presShell->FlushPendingNotifications(Flush_Layout);
415 }
416 }
418 nsIDocument*
419 SVGDocumentWrapper::GetDocument()
420 {
421 if (!mViewer)
422 return nullptr;
424 return mViewer->GetDocument(); // May be nullptr.
425 }
427 SVGSVGElement*
428 SVGDocumentWrapper::GetRootSVGElem()
429 {
430 if (!mViewer)
431 return nullptr; // Can happen during destruction
433 nsIDocument* doc = mViewer->GetDocument();
434 if (!doc)
435 return nullptr; // Can happen during destruction
437 Element* rootElem = mViewer->GetDocument()->GetRootElement();
438 if (!rootElem || !rootElem->IsSVG(nsGkAtoms::svg)) {
439 return nullptr;
440 }
442 return static_cast<SVGSVGElement*>(rootElem);
443 }
445 } // namespace image
446 } // namespace mozilla