Bug 1529445 [wpt PR 15111] - Fix #12696: make property_order default to empty list...
[gecko.git] / chrome / nsChromeRegistry.cpp
blobf81dd6d9a6343853902033e531331d0f65a9e755
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::Location;
46 ////////////////////////////////////////////////////////////////////////////////
48 void nsChromeRegistry::LogMessage(const char* aMsg, ...) {
49 nsCOMPtr<nsIConsoleService> console(
50 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
51 if (!console) return;
53 va_list args;
54 va_start(args, aMsg);
55 mozilla::SmprintfPointer formatted = mozilla::Vsmprintf(aMsg, args);
56 va_end(args);
57 if (!formatted) return;
59 console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted.get()).get());
62 void nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber,
63 uint32_t flags, const char* aMsg,
64 ...) {
65 nsresult rv;
67 nsCOMPtr<nsIConsoleService> console(
68 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
70 nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
71 if (!console || !error) return;
73 va_list args;
74 va_start(args, aMsg);
75 mozilla::SmprintfPointer formatted = mozilla::Vsmprintf(aMsg, args);
76 va_end(args);
77 if (!formatted) return;
79 nsCString spec;
80 if (aURL) aURL->GetSpec(spec);
82 rv = error->Init(
83 NS_ConvertUTF8toUTF16(formatted.get()), NS_ConvertUTF8toUTF16(spec),
84 EmptyString(), aLineNumber, 0, flags, "chrome registration",
85 false /* from private window */, true /* from chrome context */);
87 if (NS_FAILED(rv)) return;
89 console->LogMessage(error);
92 nsChromeRegistry::~nsChromeRegistry() { gChromeRegistry = nullptr; }
94 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry)
95 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry)
96 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry)
97 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry)
98 NS_INTERFACE_MAP_ENTRY(nsIObserver)
99 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
100 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChromeRegistry)
101 NS_INTERFACE_MAP_END
103 NS_IMPL_ADDREF(nsChromeRegistry)
104 NS_IMPL_RELEASE(nsChromeRegistry)
106 ////////////////////////////////////////////////////////////////////////////////
107 // nsIChromeRegistry methods:
109 already_AddRefed<nsIChromeRegistry> nsChromeRegistry::GetService() {
110 if (!gChromeRegistry) {
111 // We don't actually want this ref, we just want the service to
112 // initialize if it hasn't already.
113 nsCOMPtr<nsIChromeRegistry> reg(
114 do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
115 if (!gChromeRegistry) return nullptr;
117 nsCOMPtr<nsIChromeRegistry> registry = gChromeRegistry;
118 return registry.forget();
121 nsresult nsChromeRegistry::Init() {
122 // This initialization process is fairly complicated and may cause reentrant
123 // getservice calls to resolve chrome URIs (especially locale files). We
124 // don't want that, so we inform the protocol handler about our existence
125 // before we are actually fully initialized.
126 gChromeRegistry = this;
128 mInitialized = true;
130 return NS_OK;
133 nsresult nsChromeRegistry::GetProviderAndPath(nsIURI* aChromeURL,
134 nsACString& aProvider,
135 nsACString& aPath) {
136 nsresult rv;
138 #ifdef DEBUG
139 bool isChrome;
140 aChromeURL->SchemeIs("chrome", &isChrome);
141 NS_ASSERTION(isChrome, "Non-chrome URI?");
142 #endif
144 nsAutoCString path;
145 rv = aChromeURL->GetPathQueryRef(path);
146 NS_ENSURE_SUCCESS(rv, rv);
148 if (path.Length() < 3) {
149 LogMessage("Invalid chrome URI: %s", path.get());
150 return NS_ERROR_FAILURE;
153 path.SetLength(nsUnescapeCount(path.BeginWriting()));
154 NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!");
156 int32_t slash = path.FindChar('/', 1);
157 if (slash == 1) {
158 LogMessage("Invalid chrome URI: %s", path.get());
159 return NS_ERROR_FAILURE;
162 if (slash == -1) {
163 aPath.Truncate();
164 } else {
165 if (slash == (int32_t)path.Length() - 1)
166 aPath.Truncate();
167 else
168 aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1);
170 --slash;
173 aProvider.Assign(path.get() + 1, slash);
174 return NS_OK;
177 nsresult nsChromeRegistry::Canonify(nsCOMPtr<nsIURI>& aChromeURL) {
178 NS_NAMED_LITERAL_CSTRING(kSlash, "/");
180 nsresult rv;
182 nsAutoCString provider, path;
183 rv = GetProviderAndPath(aChromeURL, provider, path);
184 NS_ENSURE_SUCCESS(rv, rv);
186 if (path.IsEmpty()) {
187 nsAutoCString package;
188 rv = aChromeURL->GetHost(package);
189 NS_ENSURE_SUCCESS(rv, rv);
191 // we re-use the "path" local string to build a new URL path
192 path.Assign(kSlash + provider + kSlash + package);
193 if (provider.EqualsLiteral("content")) {
194 path.AppendLiteral(".xul");
195 } else if (provider.EqualsLiteral("locale")) {
196 path.AppendLiteral(".dtd");
197 } else if (provider.EqualsLiteral("skin")) {
198 path.AppendLiteral(".css");
199 } else {
200 return NS_ERROR_INVALID_ARG;
202 return NS_MutateURI(aChromeURL).SetPathQueryRef(path).Finalize(aChromeURL);
203 } else {
204 // prevent directory traversals ("..")
205 // path is already unescaped once, but uris can get unescaped twice
206 const char* pos = path.BeginReading();
207 const char* end = path.EndReading();
208 // Must start with [a-zA-Z0-9].
209 if (!('a' <= *pos && *pos <= 'z') && !('A' <= *pos && *pos <= 'Z') &&
210 !('0' <= *pos && *pos <= '9')) {
211 return NS_ERROR_DOM_BAD_URI;
213 while (pos < end) {
214 switch (*pos) {
215 case ':':
216 return NS_ERROR_DOM_BAD_URI;
217 case '.':
218 if (pos[1] == '.') return NS_ERROR_DOM_BAD_URI;
219 break;
220 case '%':
221 // chrome: URIs with double-escapes are trying to trick us.
222 // watch for %2e, and %25 in case someone triple unescapes
223 if (pos[1] == '2' &&
224 (pos[2] == 'e' || pos[2] == 'E' || pos[2] == '5'))
225 return NS_ERROR_DOM_BAD_URI;
226 break;
227 case '?':
228 case '#':
229 pos = end;
230 continue;
232 ++pos;
236 return NS_OK;
239 NS_IMETHODIMP
240 nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI** aResult) {
241 nsresult rv;
242 if (NS_WARN_IF(!aChromeURI)) {
243 return NS_ERROR_INVALID_ARG;
246 if (mOverrideTable.Get(aChromeURI, aResult)) return NS_OK;
248 nsCOMPtr<nsIURL> chromeURL(do_QueryInterface(aChromeURI));
249 NS_ENSURE_TRUE(chromeURL, NS_NOINTERFACE);
251 nsAutoCString package, provider, path;
252 rv = chromeURL->GetHostPort(package);
253 NS_ENSURE_SUCCESS(rv, rv);
255 rv = GetProviderAndPath(chromeURL, provider, path);
256 NS_ENSURE_SUCCESS(rv, rv);
258 nsIURI* baseURI = GetBaseURIFromPackage(package, provider, path);
260 uint32_t flags;
261 rv = GetFlagsFromPackage(package, &flags);
262 if (NS_FAILED(rv)) return rv;
264 if (!baseURI) {
265 LogMessage("No chrome package registered for chrome://%s/%s/%s",
266 package.get(), provider.get(), path.get());
267 return NS_ERROR_FILE_NOT_FOUND;
270 return NS_NewURI(aResult, path, nullptr, baseURI);
273 ////////////////////////////////////////////////////////////////////////
275 void nsChromeRegistry::FlushAllCaches() {
276 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
277 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
279 obsSvc->NotifyObservers((nsIChromeRegistry*)this, NS_CHROME_FLUSH_TOPIC,
280 nullptr);
283 NS_IMETHODIMP
284 nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, bool* aResult) {
285 nsresult rv;
286 *aResult = false;
288 #ifdef DEBUG
289 bool isChrome;
290 aChromeURI->SchemeIs("chrome", &isChrome);
291 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!");
292 #endif
294 nsCOMPtr<nsIURL> url(do_QueryInterface(aChromeURI));
295 NS_ENSURE_TRUE(url, NS_NOINTERFACE);
297 nsAutoCString provider, file;
298 rv = GetProviderAndPath(url, provider, file);
299 NS_ENSURE_SUCCESS(rv, rv);
301 if (!provider.EqualsLiteral("skin")) *aResult = true;
303 return NS_OK;
306 NS_IMETHODIMP
307 nsChromeRegistry::AllowContentToAccess(nsIURI* aURI, bool* aResult) {
308 nsresult rv;
310 *aResult = false;
312 #ifdef DEBUG
313 bool isChrome;
314 aURI->SchemeIs("chrome", &isChrome);
315 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowContentToAccess!");
316 #endif
318 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
319 if (!url) {
320 NS_ERROR("Chrome URL doesn't implement nsIURL.");
321 return NS_ERROR_UNEXPECTED;
324 nsAutoCString package;
325 rv = url->GetHostPort(package);
326 NS_ENSURE_SUCCESS(rv, rv);
328 uint32_t flags;
329 rv = GetFlagsFromPackage(package, &flags);
331 if (NS_SUCCEEDED(rv)) {
332 *aResult = !!(flags & CONTENT_ACCESSIBLE);
334 return NS_OK;
337 NS_IMETHODIMP
338 nsChromeRegistry::CanLoadURLRemotely(nsIURI* aURI, bool* aResult) {
339 nsresult rv;
341 *aResult = false;
343 #ifdef DEBUG
344 bool isChrome;
345 aURI->SchemeIs("chrome", &isChrome);
346 NS_ASSERTION(isChrome, "Non-chrome URI passed to CanLoadURLRemotely!");
347 #endif
349 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
350 if (!url) {
351 NS_ERROR("Chrome URL doesn't implement nsIURL.");
352 return NS_ERROR_UNEXPECTED;
355 nsAutoCString package;
356 rv = url->GetHostPort(package);
357 NS_ENSURE_SUCCESS(rv, rv);
359 uint32_t flags;
360 rv = GetFlagsFromPackage(package, &flags);
362 if (NS_SUCCEEDED(rv)) {
363 *aResult = !!(flags & REMOTE_ALLOWED);
365 return NS_OK;
368 NS_IMETHODIMP
369 nsChromeRegistry::MustLoadURLRemotely(nsIURI* aURI, bool* aResult) {
370 nsresult rv;
372 *aResult = false;
374 #ifdef DEBUG
375 bool isChrome;
376 aURI->SchemeIs("chrome", &isChrome);
377 NS_ASSERTION(isChrome, "Non-chrome URI passed to MustLoadURLRemotely!");
378 #endif
380 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
381 if (!url) {
382 NS_ERROR("Chrome URL doesn't implement nsIURL.");
383 return NS_ERROR_UNEXPECTED;
386 nsAutoCString package;
387 rv = url->GetHostPort(package);
388 NS_ENSURE_SUCCESS(rv, rv);
390 uint32_t flags;
391 rv = GetFlagsFromPackage(package, &flags);
393 if (NS_SUCCEEDED(rv)) {
394 *aResult = !!(flags & REMOTE_REQUIRED);
396 return NS_OK;
399 bool nsChromeRegistry::GetDirectionForLocale(const nsACString& aLocale) {
400 int pref = mozilla::Preferences::GetInt("intl.uidirection", -1);
401 if (pref >= 0) {
402 return (pref > 0);
404 nsAutoCString locale(aLocale);
405 SanitizeForBCP47(locale);
406 return uloc_isRightToLeft(locale.get());
409 already_AddRefed<nsChromeRegistry> nsChromeRegistry::GetSingleton() {
410 if (gChromeRegistry) {
411 RefPtr<nsChromeRegistry> registry = gChromeRegistry;
412 return registry.forget();
415 RefPtr<nsChromeRegistry> cr;
416 if (GeckoProcessType_Content == XRE_GetProcessType())
417 cr = new nsChromeRegistryContent();
418 else
419 cr = new nsChromeRegistryChrome();
421 if (NS_FAILED(cr->Init())) return nullptr;
423 return cr.forget();
426 void nsChromeRegistry::SanitizeForBCP47(nsACString& aLocale) {
427 // Currently, the only locale code we use that's not BCP47-conformant is
428 // "ja-JP-mac" on OS X, but let's try to be more general than just
429 // hard-coding that here.
430 const int32_t LANG_TAG_CAPACITY = 128;
431 char langTag[LANG_TAG_CAPACITY];
432 nsAutoCString locale(aLocale);
433 UErrorCode err = U_ZERO_ERROR;
434 // This is a fail-safe method that will set langTag to "und" if it cannot
435 // match any part of the input locale code.
436 int32_t len =
437 uloc_toLanguageTag(locale.get(), langTag, LANG_TAG_CAPACITY, false, &err);
438 if (U_SUCCESS(err) && len > 0) {
439 aLocale.Assign(langTag, len);