nsStyleSheetService.cpp (10141B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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/. */ 6 7 /* implementation of interface for managing user and user-agent style sheets */ 8 9 #include "nsStyleSheetService.h" 10 11 #include "mozilla/MemoryReporting.h" 12 #include "mozilla/PreloadedStyleSheet.h" 13 #include "mozilla/PresShell.h" 14 #include "mozilla/PresShellInlines.h" 15 #include "mozilla/StyleSheet.h" 16 #include "mozilla/StyleSheetInlines.h" 17 #include "mozilla/css/Loader.h" 18 #include "mozilla/dom/ContentParent.h" 19 #include "mozilla/dom/Promise.h" 20 #include "mozilla/ipc/URIUtils.h" 21 #include "nsCOMPtr.h" 22 #include "nsIConsoleService.h" 23 #include "nsISimpleEnumerator.h" 24 #include "nsISupportsPrimitives.h" 25 #include "nsIURI.h" 26 #include "nsLayoutStatics.h" 27 #include "nsLayoutUtils.h" 28 #include "nsNetUtil.h" 29 30 using namespace mozilla; 31 32 nsStyleSheetService* nsStyleSheetService::gInstance = nullptr; 33 34 nsStyleSheetService::nsStyleSheetService() { 35 static_assert(0 == AGENT_SHEET && 1 == USER_SHEET && 2 == AUTHOR_SHEET, 36 "Convention for Style Sheet"); 37 NS_ASSERTION(!gInstance, 38 "Someone is using CreateInstance instead of GetService"); 39 if (!gInstance) { 40 gInstance = this; 41 } 42 nsLayoutStatics::AddRef(); 43 } 44 45 nsStyleSheetService::~nsStyleSheetService() { 46 UnregisterWeakMemoryReporter(this); 47 48 if (gInstance == this) { 49 gInstance = nullptr; 50 } 51 nsLayoutStatics::Release(); 52 } 53 54 NS_IMPL_ISUPPORTS(nsStyleSheetService, nsIStyleSheetService, nsIMemoryReporter) 55 56 static bool SheetHasOriginalURI(StyleSheet* aSheet, nsIURI* aSheetURI) { 57 MOZ_ASSERT(aSheetURI); 58 59 bool result; 60 nsIURI* uri = aSheet->GetOriginalURI(); 61 return uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &result)) && result; 62 } 63 64 int32_t nsStyleSheetService::FindSheetByURI(uint32_t aSheetType, 65 nsIURI* aSheetURI) { 66 SheetArray& sheets = mSheets[aSheetType]; 67 for (int32_t i = sheets.Length() - 1; i >= 0; i--) { 68 if (SheetHasOriginalURI(sheets[i], aSheetURI)) { 69 return i; 70 } 71 } 72 73 return -1; 74 } 75 76 nsresult nsStyleSheetService::Init() { 77 // Child processes get their style sheets from the ContentParent. 78 if (XRE_IsContentProcess()) { 79 return NS_OK; 80 } 81 82 RegisterWeakMemoryReporter(this); 83 84 return NS_OK; 85 } 86 87 NS_IMETHODIMP 88 nsStyleSheetService::LoadAndRegisterSheet(nsIURI* aSheetURI, 89 uint32_t aSheetType) { 90 // Warn developers if their stylesheet URL has a #ref at the end. 91 // Stylesheet URIs don't benefit from having a #ref suffix -- and if the 92 // sheet is a data URI, someone might've created this #ref by accident (and 93 // truncated their data-URI stylesheet) by using an unescaped # character in 94 // a #RRGGBB color or #foo() ID-selector in their data-URI representation. 95 bool hasRef; 96 nsresult rv = aSheetURI->GetHasRef(&hasRef); 97 NS_ENSURE_SUCCESS(rv, rv); 98 if (aSheetURI && hasRef) { 99 nsCOMPtr<nsIConsoleService> consoleService = 100 do_GetService(NS_CONSOLESERVICE_CONTRACTID); 101 NS_WARNING_ASSERTION(consoleService, "Failed to get console service!"); 102 if (consoleService) { 103 const char16_t* message = 104 u"nsStyleSheetService::LoadAndRegisterSheet: " 105 u"URI contains unescaped hash character, which might be truncating " 106 u"the sheet, if it's a data URI."; 107 consoleService->LogStringMessage(message); 108 } 109 } 110 111 rv = LoadAndRegisterSheetInternal(aSheetURI, aSheetType); 112 if (NS_SUCCEEDED(rv)) { 113 // Hold on to a copy of the registered PresShells. 114 for (PresShell* presShell : mPresShells.Clone()) { 115 StyleSheet* sheet = mSheets[aSheetType].LastElement(); 116 presShell->NotifyStyleSheetServiceSheetAdded(sheet, aSheetType); 117 } 118 119 if (XRE_IsParentProcess()) { 120 nsTArray<dom::ContentParent*> children; 121 dom::ContentParent::GetAll(children); 122 123 if (children.IsEmpty()) { 124 return rv; 125 } 126 127 for (uint32_t i = 0; i < children.Length(); i++) { 128 (void)children[i]->SendLoadAndRegisterSheet(aSheetURI, aSheetType); 129 } 130 } 131 } 132 return rv; 133 } 134 135 nsresult nsStyleSheetService::LoadAndRegisterSheetInternal( 136 nsIURI* aSheetURI, uint32_t aSheetType) { 137 NS_ENSURE_ARG_POINTER(aSheetURI); 138 139 css::SheetParsingMode parsingMode; 140 switch (aSheetType) { 141 case AGENT_SHEET: 142 parsingMode = css::eAgentSheetFeatures; 143 break; 144 145 case USER_SHEET: 146 parsingMode = css::eUserSheetFeatures; 147 break; 148 149 case AUTHOR_SHEET: 150 parsingMode = css::eAuthorSheetFeatures; 151 break; 152 153 default: 154 NS_WARNING("invalid sheet type argument"); 155 return NS_ERROR_INVALID_ARG; 156 } 157 158 auto loader = MakeRefPtr<css::Loader>(); 159 auto result = loader->LoadSheetSync(aSheetURI, parsingMode, 160 css::Loader::UseSystemPrincipal::Yes); 161 if (result.isErr()) { 162 return result.unwrapErr(); 163 } 164 mSheets[aSheetType].AppendElement(result.unwrap()); 165 return NS_OK; 166 } 167 168 NS_IMETHODIMP 169 nsStyleSheetService::SheetRegistered(nsIURI* sheetURI, uint32_t aSheetType, 170 bool* _retval) { 171 NS_ENSURE_ARG(aSheetType == AGENT_SHEET || aSheetType == USER_SHEET || 172 aSheetType == AUTHOR_SHEET); 173 NS_ENSURE_ARG_POINTER(sheetURI); 174 MOZ_ASSERT(_retval, "Null out param"); 175 176 // Check to see if we have the sheet. 177 *_retval = FindSheetByURI(aSheetType, sheetURI) >= 0; 178 return NS_OK; 179 } 180 181 static nsresult GetParsingMode(uint32_t aSheetType, 182 css::SheetParsingMode* aParsingMode) { 183 switch (aSheetType) { 184 case nsStyleSheetService::AGENT_SHEET: 185 *aParsingMode = css::eAgentSheetFeatures; 186 return NS_OK; 187 188 case nsStyleSheetService::USER_SHEET: 189 *aParsingMode = css::eUserSheetFeatures; 190 return NS_OK; 191 192 case nsStyleSheetService::AUTHOR_SHEET: 193 *aParsingMode = css::eAuthorSheetFeatures; 194 return NS_OK; 195 196 default: 197 NS_WARNING("invalid sheet type argument"); 198 return NS_ERROR_INVALID_ARG; 199 } 200 } 201 202 NS_IMETHODIMP 203 nsStyleSheetService::PreloadSheet(nsIURI* aSheetURI, uint32_t aSheetType, 204 nsIPreloadedStyleSheet** aSheet) { 205 MOZ_ASSERT(aSheet, "Null out param"); 206 NS_ENSURE_ARG_POINTER(aSheetURI); 207 208 css::SheetParsingMode parsingMode; 209 nsresult rv = GetParsingMode(aSheetType, &parsingMode); 210 NS_ENSURE_SUCCESS(rv, rv); 211 212 auto sheet = MakeRefPtr<PreloadedStyleSheet>(aSheetURI, parsingMode); 213 NS_ENSURE_SUCCESS(rv, rv); 214 215 rv = sheet->Preload(); 216 NS_ENSURE_SUCCESS(rv, rv); 217 218 sheet.forget(aSheet); 219 return NS_OK; 220 } 221 222 NS_IMETHODIMP 223 nsStyleSheetService::PreloadSheetAsync(nsIURI* aSheetURI, uint32_t aSheetType, 224 JSContext* aCx, 225 JS::MutableHandle<JS::Value> aRval) { 226 NS_ENSURE_ARG_POINTER(aSheetURI); 227 228 css::SheetParsingMode parsingMode; 229 nsresult rv = GetParsingMode(aSheetType, &parsingMode); 230 NS_ENSURE_SUCCESS(rv, rv); 231 232 nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(aCx); 233 NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED); 234 235 ErrorResult errv; 236 RefPtr<dom::Promise> promise = dom::Promise::Create(global, errv); 237 if (errv.Failed()) { 238 return errv.StealNSResult(); 239 } 240 241 auto sheet = MakeRefPtr<PreloadedStyleSheet>(aSheetURI, parsingMode); 242 sheet->PreloadAsync(WrapNotNull(promise)); 243 244 if (!ToJSValue(aCx, promise, aRval)) { 245 return NS_ERROR_FAILURE; 246 } 247 return NS_OK; 248 } 249 250 NS_IMETHODIMP 251 nsStyleSheetService::UnregisterSheet(nsIURI* aSheetURI, uint32_t aSheetType) { 252 NS_ENSURE_ARG(aSheetType == AGENT_SHEET || aSheetType == USER_SHEET || 253 aSheetType == AUTHOR_SHEET); 254 NS_ENSURE_ARG_POINTER(aSheetURI); 255 256 RefPtr<StyleSheet> sheet; 257 int32_t foundIndex = FindSheetByURI(aSheetType, aSheetURI); 258 if (foundIndex >= 0) { 259 sheet = mSheets[aSheetType][foundIndex]; 260 mSheets[aSheetType].RemoveElementAt(foundIndex); 261 } 262 263 // Hold on to a copy of the registered PresShells. 264 for (PresShell* presShell : mPresShells.Clone()) { 265 if (presShell->StyleSet()) { 266 if (sheet) { 267 presShell->NotifyStyleSheetServiceSheetRemoved(sheet, aSheetType); 268 } 269 } 270 } 271 272 if (XRE_IsParentProcess()) { 273 nsTArray<dom::ContentParent*> children; 274 dom::ContentParent::GetAll(children); 275 276 if (children.IsEmpty()) { 277 return NS_OK; 278 } 279 280 for (uint32_t i = 0; i < children.Length(); i++) { 281 (void)children[i]->SendUnregisterSheet(aSheetURI, aSheetType); 282 } 283 } 284 285 return NS_OK; 286 } 287 288 // static 289 nsStyleSheetService* nsStyleSheetService::GetInstance() { 290 static bool first = true; 291 if (first) { 292 // make sure at first call that it's inited 293 nsCOMPtr<nsIStyleSheetService> dummy = 294 do_GetService(NS_STYLESHEETSERVICE_CONTRACTID); 295 first = false; 296 } 297 298 return gInstance; 299 } 300 301 MOZ_DEFINE_MALLOC_SIZE_OF(StyleSheetServiceMallocSizeOf) 302 303 NS_IMETHODIMP 304 nsStyleSheetService::CollectReports(nsIHandleReportCallback* aHandleReport, 305 nsISupports* aData, bool aAnonymize) { 306 MOZ_COLLECT_REPORT( 307 "explicit/layout/style-sheet-service", KIND_HEAP, UNITS_BYTES, 308 SizeOfIncludingThis(StyleSheetServiceMallocSizeOf), 309 "Memory used for style sheets held by the style sheet service."); 310 311 return NS_OK; 312 } 313 314 size_t nsStyleSheetService::SizeOfIncludingThis( 315 mozilla::MallocSizeOf aMallocSizeOf) const { 316 size_t n = aMallocSizeOf(this); 317 for (const auto& sheetArray : mSheets) { 318 n += sheetArray.ShallowSizeOfExcludingThis(aMallocSizeOf); 319 for (StyleSheet* sheet : sheetArray) { 320 if (sheet) { 321 n += sheet->SizeOfIncludingThis(aMallocSizeOf); 322 } 323 } 324 } 325 return n; 326 } 327 328 void nsStyleSheetService::RegisterPresShell(PresShell* aPresShell) { 329 MOZ_ASSERT(!mPresShells.Contains(aPresShell)); 330 mPresShells.AppendElement(aPresShell); 331 } 332 333 void nsStyleSheetService::UnregisterPresShell(PresShell* aPresShell) { 334 MOZ_ASSERT(mPresShells.Contains(aPresShell)); 335 mPresShells.RemoveElement(aPresShell); 336 }