Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
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: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsChromeRegistry.h"
8 #include "nsChromeRegistryChrome.h"
9 #include "nsChromeRegistryContent.h"
11 #include "prprf.h"
13 #include "nsCOMPtr.h"
14 #include "nsError.h"
15 #include "nsEscape.h"
16 #include "nsNetUtil.h"
17 #include "nsString.h"
19 #include "nsCSSStyleSheet.h"
20 #include "nsIConsoleService.h"
21 #include "nsIDocument.h"
22 #include "nsIDOMDocument.h"
23 #include "nsIDOMLocation.h"
24 #include "nsIDOMWindowCollection.h"
25 #include "nsIDOMWindow.h"
26 #include "nsIObserverService.h"
27 #include "nsIPresShell.h"
28 #include "nsIScriptError.h"
29 #include "nsIWindowMediator.h"
30 #include "mozilla/dom/URL.h"
32 nsChromeRegistry* nsChromeRegistry::gChromeRegistry;
33 using mozilla::dom::IsChromeURI;
35 ////////////////////////////////////////////////////////////////////////////////
37 void
38 nsChromeRegistry::LogMessage(const char* aMsg, ...)
39 {
40 nsCOMPtr<nsIConsoleService> console
41 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
42 if (!console)
43 return;
45 va_list args;
46 va_start(args, aMsg);
47 char* formatted = PR_vsmprintf(aMsg, args);
48 va_end(args);
49 if (!formatted)
50 return;
52 console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted).get());
53 PR_smprintf_free(formatted);
54 }
56 void
57 nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber, uint32_t flags,
58 const char* aMsg, ...)
59 {
60 nsresult rv;
62 nsCOMPtr<nsIConsoleService> console
63 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
65 nsCOMPtr<nsIScriptError> error
66 (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
67 if (!console || !error)
68 return;
70 va_list args;
71 va_start(args, aMsg);
72 char* formatted = PR_vsmprintf(aMsg, args);
73 va_end(args);
74 if (!formatted)
75 return;
77 nsCString spec;
78 if (aURL)
79 aURL->GetSpec(spec);
81 rv = error->Init(NS_ConvertUTF8toUTF16(formatted),
82 NS_ConvertUTF8toUTF16(spec),
83 EmptyString(),
84 aLineNumber, 0, flags, "chrome registration");
85 PR_smprintf_free(formatted);
87 if (NS_FAILED(rv))
88 return;
90 console->LogMessage(error);
91 }
93 nsChromeRegistry::~nsChromeRegistry()
94 {
95 gChromeRegistry = nullptr;
96 }
98 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry)
99 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry)
100 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry)
101 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry)
102 #ifdef MOZ_XUL
103 NS_INTERFACE_MAP_ENTRY(nsIXULOverlayProvider)
104 #endif
105 NS_INTERFACE_MAP_ENTRY(nsIObserver)
106 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
107 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChromeRegistry)
108 NS_INTERFACE_MAP_END
110 NS_IMPL_ADDREF(nsChromeRegistry)
111 NS_IMPL_RELEASE(nsChromeRegistry)
113 ////////////////////////////////////////////////////////////////////////////////
114 // nsIChromeRegistry methods:
116 already_AddRefed<nsIChromeRegistry>
117 nsChromeRegistry::GetService()
118 {
119 if (!gChromeRegistry)
120 {
121 // We don't actually want this ref, we just want the service to
122 // initialize if it hasn't already.
123 nsCOMPtr<nsIChromeRegistry> reg(
124 do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
125 if (!gChromeRegistry)
126 return nullptr;
127 }
128 nsCOMPtr<nsIChromeRegistry> registry = gChromeRegistry;
129 return registry.forget();
130 }
132 nsresult
133 nsChromeRegistry::Init()
134 {
135 // This initialization process is fairly complicated and may cause reentrant
136 // getservice calls to resolve chrome URIs (especially locale files). We
137 // don't want that, so we inform the protocol handler about our existence
138 // before we are actually fully initialized.
139 gChromeRegistry = this;
141 mInitialized = true;
143 return NS_OK;
144 }
146 nsresult
147 nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL,
148 nsACString& aProvider, nsACString& aPath)
149 {
150 nsresult rv;
152 #ifdef DEBUG
153 bool isChrome;
154 aChromeURL->SchemeIs("chrome", &isChrome);
155 NS_ASSERTION(isChrome, "Non-chrome URI?");
156 #endif
158 nsAutoCString path;
159 rv = aChromeURL->GetPath(path);
160 NS_ENSURE_SUCCESS(rv, rv);
162 if (path.Length() < 3) {
163 LogMessage("Invalid chrome URI: %s", path.get());
164 return NS_ERROR_FAILURE;
165 }
167 path.SetLength(nsUnescapeCount(path.BeginWriting()));
168 NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!");
170 int32_t slash = path.FindChar('/', 1);
171 if (slash == 1) {
172 LogMessage("Invalid chrome URI: %s", path.get());
173 return NS_ERROR_FAILURE;
174 }
176 if (slash == -1) {
177 aPath.Truncate();
178 }
179 else {
180 if (slash == (int32_t) path.Length() - 1)
181 aPath.Truncate();
182 else
183 aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1);
185 --slash;
186 }
188 aProvider.Assign(path.get() + 1, slash);
189 return NS_OK;
190 }
193 nsresult
194 nsChromeRegistry::Canonify(nsIURL* aChromeURL)
195 {
196 NS_NAMED_LITERAL_CSTRING(kSlash, "/");
198 nsresult rv;
200 nsAutoCString provider, path;
201 rv = GetProviderAndPath(aChromeURL, provider, path);
202 NS_ENSURE_SUCCESS(rv, rv);
204 if (path.IsEmpty()) {
205 nsAutoCString package;
206 rv = aChromeURL->GetHost(package);
207 NS_ENSURE_SUCCESS(rv, rv);
209 // we re-use the "path" local string to build a new URL path
210 path.Assign(kSlash + provider + kSlash + package);
211 if (provider.EqualsLiteral("content")) {
212 path.AppendLiteral(".xul");
213 }
214 else if (provider.EqualsLiteral("locale")) {
215 path.AppendLiteral(".dtd");
216 }
217 else if (provider.EqualsLiteral("skin")) {
218 path.AppendLiteral(".css");
219 }
220 else {
221 return NS_ERROR_INVALID_ARG;
222 }
223 aChromeURL->SetPath(path);
224 }
225 else {
226 // prevent directory traversals ("..")
227 // path is already unescaped once, but uris can get unescaped twice
228 const char* pos = path.BeginReading();
229 const char* end = path.EndReading();
230 while (pos < end) {
231 switch (*pos) {
232 case ':':
233 return NS_ERROR_DOM_BAD_URI;
234 case '.':
235 if (pos[1] == '.')
236 return NS_ERROR_DOM_BAD_URI;
237 break;
238 case '%':
239 // chrome: URIs with double-escapes are trying to trick us.
240 // watch for %2e, and %25 in case someone triple unescapes
241 if (pos[1] == '2' &&
242 ( pos[2] == 'e' || pos[2] == 'E' ||
243 pos[2] == '5' ))
244 return NS_ERROR_DOM_BAD_URI;
245 break;
246 case '?':
247 case '#':
248 pos = end;
249 continue;
250 }
251 ++pos;
252 }
253 }
255 return NS_OK;
256 }
258 NS_IMETHODIMP
259 nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult)
260 {
261 nsresult rv;
262 NS_ASSERTION(aChromeURI, "null url!");
264 if (mOverrideTable.Get(aChromeURI, aResult))
265 return NS_OK;
267 nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aChromeURI));
268 NS_ENSURE_TRUE(chromeURL, NS_NOINTERFACE);
270 nsAutoCString package, provider, path;
271 rv = chromeURL->GetHostPort(package);
272 NS_ENSURE_SUCCESS(rv, rv);
274 rv = GetProviderAndPath(chromeURL, provider, path);
275 NS_ENSURE_SUCCESS(rv, rv);
277 nsIURI* baseURI = GetBaseURIFromPackage(package, provider, path);
279 uint32_t flags;
280 rv = GetFlagsFromPackage(package, &flags);
281 if (NS_FAILED(rv))
282 return rv;
284 if (flags & PLATFORM_PACKAGE) {
285 #if defined(XP_WIN)
286 path.Insert("win/", 0);
287 #elif defined(XP_MACOSX)
288 path.Insert("mac/", 0);
289 #else
290 path.Insert("unix/", 0);
291 #endif
292 }
294 if (!baseURI) {
295 LogMessage("No chrome package registered for chrome://%s/%s/%s",
296 package.get(), provider.get(), path.get());
297 return NS_ERROR_FAILURE;
298 }
300 return NS_NewURI(aResult, path, nullptr, baseURI);
301 }
303 ////////////////////////////////////////////////////////////////////////
305 // theme stuff
308 static void FlushSkinBindingsForWindow(nsIDOMWindow* aWindow)
309 {
310 // Get the DOM document.
311 nsCOMPtr<nsIDOMDocument> domDocument;
312 aWindow->GetDocument(getter_AddRefs(domDocument));
313 if (!domDocument)
314 return;
316 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
317 if (!document)
318 return;
320 // Annihilate all XBL bindings.
321 document->FlushSkinBindings();
322 }
324 // XXXbsmedberg: move this to nsIWindowMediator
325 NS_IMETHODIMP nsChromeRegistry::RefreshSkins()
326 {
327 nsCOMPtr<nsIWindowMediator> windowMediator
328 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
329 if (!windowMediator)
330 return NS_OK;
332 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
333 windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
334 bool more;
335 windowEnumerator->HasMoreElements(&more);
336 while (more) {
337 nsCOMPtr<nsISupports> protoWindow;
338 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
339 if (protoWindow) {
340 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(protoWindow);
341 if (domWindow)
342 FlushSkinBindingsForWindow(domWindow);
343 }
344 windowEnumerator->HasMoreElements(&more);
345 }
347 FlushSkinCaches();
349 windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
350 windowEnumerator->HasMoreElements(&more);
351 while (more) {
352 nsCOMPtr<nsISupports> protoWindow;
353 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
354 if (protoWindow) {
355 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(protoWindow);
356 if (domWindow)
357 RefreshWindow(domWindow);
358 }
359 windowEnumerator->HasMoreElements(&more);
360 }
362 return NS_OK;
363 }
365 void
366 nsChromeRegistry::FlushSkinCaches()
367 {
368 nsCOMPtr<nsIObserverService> obsSvc =
369 mozilla::services::GetObserverService();
370 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
372 obsSvc->NotifyObservers(static_cast<nsIChromeRegistry*>(this),
373 NS_CHROME_FLUSH_SKINS_TOPIC, nullptr);
374 }
376 // XXXbsmedberg: move this to windowmediator
377 nsresult nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow)
378 {
379 // Deal with our subframes first.
380 nsCOMPtr<nsIDOMWindowCollection> frames;
381 aWindow->GetFrames(getter_AddRefs(frames));
382 uint32_t length;
383 frames->GetLength(&length);
384 uint32_t j;
385 for (j = 0; j < length; j++) {
386 nsCOMPtr<nsIDOMWindow> childWin;
387 frames->Item(j, getter_AddRefs(childWin));
388 RefreshWindow(childWin);
389 }
391 nsresult rv;
392 // Get the DOM document.
393 nsCOMPtr<nsIDOMDocument> domDocument;
394 aWindow->GetDocument(getter_AddRefs(domDocument));
395 if (!domDocument)
396 return NS_OK;
398 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
399 if (!document)
400 return NS_OK;
402 // Deal with the agent sheets first. Have to do all the style sets by hand.
403 nsCOMPtr<nsIPresShell> shell = document->GetShell();
404 if (shell) {
405 // Reload only the chrome URL agent style sheets.
406 nsCOMArray<nsIStyleSheet> agentSheets;
407 rv = shell->GetAgentStyleSheets(agentSheets);
408 NS_ENSURE_SUCCESS(rv, rv);
410 nsCOMArray<nsIStyleSheet> newAgentSheets;
411 for (int32_t l = 0; l < agentSheets.Count(); ++l) {
412 nsIStyleSheet *sheet = agentSheets[l];
414 nsIURI* uri = sheet->GetSheetURI();
416 if (IsChromeURI(uri)) {
417 // Reload the sheet.
418 nsRefPtr<nsCSSStyleSheet> newSheet;
419 rv = document->LoadChromeSheetSync(uri, true,
420 getter_AddRefs(newSheet));
421 if (NS_FAILED(rv)) return rv;
422 if (newSheet) {
423 rv = newAgentSheets.AppendObject(newSheet) ? NS_OK : NS_ERROR_FAILURE;
424 if (NS_FAILED(rv)) return rv;
425 }
426 }
427 else { // Just use the same sheet.
428 rv = newAgentSheets.AppendObject(sheet) ? NS_OK : NS_ERROR_FAILURE;
429 if (NS_FAILED(rv)) return rv;
430 }
431 }
433 rv = shell->SetAgentStyleSheets(newAgentSheets);
434 NS_ENSURE_SUCCESS(rv, rv);
435 }
437 // Build an array of nsIURIs of style sheets we need to load.
438 nsCOMArray<nsIStyleSheet> oldSheets;
439 nsCOMArray<nsIStyleSheet> newSheets;
441 int32_t count = document->GetNumberOfStyleSheets();
443 // Iterate over the style sheets.
444 int32_t i;
445 for (i = 0; i < count; i++) {
446 // Get the style sheet
447 nsIStyleSheet *styleSheet = document->GetStyleSheetAt(i);
449 if (!oldSheets.AppendObject(styleSheet)) {
450 return NS_ERROR_OUT_OF_MEMORY;
451 }
452 }
454 // Iterate over our old sheets and kick off a sync load of the new
455 // sheet if and only if it's a chrome URL.
456 for (i = 0; i < count; i++) {
457 nsRefPtr<nsCSSStyleSheet> sheet = do_QueryObject(oldSheets[i]);
458 nsIURI* uri = sheet ? sheet->GetOriginalURI() : nullptr;
460 if (uri && IsChromeURI(uri)) {
461 // Reload the sheet.
462 nsRefPtr<nsCSSStyleSheet> newSheet;
463 // XXX what about chrome sheets that have a title or are disabled? This
464 // only works by sheer dumb luck.
465 document->LoadChromeSheetSync(uri, false, getter_AddRefs(newSheet));
466 // Even if it's null, we put in in there.
467 newSheets.AppendObject(newSheet);
468 }
469 else {
470 // Just use the same sheet.
471 newSheets.AppendObject(sheet);
472 }
473 }
475 // Now notify the document that multiple sheets have been added and removed.
476 document->UpdateStyleSheets(oldSheets, newSheets);
477 return NS_OK;
478 }
480 void
481 nsChromeRegistry::FlushAllCaches()
482 {
483 nsCOMPtr<nsIObserverService> obsSvc =
484 mozilla::services::GetObserverService();
485 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
487 obsSvc->NotifyObservers((nsIChromeRegistry*) this,
488 NS_CHROME_FLUSH_TOPIC, nullptr);
489 }
491 // xxxbsmedberg Move me to nsIWindowMediator
492 NS_IMETHODIMP
493 nsChromeRegistry::ReloadChrome()
494 {
495 UpdateSelectedLocale();
496 FlushAllCaches();
497 // Do a reload of all top level windows.
498 nsresult rv = NS_OK;
500 // Get the window mediator
501 nsCOMPtr<nsIWindowMediator> windowMediator
502 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
503 if (windowMediator) {
504 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
506 rv = windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
507 if (NS_SUCCEEDED(rv)) {
508 // Get each dom window
509 bool more;
510 rv = windowEnumerator->HasMoreElements(&more);
511 if (NS_FAILED(rv)) return rv;
512 while (more) {
513 nsCOMPtr<nsISupports> protoWindow;
514 rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow));
515 if (NS_SUCCEEDED(rv)) {
516 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(protoWindow);
517 if (domWindow) {
518 nsCOMPtr<nsIDOMLocation> location;
519 domWindow->GetLocation(getter_AddRefs(location));
520 if (location) {
521 rv = location->Reload(false);
522 if (NS_FAILED(rv)) return rv;
523 }
524 }
525 }
526 rv = windowEnumerator->HasMoreElements(&more);
527 if (NS_FAILED(rv)) return rv;
528 }
529 }
530 }
531 return rv;
532 }
534 NS_IMETHODIMP
535 nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, bool *aResult)
536 {
537 nsresult rv;
538 *aResult = false;
540 #ifdef DEBUG
541 bool isChrome;
542 aChromeURI->SchemeIs("chrome", &isChrome);
543 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!");
544 #endif
546 nsCOMPtr<nsIURL> url (do_QueryInterface(aChromeURI));
547 NS_ENSURE_TRUE(url, NS_NOINTERFACE);
549 nsAutoCString provider, file;
550 rv = GetProviderAndPath(url, provider, file);
551 NS_ENSURE_SUCCESS(rv, rv);
553 if (!provider.EqualsLiteral("skin"))
554 *aResult = true;
556 return NS_OK;
557 }
559 NS_IMETHODIMP
560 nsChromeRegistry::AllowContentToAccess(nsIURI *aURI, bool *aResult)
561 {
562 nsresult rv;
564 *aResult = false;
566 #ifdef DEBUG
567 bool isChrome;
568 aURI->SchemeIs("chrome", &isChrome);
569 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowContentToAccess!");
570 #endif
572 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
573 if (!url) {
574 NS_ERROR("Chrome URL doesn't implement nsIURL.");
575 return NS_ERROR_UNEXPECTED;
576 }
578 nsAutoCString package;
579 rv = url->GetHostPort(package);
580 NS_ENSURE_SUCCESS(rv, rv);
582 uint32_t flags;
583 rv = GetFlagsFromPackage(package, &flags);
585 if (NS_SUCCEEDED(rv)) {
586 *aResult = !!(flags & CONTENT_ACCESSIBLE);
587 }
588 return NS_OK;
589 }
591 NS_IMETHODIMP_(bool)
592 nsChromeRegistry::WrappersEnabled(nsIURI *aURI)
593 {
594 nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aURI));
595 if (!chromeURL)
596 return false;
598 bool isChrome = false;
599 nsresult rv = chromeURL->SchemeIs("chrome", &isChrome);
600 if (NS_FAILED(rv) || !isChrome)
601 return false;
603 nsAutoCString package;
604 rv = chromeURL->GetHostPort(package);
605 if (NS_FAILED(rv))
606 return false;
608 uint32_t flags;
609 rv = GetFlagsFromPackage(package, &flags);
610 return NS_SUCCEEDED(rv) && (flags & XPCNATIVEWRAPPERS);
611 }
613 already_AddRefed<nsChromeRegistry>
614 nsChromeRegistry::GetSingleton()
615 {
616 if (gChromeRegistry) {
617 nsRefPtr<nsChromeRegistry> registry = gChromeRegistry;
618 return registry.forget();
619 }
621 nsRefPtr<nsChromeRegistry> cr;
622 if (GeckoProcessType_Content == XRE_GetProcessType())
623 cr = new nsChromeRegistryContent();
624 else
625 cr = new nsChromeRegistryChrome();
627 if (NS_FAILED(cr->Init()))
628 return nullptr;
630 return cr.forget();
631 }