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/. */
7 /* implementation of interface for managing user and user-agent style sheets */
9 #include "nsStyleSheetService.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/PreloadedStyleSheet.h"
12 #include "mozilla/PresShell.h"
13 #include "mozilla/PresShellInlines.h"
14 #include "mozilla/StyleSheet.h"
15 #include "mozilla/StyleSheetInlines.h"
16 #include "mozilla/Unused.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"
23 #include "nsISupportsPrimitives.h"
24 #include "nsISimpleEnumerator.h"
25 #include "nsNetUtil.h"
26 #include "nsIConsoleService.h"
27 #include "nsLayoutStatics.h"
28 #include "nsLayoutUtils.h"
30 using namespace mozilla
;
32 nsStyleSheetService
* nsStyleSheetService::gInstance
= nullptr;
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");
42 nsLayoutStatics::AddRef();
45 nsStyleSheetService::~nsStyleSheetService() {
46 UnregisterWeakMemoryReporter(this);
48 if (gInstance
== this) {
51 nsLayoutStatics::Release();
54 NS_IMPL_ISUPPORTS(nsStyleSheetService
, nsIStyleSheetService
, nsIMemoryReporter
)
56 static bool SheetHasURI(StyleSheet
* aSheet
, nsIURI
* aSheetURI
) {
57 MOZ_ASSERT(aSheetURI
);
60 nsIURI
* uri
= aSheet
->GetSheetURI();
61 return uri
&& NS_SUCCEEDED(uri
->Equals(aSheetURI
, &result
)) && result
;
64 int32_t nsStyleSheetService::FindSheetByURI(uint32_t aSheetType
,
66 SheetArray
& sheets
= mSheets
[aSheetType
];
67 for (int32_t i
= sheets
.Length() - 1; i
>= 0; i
--) {
68 if (SheetHasURI(sheets
[i
], aSheetURI
)) {
76 nsresult
nsStyleSheetService::Init() {
77 // Child processes get their style sheets from the ContentParent.
78 if (XRE_IsContentProcess()) {
82 RegisterWeakMemoryReporter(this);
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.
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
);
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
);
119 if (XRE_IsParentProcess()) {
120 nsTArray
<dom::ContentParent
*> children
;
121 dom::ContentParent::GetAll(children
);
123 if (children
.IsEmpty()) {
127 for (uint32_t i
= 0; i
< children
.Length(); i
++) {
128 Unused
<< children
[i
]->SendLoadAndRegisterSheet(aSheetURI
, aSheetType
);
135 nsresult
nsStyleSheetService::LoadAndRegisterSheetInternal(
136 nsIURI
* aSheetURI
, uint32_t aSheetType
) {
137 NS_ENSURE_ARG_POINTER(aSheetURI
);
139 css::SheetParsingMode parsingMode
;
140 switch (aSheetType
) {
142 parsingMode
= css::eAgentSheetFeatures
;
146 parsingMode
= css::eUserSheetFeatures
;
150 parsingMode
= css::eAuthorSheetFeatures
;
154 NS_WARNING("invalid sheet type argument");
155 return NS_ERROR_INVALID_ARG
;
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();
164 mSheets
[aSheetType
].AppendElement(result
.unwrap());
169 nsStyleSheetService::SheetRegistered(nsIURI
* sheetURI
, uint32_t aSheetType
,
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");
176 // Check to see if we have the sheet.
177 *_retval
= (FindSheetByURI(aSheetType
, sheetURI
) >= 0);
182 static nsresult
GetParsingMode(uint32_t aSheetType
,
183 css::SheetParsingMode
* aParsingMode
) {
184 switch (aSheetType
) {
185 case nsStyleSheetService::AGENT_SHEET
:
186 *aParsingMode
= css::eAgentSheetFeatures
;
189 case nsStyleSheetService::USER_SHEET
:
190 *aParsingMode
= css::eUserSheetFeatures
;
193 case nsStyleSheetService::AUTHOR_SHEET
:
194 *aParsingMode
= css::eAuthorSheetFeatures
;
198 NS_WARNING("invalid sheet type argument");
199 return NS_ERROR_INVALID_ARG
;
204 nsStyleSheetService::PreloadSheet(nsIURI
* aSheetURI
, uint32_t aSheetType
,
205 nsIPreloadedStyleSheet
** aSheet
) {
206 MOZ_ASSERT(aSheet
, "Null out param");
207 NS_ENSURE_ARG_POINTER(aSheetURI
);
209 css::SheetParsingMode parsingMode
;
210 nsresult rv
= GetParsingMode(aSheetType
, &parsingMode
);
211 NS_ENSURE_SUCCESS(rv
, rv
);
213 auto sheet
= MakeRefPtr
<PreloadedStyleSheet
>(aSheetURI
, parsingMode
);
214 NS_ENSURE_SUCCESS(rv
, rv
);
216 rv
= sheet
->Preload();
217 NS_ENSURE_SUCCESS(rv
, rv
);
219 sheet
.forget(aSheet
);
224 nsStyleSheetService::PreloadSheetAsync(nsIURI
* aSheetURI
, uint32_t aSheetType
,
226 JS::MutableHandle
<JS::Value
> aRval
) {
227 NS_ENSURE_ARG_POINTER(aSheetURI
);
229 css::SheetParsingMode parsingMode
;
230 nsresult rv
= GetParsingMode(aSheetType
, &parsingMode
);
231 NS_ENSURE_SUCCESS(rv
, rv
);
233 nsCOMPtr
<nsIGlobalObject
> global
= xpc::CurrentNativeGlobal(aCx
);
234 NS_ENSURE_TRUE(global
, NS_ERROR_UNEXPECTED
);
237 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(global
, errv
);
239 return errv
.StealNSResult();
242 auto sheet
= MakeRefPtr
<PreloadedStyleSheet
>(aSheetURI
, parsingMode
);
243 sheet
->PreloadAsync(WrapNotNull(promise
));
245 if (!ToJSValue(aCx
, promise
, aRval
)) {
246 return NS_ERROR_FAILURE
;
252 nsStyleSheetService::UnregisterSheet(nsIURI
* aSheetURI
, uint32_t aSheetType
) {
253 NS_ENSURE_ARG(aSheetType
== AGENT_SHEET
|| aSheetType
== USER_SHEET
||
254 aSheetType
== AUTHOR_SHEET
);
255 NS_ENSURE_ARG_POINTER(aSheetURI
);
257 RefPtr
<StyleSheet
> sheet
;
258 int32_t foundIndex
= FindSheetByURI(aSheetType
, aSheetURI
);
259 if (foundIndex
>= 0) {
260 sheet
= mSheets
[aSheetType
][foundIndex
];
261 mSheets
[aSheetType
].RemoveElementAt(foundIndex
);
264 // Hold on to a copy of the registered PresShells.
265 for (PresShell
* presShell
: mPresShells
.Clone()) {
266 if (presShell
->StyleSet()) {
268 presShell
->NotifyStyleSheetServiceSheetRemoved(sheet
, aSheetType
);
273 if (XRE_IsParentProcess()) {
274 nsTArray
<dom::ContentParent
*> children
;
275 dom::ContentParent::GetAll(children
);
277 if (children
.IsEmpty()) {
281 for (uint32_t i
= 0; i
< children
.Length(); i
++) {
282 Unused
<< children
[i
]->SendUnregisterSheet(aSheetURI
, aSheetType
);
290 nsStyleSheetService
* nsStyleSheetService::GetInstance() {
291 static bool first
= true;
293 // make sure at first call that it's inited
294 nsCOMPtr
<nsIStyleSheetService
> dummy
=
295 do_GetService(NS_STYLESHEETSERVICE_CONTRACTID
);
302 MOZ_DEFINE_MALLOC_SIZE_OF(StyleSheetServiceMallocSizeOf
)
305 nsStyleSheetService::CollectReports(nsIHandleReportCallback
* aHandleReport
,
306 nsISupports
* aData
, bool aAnonymize
) {
308 "explicit/layout/style-sheet-service", KIND_HEAP
, UNITS_BYTES
,
309 SizeOfIncludingThis(StyleSheetServiceMallocSizeOf
),
310 "Memory used for style sheets held by the style sheet service.");
315 size_t nsStyleSheetService::SizeOfIncludingThis(
316 mozilla::MallocSizeOf aMallocSizeOf
) const {
317 size_t n
= aMallocSizeOf(this);
318 for (auto& sheetArray
: mSheets
) {
319 n
+= sheetArray
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
320 for (StyleSheet
* sheet
: sheetArray
) {
322 n
+= sheet
->SizeOfIncludingThis(aMallocSizeOf
);
329 void nsStyleSheetService::RegisterPresShell(PresShell
* aPresShell
) {
330 MOZ_ASSERT(!mPresShells
.Contains(aPresShell
));
331 mPresShells
.AppendElement(aPresShell
);
334 void nsStyleSheetService::UnregisterPresShell(PresShell
* aPresShell
) {
335 MOZ_ASSERT(mPresShells
.Contains(aPresShell
));
336 mPresShells
.RemoveElement(aPresShell
);