AboutRedirector.cpp (15550B)
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/. */ 5 6 // See also: docshell/base/nsAboutRedirector.cpp 7 8 #include "AboutRedirector.h" 9 #include "nsNetUtil.h" 10 #include "nsIAppStartup.h" 11 #include "nsIChannel.h" 12 #include "nsIURI.h" 13 #include "nsIProtocolHandler.h" 14 #include "nsServiceManagerUtils.h" 15 #include "mozilla/Components.h" 16 #include "mozilla/StaticPrefs_browser.h" 17 #include "mozilla/dom/ContentChild.h" 18 #include "mozilla/browser/NimbusFeatures.h" 19 20 // For Tor Browser manual 21 #include "nsTHashSet.h" 22 #include "mozilla/intl/LocaleService.h" 23 #include "mozilla/Omnijar.h" 24 25 #define PROFILES_ENABLED_PREF "browser.profiles.enabled" 26 #define ABOUT_WELCOME_CHROME_URL \ 27 "chrome://browser/content/aboutwelcome/aboutwelcome.html" 28 #define ABOUT_HOME_URL "about:home" 29 // NOTE: We return "about:tor" rather than the "chrome:" path 30 // "chrome://browser/content/abouttor/aboutTor.html" 31 // The result is that the channel created in NewChannel in will have its 32 // resultPrincipalURI set to "about:tor". 33 // What this means in practice is that the loaded document's documentURI and 34 // currentURI will be "about:tor" rather than "about:newtab", "about:home", 35 // "about:welcome" or "about:privatebrowsing". 36 // The disadvantage of this is that we often need to add "about:tor" to places 37 // where "about:newtab" or other URIs appear. 38 // The advantage is that we maintain more control against changes in 39 // mozilla-central. 40 #define BASE_BROWSER_HOME_PAGE_URL "about:tor" 41 42 namespace mozilla { 43 namespace browser { 44 45 NS_IMPL_ISUPPORTS(AboutRedirector, nsIAboutModule) 46 47 struct RedirEntry { 48 const char* id; 49 const char* url; 50 uint32_t flags; 51 }; 52 53 static const uint32_t BASE_BROWSER_HOME_PAGE_FLAGS = 54 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 55 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 56 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 57 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI; 58 59 /* 60 Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome 61 privileges. This is potentially dangerous. Please use 62 URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below 63 unless your about: page really needs chrome privileges. Security review is 64 required before adding new map entries without 65 URI_SAFE_FOR_UNTRUSTED_CONTENT. 66 67 NOTE: changes to this redir map need to be accompanied with changes to 68 browser/components/about/components.conf 69 */ 70 static const RedirEntry kRedirMap[] = { 71 {"aichatcontent", "chrome://browser/content/aiwindow/aiChatContent.html", 72 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 73 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 74 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 75 {"asrouter", "chrome://browser/content/asrouter/asrouter-admin.html", 76 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 77 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 78 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 79 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 80 {"blocked", "chrome://browser/content/blockedSite.xhtml", 81 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 82 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 83 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 84 {"certerror", "chrome://global/content/aboutNetError.html", 85 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 86 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 87 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 88 {"unloads", "chrome://browser/content/tabunloader/aboutUnloads.html", 89 nsIAboutModule::ALLOW_SCRIPT}, 90 {"framecrashed", "chrome://browser/content/aboutFrameCrashed.html", 91 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 92 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | 93 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 94 {"logins", "chrome://browser/content/aboutlogins/aboutLogins.html", 95 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 96 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 97 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 98 nsIAboutModule::IS_SECURE_CHROME_UI}, 99 {"loginsimportreport", 100 "chrome://browser/content/aboutlogins/aboutLoginsImportReport.html", 101 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 102 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 103 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 104 nsIAboutModule::IS_SECURE_CHROME_UI}, 105 {"opentabs", "chrome://browser/content/tabbrowser/opentabs.html", 106 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI | 107 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 108 {"policies", "chrome://browser/content/policies/aboutPolicies.html", 109 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 110 {"privatebrowsing", "about:blank", BASE_BROWSER_HOME_PAGE_FLAGS}, 111 {"profiling", 112 "chrome://devtools/content/performance-new/aboutprofiling/index.html", 113 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 114 // Drop about:rights. tor-browser#43901. 115 #ifndef BASE_BROWSER_VERSION 116 {"robots", "chrome://browser/content/aboutRobots.xhtml", 117 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 118 nsIAboutModule::ALLOW_SCRIPT}, 119 #endif 120 {"rulesets", "chrome://browser/content/rulesets/aboutRulesets.html", 121 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 122 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 123 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 124 nsIAboutModule::IS_SECURE_CHROME_UI}, 125 {"sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml", 126 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT | 127 nsIAboutModule::IS_SECURE_CHROME_UI}, 128 {"tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml", 129 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 130 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 131 {"welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml", 132 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT | 133 nsIAboutModule::IS_SECURE_CHROME_UI}, 134 {"welcome", "about:blank", BASE_BROWSER_HOME_PAGE_FLAGS}, 135 {"home", "about:blank", BASE_BROWSER_HOME_PAGE_FLAGS}, 136 {"newtab", "chrome://browser/content/blanktab.html", 137 BASE_BROWSER_HOME_PAGE_FLAGS}, 138 {"messagepreview", 139 "chrome://browser/content/messagepreview/messagepreview.html", 140 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 141 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 142 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 143 {"settings", "chrome://browser/content/preferences/preferences.xhtml", 144 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI | 145 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 146 {"preferences", "chrome://browser/content/preferences/preferences.xhtml", 147 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 148 {"downloads", 149 "chrome://browser/content/downloads/contentAreaDownloadsView.xhtml", 150 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 151 {"reader", "chrome://global/content/reader/aboutReader.html", 152 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 153 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 154 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 155 {"restartrequired", "chrome://browser/content/aboutRestartRequired.xhtml", 156 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 157 #ifdef MOZ_SELECTABLE_PROFILES 158 {"profilemanager", "chrome://browser/content/profiles/profiles.html", 159 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI | 160 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 161 {"editprofile", "chrome://browser/content/profiles/edit-profile.html", 162 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 163 nsIAboutModule::IS_SECURE_CHROME_UI | nsIAboutModule::ALLOW_SCRIPT | 164 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 165 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 166 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 167 {"deleteprofile", "chrome://browser/content/profiles/delete-profile.html", 168 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 169 nsIAboutModule::IS_SECURE_CHROME_UI | 170 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 171 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 172 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 173 {"newprofile", "chrome://browser/content/profiles/new-profile.html", 174 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 175 nsIAboutModule::IS_SECURE_CHROME_UI | nsIAboutModule::ALLOW_SCRIPT | 176 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 177 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 178 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 179 #endif 180 {"keyboard", "chrome://browser/content/customkeys/customkeys.html", 181 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 182 nsIAboutModule::IS_SECURE_CHROME_UI | nsIAboutModule::ALLOW_SCRIPT | 183 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 184 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS}, 185 {"tor", "chrome://browser/content/abouttor/aboutTor.html", 186 BASE_BROWSER_HOME_PAGE_FLAGS}, 187 // The correct URI must be obtained by GetManualChromeURI 188 {"manual", "about:blank", 189 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 190 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 191 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 192 nsIAboutModule::IS_SECURE_CHROME_UI}, 193 }; 194 195 static nsAutoCString GetAboutModuleName(nsIURI* aURI) { 196 nsAutoCString path; 197 aURI->GetPathQueryRef(path); 198 199 int32_t f = path.FindChar('#'); 200 if (f >= 0) path.SetLength(f); 201 202 f = path.FindChar('?'); 203 if (f >= 0) path.SetLength(f); 204 205 ToLowerCase(path); 206 return path; 207 } 208 209 static nsTHashSet<nsCStringHashKey> GetManualLocales() { 210 nsTHashSet<nsCStringHashKey> locales; 211 RefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::APP); 212 if (!zip) { 213 // Probably a local build started with ./mach run 214 return locales; 215 } 216 UniquePtr<nsZipFind> find; 217 const nsAutoCString prefix("chrome/browser/content/browser/manual/"); 218 nsAutoCString needle = prefix; 219 needle.Append("*.html"); 220 if (NS_SUCCEEDED(zip->FindInit(needle.get(), getter_Transfers(find)))) { 221 const char* entryName; 222 uint16_t entryNameLen; 223 while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) { 224 // 5 is to remove the final `.html` 225 const size_t length = entryNameLen - prefix.Length() - 5; 226 locales.Insert(nsAutoCString(entryName + prefix.Length(), length)); 227 } 228 } 229 return locales; 230 } 231 232 static nsAutoCString GetManualChromeURI() { 233 static nsTHashSet<nsCStringHashKey> locales = GetManualLocales(); 234 235 nsAutoCString reqLocale; 236 intl::LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale); 237 // Check every time the URL is needed in case the locale has changed. 238 // It might help also if we start allowing to change language, e.g., with a 239 // get parameter (see tor-browser#42675). 240 if (!locales.Contains(reqLocale) && reqLocale.Length() > 2 && 241 reqLocale[2] == '-') { 242 // At the moment, codes in our manual output are either 2 letters (en) or 243 // 5 letters (pt-BR) 244 reqLocale.SetLength(2); 245 } 246 if (!locales.Contains(reqLocale)) { 247 reqLocale = "en"; 248 } 249 250 // %s is the language 251 constexpr char model[] = "chrome://browser/content/manual/%s.html"; 252 nsAutoCString url; 253 url.AppendPrintf(model, reqLocale.get()); 254 return url; 255 } 256 257 NS_IMETHODIMP 258 AboutRedirector::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, 259 nsIChannel** result) { 260 NS_ENSURE_ARG_POINTER(aURI); 261 NS_ENSURE_ARG_POINTER(aLoadInfo); 262 263 NS_ASSERTION(result, "must not be null"); 264 265 nsAutoCString path = GetAboutModuleName(aURI); 266 267 if ((path.EqualsASCII("editprofile") || path.EqualsASCII("deleteprofile") || 268 path.EqualsASCII("newprofile")) && 269 !mozilla::Preferences::GetBool(PROFILES_ENABLED_PREF, false)) { 270 return NS_ERROR_NOT_AVAILABLE; 271 } 272 273 if (path.EqualsASCII("profilemanager") && 274 !mozilla::Preferences::GetBool(PROFILES_ENABLED_PREF, false)) { 275 bool startingUp; 276 nsCOMPtr<nsIAppStartup> appStartup( 277 mozilla::components::AppStartup::Service()); 278 if (NS_FAILED(appStartup->GetStartingUp(&startingUp)) || !startingUp) { 279 return NS_ERROR_NOT_AVAILABLE; 280 } 281 } 282 283 for (auto& redir : kRedirMap) { 284 if (!strcmp(path.get(), redir.id)) { 285 nsAutoCString url; 286 287 if (path.EqualsLiteral("welcome") || path.EqualsLiteral("home") || 288 path.EqualsLiteral("privatebrowsing") || 289 (path.EqualsLiteral("newtab") && 290 StaticPrefs::browser_newtabpage_enabled())) { 291 url.AssignASCII(BASE_BROWSER_HOME_PAGE_URL); 292 } 293 294 if (path.EqualsLiteral("manual")) { 295 url = GetManualChromeURI(); 296 } 297 298 // fall back to the specified url in the map 299 if (url.IsEmpty()) { 300 url.AssignASCII(redir.url); 301 } 302 303 nsCOMPtr<nsIChannel> tempChannel; 304 nsCOMPtr<nsIURI> tempURI; 305 nsresult rv = NS_NewURI(getter_AddRefs(tempURI), url); 306 NS_ENSURE_SUCCESS(rv, rv); 307 308 // If tempURI links to an external URI (i.e. something other than 309 // chrome:// or resource://) then set the result principal URI on the 310 // load info which forces the channel prncipal to reflect the displayed 311 // URL rather then being the systemPrincipal. 312 bool isUIResource = false; 313 rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, 314 &isUIResource); 315 NS_ENSURE_SUCCESS(rv, rv); 316 317 rv = NS_NewChannelInternal(getter_AddRefs(tempChannel), tempURI, 318 aLoadInfo); 319 NS_ENSURE_SUCCESS(rv, rv); 320 321 if (!isUIResource) { 322 aLoadInfo->SetResultPrincipalURI(tempURI); 323 } 324 tempChannel->SetOriginalURI(aURI); 325 326 NS_ADDREF(*result = tempChannel); 327 return rv; 328 } 329 } 330 331 return NS_ERROR_ILLEGAL_VALUE; 332 } 333 334 NS_IMETHODIMP 335 AboutRedirector::GetURIFlags(nsIURI* aURI, uint32_t* result) { 336 NS_ENSURE_ARG_POINTER(aURI); 337 338 nsAutoCString name = GetAboutModuleName(aURI); 339 340 for (auto& redir : kRedirMap) { 341 if (name.Equals(redir.id)) { 342 *result = redir.flags; 343 return NS_OK; 344 } 345 } 346 347 return NS_ERROR_ILLEGAL_VALUE; 348 } 349 350 NS_IMETHODIMP 351 AboutRedirector::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) { 352 NS_ENSURE_ARG_POINTER(aURI); 353 354 nsAutoCString name = GetAboutModuleName(aURI); 355 356 if (name.EqualsLiteral("manual")) { 357 return NS_NewURI(chromeURI, GetManualChromeURI()); 358 } 359 360 for (const auto& redir : kRedirMap) { 361 if (name.Equals(redir.id)) { 362 return NS_NewURI(chromeURI, redir.url); 363 } 364 } 365 366 return NS_ERROR_ILLEGAL_VALUE; 367 } 368 369 nsresult AboutRedirector::Create(REFNSIID aIID, void** result) { 370 AboutRedirector* about = new AboutRedirector(); 371 if (about == nullptr) return NS_ERROR_OUT_OF_MEMORY; 372 NS_ADDREF(about); 373 nsresult rv = about->QueryInterface(aIID, result); 374 NS_RELEASE(about); 375 return rv; 376 } 377 378 } // namespace browser 379 } // namespace mozilla