Bumping manifests a=b2g-bump
[gecko.git] / chrome / nsChromeRegistry.cpp
blobfa96f22c4c12c826808c64c67dd3553abbfc242b
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 "mozilla/CSSStyleSheet.h"
20 #include "mozilla/dom/URL.h"
21 #include "nsIConsoleService.h"
22 #include "nsIDocument.h"
23 #include "nsIDOMDocument.h"
24 #include "nsIDOMLocation.h"
25 #include "nsIDOMWindowCollection.h"
26 #include "nsIDOMWindow.h"
27 #include "nsIObserverService.h"
28 #include "nsIPresShell.h"
29 #include "nsIScriptError.h"
30 #include "nsIWindowMediator.h"
32 nsChromeRegistry* nsChromeRegistry::gChromeRegistry;
34 // DO NOT use namespace mozilla; it'll break due to a naming conflict between
35 // mozilla::TextRange and a TextRange in OSX headers.
36 using mozilla::CSSStyleSheet;
37 using mozilla::dom::IsChromeURI;
39 ////////////////////////////////////////////////////////////////////////////////
41 void
42 nsChromeRegistry::LogMessage(const char* aMsg, ...)
44 nsCOMPtr<nsIConsoleService> console
45 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
46 if (!console)
47 return;
49 va_list args;
50 va_start(args, aMsg);
51 char* formatted = PR_vsmprintf(aMsg, args);
52 va_end(args);
53 if (!formatted)
54 return;
56 console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted).get());
57 PR_smprintf_free(formatted);
60 void
61 nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber, uint32_t flags,
62 const char* aMsg, ...)
64 nsresult rv;
66 nsCOMPtr<nsIConsoleService> console
67 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
69 nsCOMPtr<nsIScriptError> error
70 (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
71 if (!console || !error)
72 return;
74 va_list args;
75 va_start(args, aMsg);
76 char* formatted = PR_vsmprintf(aMsg, args);
77 va_end(args);
78 if (!formatted)
79 return;
81 nsCString spec;
82 if (aURL)
83 aURL->GetSpec(spec);
85 rv = error->Init(NS_ConvertUTF8toUTF16(formatted),
86 NS_ConvertUTF8toUTF16(spec),
87 EmptyString(),
88 aLineNumber, 0, flags, "chrome registration");
89 PR_smprintf_free(formatted);
91 if (NS_FAILED(rv))
92 return;
94 console->LogMessage(error);
97 nsChromeRegistry::~nsChromeRegistry()
99 gChromeRegistry = nullptr;
102 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry)
103 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry)
104 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry)
105 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry)
106 #ifdef MOZ_XUL
107 NS_INTERFACE_MAP_ENTRY(nsIXULOverlayProvider)
108 #endif
109 NS_INTERFACE_MAP_ENTRY(nsIObserver)
110 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
111 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChromeRegistry)
112 NS_INTERFACE_MAP_END
114 NS_IMPL_ADDREF(nsChromeRegistry)
115 NS_IMPL_RELEASE(nsChromeRegistry)
117 ////////////////////////////////////////////////////////////////////////////////
118 // nsIChromeRegistry methods:
120 already_AddRefed<nsIChromeRegistry>
121 nsChromeRegistry::GetService()
123 if (!gChromeRegistry)
125 // We don't actually want this ref, we just want the service to
126 // initialize if it hasn't already.
127 nsCOMPtr<nsIChromeRegistry> reg(
128 do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
129 if (!gChromeRegistry)
130 return nullptr;
132 nsCOMPtr<nsIChromeRegistry> registry = gChromeRegistry;
133 return registry.forget();
136 nsresult
137 nsChromeRegistry::Init()
139 // This initialization process is fairly complicated and may cause reentrant
140 // getservice calls to resolve chrome URIs (especially locale files). We
141 // don't want that, so we inform the protocol handler about our existence
142 // before we are actually fully initialized.
143 gChromeRegistry = this;
145 mInitialized = true;
147 return NS_OK;
150 nsresult
151 nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL,
152 nsACString& aProvider, nsACString& aPath)
154 nsresult rv;
156 #ifdef DEBUG
157 bool isChrome;
158 aChromeURL->SchemeIs("chrome", &isChrome);
159 NS_ASSERTION(isChrome, "Non-chrome URI?");
160 #endif
162 nsAutoCString path;
163 rv = aChromeURL->GetPath(path);
164 NS_ENSURE_SUCCESS(rv, rv);
166 if (path.Length() < 3) {
167 LogMessage("Invalid chrome URI: %s", path.get());
168 return NS_ERROR_FAILURE;
171 path.SetLength(nsUnescapeCount(path.BeginWriting()));
172 NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!");
174 int32_t slash = path.FindChar('/', 1);
175 if (slash == 1) {
176 LogMessage("Invalid chrome URI: %s", path.get());
177 return NS_ERROR_FAILURE;
180 if (slash == -1) {
181 aPath.Truncate();
183 else {
184 if (slash == (int32_t) path.Length() - 1)
185 aPath.Truncate();
186 else
187 aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1);
189 --slash;
192 aProvider.Assign(path.get() + 1, slash);
193 return NS_OK;
197 nsresult
198 nsChromeRegistry::Canonify(nsIURL* aChromeURL)
200 NS_NAMED_LITERAL_CSTRING(kSlash, "/");
202 nsresult rv;
204 nsAutoCString provider, path;
205 rv = GetProviderAndPath(aChromeURL, provider, path);
206 NS_ENSURE_SUCCESS(rv, rv);
208 if (path.IsEmpty()) {
209 nsAutoCString package;
210 rv = aChromeURL->GetHost(package);
211 NS_ENSURE_SUCCESS(rv, rv);
213 // we re-use the "path" local string to build a new URL path
214 path.Assign(kSlash + provider + kSlash + package);
215 if (provider.EqualsLiteral("content")) {
216 path.AppendLiteral(".xul");
218 else if (provider.EqualsLiteral("locale")) {
219 path.AppendLiteral(".dtd");
221 else if (provider.EqualsLiteral("skin")) {
222 path.AppendLiteral(".css");
224 else {
225 return NS_ERROR_INVALID_ARG;
227 aChromeURL->SetPath(path);
229 else {
230 // prevent directory traversals ("..")
231 // path is already unescaped once, but uris can get unescaped twice
232 const char* pos = path.BeginReading();
233 const char* end = path.EndReading();
234 while (pos < end) {
235 switch (*pos) {
236 case ':':
237 return NS_ERROR_DOM_BAD_URI;
238 case '.':
239 if (pos[1] == '.')
240 return NS_ERROR_DOM_BAD_URI;
241 break;
242 case '%':
243 // chrome: URIs with double-escapes are trying to trick us.
244 // watch for %2e, and %25 in case someone triple unescapes
245 if (pos[1] == '2' &&
246 ( pos[2] == 'e' || pos[2] == 'E' ||
247 pos[2] == '5' ))
248 return NS_ERROR_DOM_BAD_URI;
249 break;
250 case '?':
251 case '#':
252 pos = end;
253 continue;
255 ++pos;
259 return NS_OK;
262 NS_IMETHODIMP
263 nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult)
265 nsresult rv;
266 NS_ASSERTION(aChromeURI, "null url!");
268 if (mOverrideTable.Get(aChromeURI, aResult))
269 return NS_OK;
271 nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aChromeURI));
272 NS_ENSURE_TRUE(chromeURL, NS_NOINTERFACE);
274 nsAutoCString package, provider, path;
275 rv = chromeURL->GetHostPort(package);
276 NS_ENSURE_SUCCESS(rv, rv);
278 rv = GetProviderAndPath(chromeURL, provider, path);
279 NS_ENSURE_SUCCESS(rv, rv);
281 nsIURI* baseURI = GetBaseURIFromPackage(package, provider, path);
283 uint32_t flags;
284 rv = GetFlagsFromPackage(package, &flags);
285 if (NS_FAILED(rv))
286 return rv;
288 if (flags & PLATFORM_PACKAGE) {
289 #if defined(XP_WIN)
290 path.Insert("win/", 0);
291 #elif defined(XP_MACOSX)
292 path.Insert("mac/", 0);
293 #else
294 path.Insert("unix/", 0);
295 #endif
298 if (!baseURI) {
299 LogMessage("No chrome package registered for chrome://%s/%s/%s",
300 package.get(), provider.get(), path.get());
301 return NS_ERROR_FILE_NOT_FOUND;
304 return NS_NewURI(aResult, path, nullptr, baseURI);
307 ////////////////////////////////////////////////////////////////////////
309 // theme stuff
312 static void FlushSkinBindingsForWindow(nsIDOMWindow* aWindow)
314 // Get the DOM document.
315 nsCOMPtr<nsIDOMDocument> domDocument;
316 aWindow->GetDocument(getter_AddRefs(domDocument));
317 if (!domDocument)
318 return;
320 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
321 if (!document)
322 return;
324 // Annihilate all XBL bindings.
325 document->FlushSkinBindings();
328 // XXXbsmedberg: move this to nsIWindowMediator
329 NS_IMETHODIMP nsChromeRegistry::RefreshSkins()
331 nsCOMPtr<nsIWindowMediator> windowMediator
332 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
333 if (!windowMediator)
334 return NS_OK;
336 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
337 windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
338 bool more;
339 windowEnumerator->HasMoreElements(&more);
340 while (more) {
341 nsCOMPtr<nsISupports> protoWindow;
342 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
343 if (protoWindow) {
344 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(protoWindow);
345 if (domWindow)
346 FlushSkinBindingsForWindow(domWindow);
348 windowEnumerator->HasMoreElements(&more);
351 FlushSkinCaches();
353 windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
354 windowEnumerator->HasMoreElements(&more);
355 while (more) {
356 nsCOMPtr<nsISupports> protoWindow;
357 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
358 if (protoWindow) {
359 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(protoWindow);
360 if (domWindow)
361 RefreshWindow(domWindow);
363 windowEnumerator->HasMoreElements(&more);
366 return NS_OK;
369 void
370 nsChromeRegistry::FlushSkinCaches()
372 nsCOMPtr<nsIObserverService> obsSvc =
373 mozilla::services::GetObserverService();
374 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
376 obsSvc->NotifyObservers(static_cast<nsIChromeRegistry*>(this),
377 NS_CHROME_FLUSH_SKINS_TOPIC, nullptr);
380 // XXXbsmedberg: move this to windowmediator
381 nsresult nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow)
383 // Deal with our subframes first.
384 nsCOMPtr<nsIDOMWindowCollection> frames;
385 aWindow->GetFrames(getter_AddRefs(frames));
386 uint32_t length;
387 frames->GetLength(&length);
388 uint32_t j;
389 for (j = 0; j < length; j++) {
390 nsCOMPtr<nsIDOMWindow> childWin;
391 frames->Item(j, getter_AddRefs(childWin));
392 RefreshWindow(childWin);
395 nsresult rv;
396 // Get the DOM document.
397 nsCOMPtr<nsIDOMDocument> domDocument;
398 aWindow->GetDocument(getter_AddRefs(domDocument));
399 if (!domDocument)
400 return NS_OK;
402 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
403 if (!document)
404 return NS_OK;
406 // Deal with the agent sheets first. Have to do all the style sets by hand.
407 nsCOMPtr<nsIPresShell> shell = document->GetShell();
408 if (shell) {
409 // Reload only the chrome URL agent style sheets.
410 nsCOMArray<nsIStyleSheet> agentSheets;
411 rv = shell->GetAgentStyleSheets(agentSheets);
412 NS_ENSURE_SUCCESS(rv, rv);
414 nsCOMArray<nsIStyleSheet> newAgentSheets;
415 for (int32_t l = 0; l < agentSheets.Count(); ++l) {
416 nsIStyleSheet *sheet = agentSheets[l];
418 nsIURI* uri = sheet->GetSheetURI();
420 if (IsChromeURI(uri)) {
421 // Reload the sheet.
422 nsRefPtr<CSSStyleSheet> newSheet;
423 rv = document->LoadChromeSheetSync(uri, true,
424 getter_AddRefs(newSheet));
425 if (NS_FAILED(rv)) return rv;
426 if (newSheet) {
427 rv = newAgentSheets.AppendObject(newSheet) ? NS_OK : NS_ERROR_FAILURE;
428 if (NS_FAILED(rv)) return rv;
431 else { // Just use the same sheet.
432 rv = newAgentSheets.AppendObject(sheet) ? NS_OK : NS_ERROR_FAILURE;
433 if (NS_FAILED(rv)) return rv;
437 rv = shell->SetAgentStyleSheets(newAgentSheets);
438 NS_ENSURE_SUCCESS(rv, rv);
441 // Build an array of nsIURIs of style sheets we need to load.
442 nsCOMArray<nsIStyleSheet> oldSheets;
443 nsCOMArray<nsIStyleSheet> newSheets;
445 int32_t count = document->GetNumberOfStyleSheets();
447 // Iterate over the style sheets.
448 int32_t i;
449 for (i = 0; i < count; i++) {
450 // Get the style sheet
451 nsIStyleSheet *styleSheet = document->GetStyleSheetAt(i);
453 if (!oldSheets.AppendObject(styleSheet)) {
454 return NS_ERROR_OUT_OF_MEMORY;
458 // Iterate over our old sheets and kick off a sync load of the new
459 // sheet if and only if it's a chrome URL.
460 for (i = 0; i < count; i++) {
461 nsRefPtr<CSSStyleSheet> sheet = do_QueryObject(oldSheets[i]);
462 nsIURI* uri = sheet ? sheet->GetOriginalURI() : nullptr;
464 if (uri && IsChromeURI(uri)) {
465 // Reload the sheet.
466 nsRefPtr<CSSStyleSheet> newSheet;
467 // XXX what about chrome sheets that have a title or are disabled? This
468 // only works by sheer dumb luck.
469 document->LoadChromeSheetSync(uri, false, getter_AddRefs(newSheet));
470 // Even if it's null, we put in in there.
471 newSheets.AppendObject(newSheet);
473 else {
474 // Just use the same sheet.
475 newSheets.AppendObject(sheet);
479 // Now notify the document that multiple sheets have been added and removed.
480 document->UpdateStyleSheets(oldSheets, newSheets);
481 return NS_OK;
484 void
485 nsChromeRegistry::FlushAllCaches()
487 nsCOMPtr<nsIObserverService> obsSvc =
488 mozilla::services::GetObserverService();
489 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
491 obsSvc->NotifyObservers((nsIChromeRegistry*) this,
492 NS_CHROME_FLUSH_TOPIC, nullptr);
495 // xxxbsmedberg Move me to nsIWindowMediator
496 NS_IMETHODIMP
497 nsChromeRegistry::ReloadChrome()
499 UpdateSelectedLocale();
500 FlushAllCaches();
501 // Do a reload of all top level windows.
502 nsresult rv = NS_OK;
504 // Get the window mediator
505 nsCOMPtr<nsIWindowMediator> windowMediator
506 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
507 if (windowMediator) {
508 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
510 rv = windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
511 if (NS_SUCCEEDED(rv)) {
512 // Get each dom window
513 bool more;
514 rv = windowEnumerator->HasMoreElements(&more);
515 if (NS_FAILED(rv)) return rv;
516 while (more) {
517 nsCOMPtr<nsISupports> protoWindow;
518 rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow));
519 if (NS_SUCCEEDED(rv)) {
520 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(protoWindow);
521 if (domWindow) {
522 nsCOMPtr<nsIDOMLocation> location;
523 domWindow->GetLocation(getter_AddRefs(location));
524 if (location) {
525 rv = location->Reload(false);
526 if (NS_FAILED(rv)) return rv;
530 rv = windowEnumerator->HasMoreElements(&more);
531 if (NS_FAILED(rv)) return rv;
535 return rv;
538 NS_IMETHODIMP
539 nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, bool *aResult)
541 nsresult rv;
542 *aResult = false;
544 #ifdef DEBUG
545 bool isChrome;
546 aChromeURI->SchemeIs("chrome", &isChrome);
547 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!");
548 #endif
550 nsCOMPtr<nsIURL> url (do_QueryInterface(aChromeURI));
551 NS_ENSURE_TRUE(url, NS_NOINTERFACE);
553 nsAutoCString provider, file;
554 rv = GetProviderAndPath(url, provider, file);
555 NS_ENSURE_SUCCESS(rv, rv);
557 if (!provider.EqualsLiteral("skin"))
558 *aResult = true;
560 return NS_OK;
563 NS_IMETHODIMP
564 nsChromeRegistry::AllowContentToAccess(nsIURI *aURI, bool *aResult)
566 nsresult rv;
568 *aResult = false;
570 #ifdef DEBUG
571 bool isChrome;
572 aURI->SchemeIs("chrome", &isChrome);
573 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowContentToAccess!");
574 #endif
576 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
577 if (!url) {
578 NS_ERROR("Chrome URL doesn't implement nsIURL.");
579 return NS_ERROR_UNEXPECTED;
582 nsAutoCString package;
583 rv = url->GetHostPort(package);
584 NS_ENSURE_SUCCESS(rv, rv);
586 uint32_t flags;
587 rv = GetFlagsFromPackage(package, &flags);
589 if (NS_SUCCEEDED(rv)) {
590 *aResult = !!(flags & CONTENT_ACCESSIBLE);
592 return NS_OK;
595 NS_IMETHODIMP_(bool)
596 nsChromeRegistry::WrappersEnabled(nsIURI *aURI)
598 nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aURI));
599 if (!chromeURL)
600 return false;
602 bool isChrome = false;
603 nsresult rv = chromeURL->SchemeIs("chrome", &isChrome);
604 if (NS_FAILED(rv) || !isChrome)
605 return false;
607 nsAutoCString package;
608 rv = chromeURL->GetHostPort(package);
609 if (NS_FAILED(rv))
610 return false;
612 uint32_t flags;
613 rv = GetFlagsFromPackage(package, &flags);
614 return NS_SUCCEEDED(rv) && (flags & XPCNATIVEWRAPPERS);
617 already_AddRefed<nsChromeRegistry>
618 nsChromeRegistry::GetSingleton()
620 if (gChromeRegistry) {
621 nsRefPtr<nsChromeRegistry> registry = gChromeRegistry;
622 return registry.forget();
625 nsRefPtr<nsChromeRegistry> cr;
626 if (GeckoProcessType_Content == XRE_GetProcessType())
627 cr = new nsChromeRegistryContent();
628 else
629 cr = new nsChromeRegistryChrome();
631 if (NS_FAILED(cr->Init()))
632 return nullptr;
634 return cr.forget();