Bug 1543288 - Update the Print icon and Add rules button position. r=mtigley
[gecko.git] / chrome / nsChromeRegistry.cpp
blob0eb645cbaab3ec05147713971458c675a5902afd
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 "nsCOMPtr.h"
12 #include "nsError.h"
13 #include "nsEscape.h"
14 #include "nsNetUtil.h"
15 #include "nsString.h"
16 #include "nsQueryObject.h"
18 #include "mozilla/dom/URL.h"
19 #include "nsDOMWindowList.h"
20 #include "nsIConsoleService.h"
21 #include "mozilla/dom/Document.h"
22 #include "nsIDOMWindow.h"
23 #include "nsIObserverService.h"
24 #include "nsIScriptError.h"
25 #include "nsIWindowMediator.h"
26 #include "nsIPrefService.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/PresShell.h"
29 #include "mozilla/Printf.h"
30 #include "mozilla/StyleSheet.h"
31 #include "mozilla/StyleSheetInlines.h"
32 #include "mozilla/dom/Location.h"
33 #include "nsIURIMutator.h"
35 #include "unicode/uloc.h"
37 nsChromeRegistry* nsChromeRegistry::gChromeRegistry;
39 // DO NOT use namespace mozilla; it'll break due to a naming conflict between
40 // mozilla::TextRange and a TextRange in OSX headers.
41 using mozilla::PresShell;
42 using mozilla::StyleSheet;
43 using mozilla::dom::Document;
44 using mozilla::dom::IsChromeURI;
45 using mozilla::dom::Location;
47 ////////////////////////////////////////////////////////////////////////////////
49 void nsChromeRegistry::LogMessage(const char* aMsg, ...) {
50 nsCOMPtr<nsIConsoleService> console(
51 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
52 if (!console) return;
54 va_list args;
55 va_start(args, aMsg);
56 mozilla::SmprintfPointer formatted = mozilla::Vsmprintf(aMsg, args);
57 va_end(args);
58 if (!formatted) return;
60 console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted.get()).get());
63 void nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber,
64 uint32_t flags, const char* aMsg,
65 ...) {
66 nsresult rv;
68 nsCOMPtr<nsIConsoleService> console(
69 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
71 nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
72 if (!console || !error) return;
74 va_list args;
75 va_start(args, aMsg);
76 mozilla::SmprintfPointer formatted = mozilla::Vsmprintf(aMsg, args);
77 va_end(args);
78 if (!formatted) return;
80 nsCString spec;
81 if (aURL) aURL->GetSpec(spec);
83 rv = error->Init(
84 NS_ConvertUTF8toUTF16(formatted.get()), NS_ConvertUTF8toUTF16(spec),
85 EmptyString(), aLineNumber, 0, flags, "chrome registration",
86 false /* from private window */, true /* from chrome context */);
88 if (NS_FAILED(rv)) return;
90 console->LogMessage(error);
93 nsChromeRegistry::~nsChromeRegistry() { gChromeRegistry = nullptr; }
95 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry)
96 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry)
97 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry)
98 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry)
99 NS_INTERFACE_MAP_ENTRY(nsIObserver)
100 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
101 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChromeRegistry)
102 NS_INTERFACE_MAP_END
104 NS_IMPL_ADDREF(nsChromeRegistry)
105 NS_IMPL_RELEASE(nsChromeRegistry)
107 ////////////////////////////////////////////////////////////////////////////////
108 // nsIChromeRegistry methods:
110 already_AddRefed<nsIChromeRegistry> nsChromeRegistry::GetService() {
111 if (!gChromeRegistry) {
112 // We don't actually want this ref, we just want the service to
113 // initialize if it hasn't already.
114 nsCOMPtr<nsIChromeRegistry> reg(
115 do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
116 if (!gChromeRegistry) return nullptr;
118 nsCOMPtr<nsIChromeRegistry> registry = gChromeRegistry;
119 return registry.forget();
122 nsresult nsChromeRegistry::Init() {
123 // This initialization process is fairly complicated and may cause reentrant
124 // getservice calls to resolve chrome URIs (especially locale files). We
125 // don't want that, so we inform the protocol handler about our existence
126 // before we are actually fully initialized.
127 gChromeRegistry = this;
129 mInitialized = true;
131 return NS_OK;
134 nsresult nsChromeRegistry::GetProviderAndPath(nsIURI* aChromeURL,
135 nsACString& aProvider,
136 nsACString& aPath) {
137 nsresult rv;
139 #ifdef DEBUG
140 bool isChrome;
141 aChromeURL->SchemeIs("chrome", &isChrome);
142 NS_ASSERTION(isChrome, "Non-chrome URI?");
143 #endif
145 nsAutoCString path;
146 rv = aChromeURL->GetPathQueryRef(path);
147 NS_ENSURE_SUCCESS(rv, rv);
149 if (path.Length() < 3) {
150 LogMessage("Invalid chrome URI: %s", path.get());
151 return NS_ERROR_FAILURE;
154 path.SetLength(nsUnescapeCount(path.BeginWriting()));
155 NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!");
157 int32_t slash = path.FindChar('/', 1);
158 if (slash == 1) {
159 LogMessage("Invalid chrome URI: %s", path.get());
160 return NS_ERROR_FAILURE;
163 if (slash == -1) {
164 aPath.Truncate();
165 } else {
166 if (slash == (int32_t)path.Length() - 1)
167 aPath.Truncate();
168 else
169 aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1);
171 --slash;
174 aProvider.Assign(path.get() + 1, slash);
175 return NS_OK;
178 nsresult nsChromeRegistry::Canonify(nsCOMPtr<nsIURI>& aChromeURL) {
179 NS_NAMED_LITERAL_CSTRING(kSlash, "/");
181 nsresult rv;
183 nsAutoCString provider, path;
184 rv = GetProviderAndPath(aChromeURL, provider, path);
185 NS_ENSURE_SUCCESS(rv, rv);
187 if (path.IsEmpty()) {
188 nsAutoCString package;
189 rv = aChromeURL->GetHost(package);
190 NS_ENSURE_SUCCESS(rv, rv);
192 // we re-use the "path" local string to build a new URL path
193 path.Assign(kSlash + provider + kSlash + package);
194 if (provider.EqualsLiteral("content")) {
195 path.AppendLiteral(".xul");
196 } else if (provider.EqualsLiteral("locale")) {
197 path.AppendLiteral(".dtd");
198 } else if (provider.EqualsLiteral("skin")) {
199 path.AppendLiteral(".css");
200 } else {
201 return NS_ERROR_INVALID_ARG;
203 return NS_MutateURI(aChromeURL).SetPathQueryRef(path).Finalize(aChromeURL);
204 } else {
205 // prevent directory traversals ("..")
206 // path is already unescaped once, but uris can get unescaped twice
207 const char* pos = path.BeginReading();
208 const char* end = path.EndReading();
209 // Must start with [a-zA-Z0-9].
210 if (!('a' <= *pos && *pos <= 'z') && !('A' <= *pos && *pos <= 'Z') &&
211 !('0' <= *pos && *pos <= '9')) {
212 return NS_ERROR_DOM_BAD_URI;
214 while (pos < end) {
215 switch (*pos) {
216 case ':':
217 return NS_ERROR_DOM_BAD_URI;
218 case '.':
219 if (pos[1] == '.') return NS_ERROR_DOM_BAD_URI;
220 break;
221 case '%':
222 // chrome: URIs with double-escapes are trying to trick us.
223 // watch for %2e, and %25 in case someone triple unescapes
224 if (pos[1] == '2' &&
225 (pos[2] == 'e' || pos[2] == 'E' || pos[2] == '5'))
226 return NS_ERROR_DOM_BAD_URI;
227 break;
228 case '?':
229 case '#':
230 pos = end;
231 continue;
233 ++pos;
237 return NS_OK;
240 NS_IMETHODIMP
241 nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI** aResult) {
242 nsresult rv;
243 if (NS_WARN_IF(!aChromeURI)) {
244 return NS_ERROR_INVALID_ARG;
247 if (mOverrideTable.Get(aChromeURI, aResult)) return NS_OK;
249 nsCOMPtr<nsIURL> chromeURL(do_QueryInterface(aChromeURI));
250 NS_ENSURE_TRUE(chromeURL, NS_NOINTERFACE);
252 nsAutoCString package, provider, path;
253 rv = chromeURL->GetHostPort(package);
254 NS_ENSURE_SUCCESS(rv, rv);
256 rv = GetProviderAndPath(chromeURL, provider, path);
257 NS_ENSURE_SUCCESS(rv, rv);
259 nsIURI* baseURI = GetBaseURIFromPackage(package, provider, path);
261 uint32_t flags;
262 rv = GetFlagsFromPackage(package, &flags);
263 if (NS_FAILED(rv)) return rv;
265 if (!baseURI) {
266 LogMessage("No chrome package registered for chrome://%s/%s/%s",
267 package.get(), provider.get(), path.get());
268 return NS_ERROR_FILE_NOT_FOUND;
271 return NS_NewURI(aResult, path, nullptr, baseURI);
274 ////////////////////////////////////////////////////////////////////////
276 // theme stuff
278 static void FlushSkinBindingsForWindow(nsPIDOMWindowOuter* aWindow) {
279 // Get the document.
280 RefPtr<Document> document = aWindow->GetDoc();
281 if (!document) return;
283 // Annihilate all XBL bindings.
284 document->FlushSkinBindings();
287 // XXXbsmedberg: move this to nsIWindowMediator
288 NS_IMETHODIMP nsChromeRegistry::RefreshSkins() {
289 nsCOMPtr<nsIWindowMediator> windowMediator(
290 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
291 if (!windowMediator) return NS_OK;
293 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
294 windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
295 bool more;
296 windowEnumerator->HasMoreElements(&more);
297 while (more) {
298 nsCOMPtr<nsISupports> protoWindow;
299 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
300 if (protoWindow) {
301 nsCOMPtr<nsPIDOMWindowOuter> domWindow = do_QueryInterface(protoWindow);
302 if (domWindow) FlushSkinBindingsForWindow(domWindow);
304 windowEnumerator->HasMoreElements(&more);
307 FlushSkinCaches();
309 windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
310 windowEnumerator->HasMoreElements(&more);
311 while (more) {
312 nsCOMPtr<nsISupports> protoWindow;
313 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
314 if (protoWindow) {
315 nsCOMPtr<nsPIDOMWindowOuter> domWindow = do_QueryInterface(protoWindow);
316 if (domWindow) RefreshWindow(domWindow);
318 windowEnumerator->HasMoreElements(&more);
321 return NS_OK;
324 void nsChromeRegistry::FlushSkinCaches() {
325 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
326 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
328 obsSvc->NotifyObservers(static_cast<nsIChromeRegistry*>(this),
329 NS_CHROME_FLUSH_SKINS_TOPIC, nullptr);
332 // XXXbsmedberg: move this to windowmediator
333 nsresult nsChromeRegistry::RefreshWindow(nsPIDOMWindowOuter* aWindow) {
334 // Deal with our subframes first.
335 nsDOMWindowList* frames = aWindow->GetFrames();
336 uint32_t length = frames->GetLength();
337 for (uint32_t j = 0; j < length; j++) {
338 nsCOMPtr<nsPIDOMWindowOuter> piWindow = frames->IndexedGetter(j);
339 RefreshWindow(piWindow);
342 nsresult rv;
343 // Get the document.
344 RefPtr<Document> document = aWindow->GetDoc();
345 if (!document) return NS_OK;
347 // Deal with the agent sheets first. Have to do all the style sets by hand.
348 RefPtr<PresShell> presShell = document->GetPresShell();
349 if (presShell) {
350 // Reload only the chrome URL agent style sheets.
351 nsTArray<RefPtr<StyleSheet>> agentSheets;
352 rv = presShell->GetAgentStyleSheets(agentSheets);
353 NS_ENSURE_SUCCESS(rv, rv);
355 nsTArray<RefPtr<StyleSheet>> newAgentSheets;
356 for (StyleSheet* sheet : agentSheets) {
357 nsIURI* uri = sheet->GetSheetURI();
359 if (IsChromeURI(uri)) {
360 // Reload the sheet.
361 RefPtr<StyleSheet> newSheet;
362 rv = document->LoadChromeSheetSync(uri, true, &newSheet);
363 if (NS_FAILED(rv)) return rv;
364 if (newSheet) {
365 newAgentSheets.AppendElement(newSheet);
366 return NS_OK;
368 } else { // Just use the same sheet.
369 rv = newAgentSheets.AppendElement(sheet) ? NS_OK : NS_ERROR_FAILURE;
370 if (NS_FAILED(rv)) return rv;
374 rv = presShell->SetAgentStyleSheets(newAgentSheets);
375 NS_ENSURE_SUCCESS(rv, rv);
378 size_t count = document->SheetCount();
380 // Build an array of style sheets we need to reload.
381 nsTArray<RefPtr<StyleSheet>> oldSheets(count);
382 nsTArray<RefPtr<StyleSheet>> newSheets(count);
384 // Iterate over the style sheets.
385 for (size_t i = 0; i < count; i++) {
386 // Get the style sheet
387 oldSheets.AppendElement(document->SheetAt(i));
390 // Iterate over our old sheets and kick off a sync load of the new
391 // sheet if and only if it's a non-inline sheet with a chrome URL.
392 for (StyleSheet* sheet : oldSheets) {
393 MOZ_ASSERT(sheet,
394 "SheetAt shouldn't return nullptr for "
395 "in-range sheet indexes");
396 nsIURI* uri = sheet->GetSheetURI();
398 if (!sheet->IsInline() && IsChromeURI(uri)) {
399 // Reload the sheet.
400 RefPtr<StyleSheet> newSheet;
401 // XXX what about chrome sheets that have a title or are disabled? This
402 // only works by sheer dumb luck.
403 document->LoadChromeSheetSync(uri, false, &newSheet);
404 // Even if it's null, we put in in there.
405 newSheets.AppendElement(newSheet);
406 } else {
407 // Just use the same sheet.
408 newSheets.AppendElement(sheet);
412 // Now notify the document that multiple sheets have been added and removed.
413 document->UpdateStyleSheets(oldSheets, newSheets);
414 return NS_OK;
417 void nsChromeRegistry::FlushAllCaches() {
418 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
419 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
421 obsSvc->NotifyObservers((nsIChromeRegistry*)this, NS_CHROME_FLUSH_TOPIC,
422 nullptr);
425 NS_IMETHODIMP
426 nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, bool* aResult) {
427 nsresult rv;
428 *aResult = false;
430 #ifdef DEBUG
431 bool isChrome;
432 aChromeURI->SchemeIs("chrome", &isChrome);
433 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!");
434 #endif
436 nsCOMPtr<nsIURL> url(do_QueryInterface(aChromeURI));
437 NS_ENSURE_TRUE(url, NS_NOINTERFACE);
439 nsAutoCString provider, file;
440 rv = GetProviderAndPath(url, provider, file);
441 NS_ENSURE_SUCCESS(rv, rv);
443 if (!provider.EqualsLiteral("skin")) *aResult = true;
445 return NS_OK;
448 NS_IMETHODIMP
449 nsChromeRegistry::AllowContentToAccess(nsIURI* aURI, bool* aResult) {
450 nsresult rv;
452 *aResult = false;
454 #ifdef DEBUG
455 bool isChrome;
456 aURI->SchemeIs("chrome", &isChrome);
457 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowContentToAccess!");
458 #endif
460 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
461 if (!url) {
462 NS_ERROR("Chrome URL doesn't implement nsIURL.");
463 return NS_ERROR_UNEXPECTED;
466 nsAutoCString package;
467 rv = url->GetHostPort(package);
468 NS_ENSURE_SUCCESS(rv, rv);
470 uint32_t flags;
471 rv = GetFlagsFromPackage(package, &flags);
473 if (NS_SUCCEEDED(rv)) {
474 *aResult = !!(flags & CONTENT_ACCESSIBLE);
476 return NS_OK;
479 NS_IMETHODIMP
480 nsChromeRegistry::CanLoadURLRemotely(nsIURI* aURI, bool* aResult) {
481 nsresult rv;
483 *aResult = false;
485 #ifdef DEBUG
486 bool isChrome;
487 aURI->SchemeIs("chrome", &isChrome);
488 NS_ASSERTION(isChrome, "Non-chrome URI passed to CanLoadURLRemotely!");
489 #endif
491 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
492 if (!url) {
493 NS_ERROR("Chrome URL doesn't implement nsIURL.");
494 return NS_ERROR_UNEXPECTED;
497 nsAutoCString package;
498 rv = url->GetHostPort(package);
499 NS_ENSURE_SUCCESS(rv, rv);
501 uint32_t flags;
502 rv = GetFlagsFromPackage(package, &flags);
504 if (NS_SUCCEEDED(rv)) {
505 *aResult = !!(flags & REMOTE_ALLOWED);
507 return NS_OK;
510 NS_IMETHODIMP
511 nsChromeRegistry::MustLoadURLRemotely(nsIURI* aURI, bool* aResult) {
512 nsresult rv;
514 *aResult = false;
516 #ifdef DEBUG
517 bool isChrome;
518 aURI->SchemeIs("chrome", &isChrome);
519 NS_ASSERTION(isChrome, "Non-chrome URI passed to MustLoadURLRemotely!");
520 #endif
522 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
523 if (!url) {
524 NS_ERROR("Chrome URL doesn't implement nsIURL.");
525 return NS_ERROR_UNEXPECTED;
528 nsAutoCString package;
529 rv = url->GetHostPort(package);
530 NS_ENSURE_SUCCESS(rv, rv);
532 uint32_t flags;
533 rv = GetFlagsFromPackage(package, &flags);
535 if (NS_SUCCEEDED(rv)) {
536 *aResult = !!(flags & REMOTE_REQUIRED);
538 return NS_OK;
541 bool nsChromeRegistry::GetDirectionForLocale(const nsACString& aLocale) {
542 int pref = mozilla::Preferences::GetInt("intl.uidirection", -1);
543 if (pref >= 0) {
544 return (pref > 0);
546 nsAutoCString locale(aLocale);
547 SanitizeForBCP47(locale);
548 return uloc_isRightToLeft(locale.get());
551 already_AddRefed<nsChromeRegistry> nsChromeRegistry::GetSingleton() {
552 if (gChromeRegistry) {
553 RefPtr<nsChromeRegistry> registry = gChromeRegistry;
554 return registry.forget();
557 RefPtr<nsChromeRegistry> cr;
558 if (GeckoProcessType_Content == XRE_GetProcessType())
559 cr = new nsChromeRegistryContent();
560 else
561 cr = new nsChromeRegistryChrome();
563 if (NS_FAILED(cr->Init())) return nullptr;
565 return cr.forget();
568 void nsChromeRegistry::SanitizeForBCP47(nsACString& aLocale) {
569 // Currently, the only locale code we use that's not BCP47-conformant is
570 // "ja-JP-mac" on OS X, but let's try to be more general than just
571 // hard-coding that here.
572 const int32_t LANG_TAG_CAPACITY = 128;
573 char langTag[LANG_TAG_CAPACITY];
574 nsAutoCString locale(aLocale);
575 UErrorCode err = U_ZERO_ERROR;
576 // This is a fail-safe method that will set langTag to "und" if it cannot
577 // match any part of the input locale code.
578 int32_t len =
579 uloc_toLanguageTag(locale.get(), langTag, LANG_TAG_CAPACITY, false, &err);
580 if (U_SUCCESS(err) && len > 0) {
581 aLocale.Assign(langTag, len);