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