/src/mozilla-central/layout/base/nsStyleSheetService.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "mozilla/MemoryReporting.h" |
11 | | #include "mozilla/PreloadedStyleSheet.h" |
12 | | #include "mozilla/StyleSheet.h" |
13 | | #include "mozilla/StyleSheetInlines.h" |
14 | | #include "mozilla/Unused.h" |
15 | | #include "mozilla/css/Loader.h" |
16 | | #include "mozilla/dom/ContentParent.h" |
17 | | #include "mozilla/dom/Promise.h" |
18 | | #include "mozilla/ipc/URIUtils.h" |
19 | | #include "nsIURI.h" |
20 | | #include "nsCOMPtr.h" |
21 | | #include "nsICategoryManager.h" |
22 | | #include "nsISupportsPrimitives.h" |
23 | | #include "nsISimpleEnumerator.h" |
24 | | #include "nsNetUtil.h" |
25 | | #include "nsIConsoleService.h" |
26 | | #include "nsIObserverService.h" |
27 | | #include "nsLayoutStatics.h" |
28 | | #include "nsLayoutUtils.h" |
29 | | |
30 | | using namespace mozilla; |
31 | | |
32 | | nsStyleSheetService *nsStyleSheetService::gInstance = nullptr; |
33 | | |
34 | | nsStyleSheetService::nsStyleSheetService() |
35 | 0 | { |
36 | 0 | static_assert(0 == AGENT_SHEET && 1 == USER_SHEET && 2 == AUTHOR_SHEET, |
37 | 0 | "Convention for Style Sheet"); |
38 | 0 | NS_ASSERTION(!gInstance, "Someone is using CreateInstance instead of GetService"); |
39 | 0 | gInstance = this; |
40 | 0 | nsLayoutStatics::AddRef(); |
41 | 0 | } |
42 | | |
43 | | nsStyleSheetService::~nsStyleSheetService() |
44 | 0 | { |
45 | 0 | UnregisterWeakMemoryReporter(this); |
46 | 0 |
|
47 | 0 | gInstance = nullptr; |
48 | 0 | nsLayoutStatics::Release(); |
49 | 0 | } |
50 | | |
51 | | NS_IMPL_ISUPPORTS( |
52 | | nsStyleSheetService, nsIStyleSheetService, nsIMemoryReporter) |
53 | | |
54 | | void |
55 | | nsStyleSheetService::RegisterFromEnumerator(nsICategoryManager *aManager, |
56 | | const char *aCategory, |
57 | | nsISimpleEnumerator *aEnumerator, |
58 | | uint32_t aSheetType) |
59 | 0 | { |
60 | 0 | if (!aEnumerator) |
61 | 0 | return; |
62 | 0 | |
63 | 0 | bool hasMore; |
64 | 0 | while (NS_SUCCEEDED(aEnumerator->HasMoreElements(&hasMore)) && hasMore) { |
65 | 0 | nsCOMPtr<nsISupports> element; |
66 | 0 | if (NS_FAILED(aEnumerator->GetNext(getter_AddRefs(element)))) |
67 | 0 | break; |
68 | 0 | |
69 | 0 | nsCOMPtr<nsISupportsCString> icStr = do_QueryInterface(element); |
70 | 0 | NS_ASSERTION(icStr, |
71 | 0 | "category manager entries must be nsISupportsCStrings"); |
72 | 0 |
|
73 | 0 | nsAutoCString name; |
74 | 0 | icStr->GetData(name); |
75 | 0 |
|
76 | 0 | nsCString spec; |
77 | 0 | aManager->GetCategoryEntry(nsDependentCString(aCategory), name, spec); |
78 | 0 |
|
79 | 0 | nsCOMPtr<nsIURI> uri; |
80 | 0 | NS_NewURI(getter_AddRefs(uri), spec); |
81 | 0 | if (uri) |
82 | 0 | LoadAndRegisterSheetInternal(uri, aSheetType); |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | | static bool |
87 | | SheetHasURI(StyleSheet* aSheet, nsIURI* aSheetURI) |
88 | 0 | { |
89 | 0 | MOZ_ASSERT(aSheetURI); |
90 | 0 |
|
91 | 0 | bool result; |
92 | 0 | nsIURI* uri = aSheet->GetSheetURI(); |
93 | 0 | return uri && |
94 | 0 | NS_SUCCEEDED(uri->Equals(aSheetURI, &result)) && |
95 | 0 | result; |
96 | 0 | } |
97 | | |
98 | | int32_t |
99 | | nsStyleSheetService::FindSheetByURI(uint32_t aSheetType, nsIURI* aSheetURI) |
100 | 0 | { |
101 | 0 | SheetArray& sheets = mSheets[aSheetType]; |
102 | 0 | for (int32_t i = sheets.Length() - 1; i >= 0; i-- ) { |
103 | 0 | if (SheetHasURI(sheets[i], aSheetURI)) { |
104 | 0 | return i; |
105 | 0 | } |
106 | 0 | } |
107 | 0 |
|
108 | 0 | return -1; |
109 | 0 | } |
110 | | |
111 | | nsresult |
112 | | nsStyleSheetService::Init() |
113 | 0 | { |
114 | 0 | // If you make changes here, consider whether |
115 | 0 | // SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded should be updated too. |
116 | 0 |
|
117 | 0 | // Child processes get their style sheets from the ContentParent. |
118 | 0 | if (XRE_IsContentProcess()) { |
119 | 0 | return NS_OK; |
120 | 0 | } |
121 | 0 | |
122 | 0 | // Enumerate all of the style sheet URIs registered in the category |
123 | 0 | // manager and load them. |
124 | 0 | |
125 | 0 | nsCOMPtr<nsICategoryManager> catMan = |
126 | 0 | do_GetService(NS_CATEGORYMANAGER_CONTRACTID); |
127 | 0 |
|
128 | 0 | NS_ENSURE_TRUE(catMan, NS_ERROR_OUT_OF_MEMORY); |
129 | 0 |
|
130 | 0 | nsCOMPtr<nsISimpleEnumerator> sheets; |
131 | 0 | catMan->EnumerateCategory("agent-style-sheets", getter_AddRefs(sheets)); |
132 | 0 | RegisterFromEnumerator(catMan, "agent-style-sheets", sheets, AGENT_SHEET); |
133 | 0 |
|
134 | 0 | catMan->EnumerateCategory("user-style-sheets", getter_AddRefs(sheets)); |
135 | 0 | RegisterFromEnumerator(catMan, "user-style-sheets", sheets, USER_SHEET); |
136 | 0 |
|
137 | 0 | catMan->EnumerateCategory("author-style-sheets", getter_AddRefs(sheets)); |
138 | 0 | RegisterFromEnumerator(catMan, "author-style-sheets", sheets, AUTHOR_SHEET); |
139 | 0 |
|
140 | 0 | RegisterWeakMemoryReporter(this); |
141 | 0 |
|
142 | 0 | return NS_OK; |
143 | 0 | } |
144 | | |
145 | | NS_IMETHODIMP |
146 | | nsStyleSheetService::LoadAndRegisterSheet(nsIURI *aSheetURI, |
147 | | uint32_t aSheetType) |
148 | 0 | { |
149 | 0 | // Warn developers if their stylesheet URL has a #ref at the end. |
150 | 0 | // Stylesheet URIs don't benefit from having a #ref suffix -- and if the |
151 | 0 | // sheet is a data URI, someone might've created this #ref by accident (and |
152 | 0 | // truncated their data-URI stylesheet) by using an unescaped # character in |
153 | 0 | // a #RRGGBB color or #foo() ID-selector in their data-URI representation. |
154 | 0 | bool hasRef; |
155 | 0 | nsresult rv = aSheetURI->GetHasRef(&hasRef); |
156 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
157 | 0 | if (aSheetURI && hasRef) { |
158 | 0 | nsCOMPtr<nsIConsoleService> consoleService = |
159 | 0 | do_GetService(NS_CONSOLESERVICE_CONTRACTID); |
160 | 0 | NS_WARNING_ASSERTION(consoleService, "Failed to get console service!"); |
161 | 0 | if (consoleService) { |
162 | 0 | const char16_t* message = u"nsStyleSheetService::LoadAndRegisterSheet: " |
163 | 0 | u"URI contains unescaped hash character, which might be truncating " |
164 | 0 | u"the sheet, if it's a data URI."; |
165 | 0 | consoleService->LogStringMessage(message); |
166 | 0 | } |
167 | 0 | } |
168 | 0 |
|
169 | 0 | rv = LoadAndRegisterSheetInternal(aSheetURI, aSheetType); |
170 | 0 | if (NS_SUCCEEDED(rv)) { |
171 | 0 | // Hold on to a copy of the registered PresShells. |
172 | 0 | nsTArray<nsCOMPtr<nsIPresShell>> toNotify(mPresShells); |
173 | 0 | for (nsIPresShell* presShell : toNotify) { |
174 | 0 | StyleSheet* sheet = mSheets[aSheetType].LastElement(); |
175 | 0 | presShell->NotifyStyleSheetServiceSheetAdded(sheet, aSheetType); |
176 | 0 | } |
177 | 0 |
|
178 | 0 | if (XRE_IsParentProcess()) { |
179 | 0 | nsTArray<dom::ContentParent*> children; |
180 | 0 | dom::ContentParent::GetAll(children); |
181 | 0 |
|
182 | 0 | if (children.IsEmpty()) { |
183 | 0 | return rv; |
184 | 0 | } |
185 | 0 | |
186 | 0 | mozilla::ipc::URIParams uri; |
187 | 0 | SerializeURI(aSheetURI, uri); |
188 | 0 |
|
189 | 0 | for (uint32_t i = 0; i < children.Length(); i++) { |
190 | 0 | Unused << children[i]->SendLoadAndRegisterSheet(uri, aSheetType); |
191 | 0 | } |
192 | 0 | } |
193 | 0 | } |
194 | 0 | return rv; |
195 | 0 | } |
196 | | |
197 | | static nsresult |
198 | | LoadSheet(nsIURI* aURI, |
199 | | css::SheetParsingMode aParsingMode, |
200 | | RefPtr<StyleSheet>* aResult) |
201 | 0 | { |
202 | 0 | RefPtr<css::Loader> loader = new css::Loader; |
203 | 0 | return loader->LoadSheetSync(aURI, aParsingMode, true, aResult); |
204 | 0 | } |
205 | | |
206 | | nsresult |
207 | | nsStyleSheetService::LoadAndRegisterSheetInternal(nsIURI *aSheetURI, |
208 | | uint32_t aSheetType) |
209 | 0 | { |
210 | 0 | NS_ENSURE_ARG_POINTER(aSheetURI); |
211 | 0 |
|
212 | 0 | css::SheetParsingMode parsingMode; |
213 | 0 | switch (aSheetType) { |
214 | 0 | case AGENT_SHEET: |
215 | 0 | parsingMode = css::eAgentSheetFeatures; |
216 | 0 | break; |
217 | 0 |
|
218 | 0 | case USER_SHEET: |
219 | 0 | parsingMode = css::eUserSheetFeatures; |
220 | 0 | break; |
221 | 0 |
|
222 | 0 | case AUTHOR_SHEET: |
223 | 0 | parsingMode = css::eAuthorSheetFeatures; |
224 | 0 | break; |
225 | 0 |
|
226 | 0 | default: |
227 | 0 | NS_WARNING("invalid sheet type argument"); |
228 | 0 | return NS_ERROR_INVALID_ARG; |
229 | 0 | } |
230 | 0 |
|
231 | 0 |
|
232 | 0 | RefPtr<StyleSheet> sheet; |
233 | 0 | nsresult rv = LoadSheet(aSheetURI, parsingMode, &sheet); |
234 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
235 | 0 | MOZ_ASSERT(sheet); |
236 | 0 | mSheets[aSheetType].AppendElement(sheet); |
237 | 0 |
|
238 | 0 | return NS_OK; |
239 | 0 | } |
240 | | |
241 | | NS_IMETHODIMP |
242 | | nsStyleSheetService::SheetRegistered(nsIURI *sheetURI, |
243 | | uint32_t aSheetType, bool *_retval) |
244 | 0 | { |
245 | 0 | NS_ENSURE_ARG(aSheetType == AGENT_SHEET || |
246 | 0 | aSheetType == USER_SHEET || |
247 | 0 | aSheetType == AUTHOR_SHEET); |
248 | 0 | NS_ENSURE_ARG_POINTER(sheetURI); |
249 | 0 | MOZ_ASSERT(_retval, "Null out param"); |
250 | 0 |
|
251 | 0 | // Check to see if we have the sheet. |
252 | 0 | *_retval = (FindSheetByURI(aSheetType, sheetURI) >= 0); |
253 | 0 |
|
254 | 0 | return NS_OK; |
255 | 0 | } |
256 | | |
257 | | static nsresult |
258 | | GetParsingMode(uint32_t aSheetType, css::SheetParsingMode* aParsingMode) |
259 | 0 | { |
260 | 0 | switch (aSheetType) { |
261 | 0 | case nsStyleSheetService::AGENT_SHEET: |
262 | 0 | *aParsingMode = css::eAgentSheetFeatures; |
263 | 0 | return NS_OK; |
264 | 0 |
|
265 | 0 | case nsStyleSheetService::USER_SHEET: |
266 | 0 | *aParsingMode = css::eUserSheetFeatures; |
267 | 0 | return NS_OK; |
268 | 0 |
|
269 | 0 | case nsStyleSheetService::AUTHOR_SHEET: |
270 | 0 | *aParsingMode = css::eAuthorSheetFeatures; |
271 | 0 | return NS_OK; |
272 | 0 |
|
273 | 0 | default: |
274 | 0 | NS_WARNING("invalid sheet type argument"); |
275 | 0 | return NS_ERROR_INVALID_ARG; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | NS_IMETHODIMP |
280 | | nsStyleSheetService::PreloadSheet(nsIURI* aSheetURI, uint32_t aSheetType, |
281 | | nsIPreloadedStyleSheet** aSheet) |
282 | 0 | { |
283 | 0 | MOZ_ASSERT(aSheet, "Null out param"); |
284 | 0 | NS_ENSURE_ARG_POINTER(aSheetURI); |
285 | 0 |
|
286 | 0 | css::SheetParsingMode parsingMode; |
287 | 0 | nsresult rv = GetParsingMode(aSheetType, &parsingMode); |
288 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
289 | 0 |
|
290 | 0 | RefPtr<PreloadedStyleSheet> sheet; |
291 | 0 | rv = PreloadedStyleSheet::Create(aSheetURI, parsingMode, |
292 | 0 | getter_AddRefs(sheet)); |
293 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
294 | 0 |
|
295 | 0 | rv = sheet->Preload(); |
296 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
297 | 0 |
|
298 | 0 | sheet.forget(aSheet); |
299 | 0 | return NS_OK; |
300 | 0 | } |
301 | | |
302 | | NS_IMETHODIMP |
303 | | nsStyleSheetService::PreloadSheetAsync(nsIURI* aSheetURI, uint32_t aSheetType, |
304 | | JSContext* aCx, |
305 | | JS::MutableHandleValue aRval) |
306 | 0 | { |
307 | 0 | NS_ENSURE_ARG_POINTER(aSheetURI); |
308 | 0 |
|
309 | 0 | css::SheetParsingMode parsingMode; |
310 | 0 | nsresult rv = GetParsingMode(aSheetType, &parsingMode); |
311 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
312 | 0 |
|
313 | 0 | nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(aCx); |
314 | 0 | NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED); |
315 | 0 |
|
316 | 0 | ErrorResult errv; |
317 | 0 | RefPtr<dom::Promise> promise = dom::Promise::Create(global, errv); |
318 | 0 | if (errv.Failed()) { |
319 | 0 | return errv.StealNSResult(); |
320 | 0 | } |
321 | 0 | |
322 | 0 | RefPtr<PreloadedStyleSheet> sheet; |
323 | 0 | rv = PreloadedStyleSheet::Create(aSheetURI, parsingMode, |
324 | 0 | getter_AddRefs(sheet)); |
325 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
326 | 0 |
|
327 | 0 | sheet->PreloadAsync(WrapNotNull(promise)); |
328 | 0 |
|
329 | 0 | if (!ToJSValue(aCx, promise, aRval)) { |
330 | 0 | return NS_ERROR_FAILURE; |
331 | 0 | } |
332 | 0 | return NS_OK; |
333 | 0 | } |
334 | | |
335 | | NS_IMETHODIMP |
336 | | nsStyleSheetService::UnregisterSheet(nsIURI *aSheetURI, uint32_t aSheetType) |
337 | 0 | { |
338 | 0 | NS_ENSURE_ARG(aSheetType == AGENT_SHEET || |
339 | 0 | aSheetType == USER_SHEET || |
340 | 0 | aSheetType == AUTHOR_SHEET); |
341 | 0 | NS_ENSURE_ARG_POINTER(aSheetURI); |
342 | 0 |
|
343 | 0 | RefPtr<StyleSheet> sheet; |
344 | 0 | int32_t foundIndex = FindSheetByURI(aSheetType, aSheetURI); |
345 | 0 | if (foundIndex >= 0) { |
346 | 0 | sheet = mSheets[aSheetType][foundIndex]; |
347 | 0 | mSheets[aSheetType].RemoveElementAt(foundIndex); |
348 | 0 | } |
349 | 0 |
|
350 | 0 | // Hold on to a copy of the registered PresShells. |
351 | 0 | nsTArray<nsCOMPtr<nsIPresShell>> toNotify(mPresShells); |
352 | 0 | for (nsIPresShell* presShell : toNotify) { |
353 | 0 | if (presShell->StyleSet()) { |
354 | 0 | if (sheet) { |
355 | 0 | presShell->NotifyStyleSheetServiceSheetRemoved(sheet, aSheetType); |
356 | 0 | } |
357 | 0 | } |
358 | 0 | } |
359 | 0 |
|
360 | 0 | if (XRE_IsParentProcess()) { |
361 | 0 | nsTArray<dom::ContentParent*> children; |
362 | 0 | dom::ContentParent::GetAll(children); |
363 | 0 |
|
364 | 0 | if (children.IsEmpty()) { |
365 | 0 | return NS_OK; |
366 | 0 | } |
367 | 0 | |
368 | 0 | mozilla::ipc::URIParams uri; |
369 | 0 | SerializeURI(aSheetURI, uri); |
370 | 0 |
|
371 | 0 | for (uint32_t i = 0; i < children.Length(); i++) { |
372 | 0 | Unused << children[i]->SendUnregisterSheet(uri, aSheetType); |
373 | 0 | } |
374 | 0 | } |
375 | 0 |
|
376 | 0 | return NS_OK; |
377 | 0 | } |
378 | | |
379 | | //static |
380 | | nsStyleSheetService * |
381 | | nsStyleSheetService::GetInstance() |
382 | 0 | { |
383 | 0 | static bool first = true; |
384 | 0 | if (first) { |
385 | 0 | // make sure at first call that it's inited |
386 | 0 | nsCOMPtr<nsIStyleSheetService> dummy = |
387 | 0 | do_GetService(NS_STYLESHEETSERVICE_CONTRACTID); |
388 | 0 | first = false; |
389 | 0 | } |
390 | 0 |
|
391 | 0 | return gInstance; |
392 | 0 | } |
393 | | |
394 | | MOZ_DEFINE_MALLOC_SIZE_OF(StyleSheetServiceMallocSizeOf) |
395 | | |
396 | | NS_IMETHODIMP |
397 | | nsStyleSheetService::CollectReports(nsIHandleReportCallback* aHandleReport, |
398 | | nsISupports* aData, bool aAnonymize) |
399 | 0 | { |
400 | 0 | MOZ_COLLECT_REPORT( |
401 | 0 | "explicit/layout/style-sheet-service", KIND_HEAP, UNITS_BYTES, |
402 | 0 | SizeOfIncludingThis(StyleSheetServiceMallocSizeOf), |
403 | 0 | "Memory used for style sheets held by the style sheet service."); |
404 | 0 |
|
405 | 0 | return NS_OK; |
406 | 0 | } |
407 | | |
408 | | size_t |
409 | | nsStyleSheetService::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
410 | 0 | { |
411 | 0 | size_t n = aMallocSizeOf(this); |
412 | 0 | for (auto& sheetArray : mSheets) { |
413 | 0 | n += sheetArray.ShallowSizeOfExcludingThis(aMallocSizeOf); |
414 | 0 | for (StyleSheet* sheet : sheetArray) { |
415 | 0 | if (sheet) { |
416 | 0 | n += sheet->SizeOfIncludingThis(aMallocSizeOf); |
417 | 0 | } |
418 | 0 | } |
419 | 0 | } |
420 | 0 | return n; |
421 | 0 | } |
422 | | |
423 | | void |
424 | | nsStyleSheetService::RegisterPresShell(nsIPresShell* aPresShell) |
425 | 0 | { |
426 | 0 | MOZ_ASSERT(!mPresShells.Contains(aPresShell)); |
427 | 0 | mPresShells.AppendElement(aPresShell); |
428 | 0 | } |
429 | | |
430 | | void |
431 | | nsStyleSheetService::UnregisterPresShell(nsIPresShell* aPresShell) |
432 | 0 | { |
433 | 0 | MOZ_ASSERT(mPresShells.Contains(aPresShell)); |
434 | 0 | mPresShells.RemoveElement(aPresShell); |
435 | 0 | } |