Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / layout / base / nsStyleSheetService.cpp
blob1c1a58f5548cf061f0df2657bff6586ff48696cf
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"
21 #include "nsIURI.h"
22 #include "nsCOMPtr.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");
39 if (!gInstance) {
40 gInstance = this;
42 nsLayoutStatics::AddRef();
45 nsStyleSheetService::~nsStyleSheetService() {
46 UnregisterWeakMemoryReporter(this);
48 if (gInstance == this) {
49 gInstance = nullptr;
51 nsLayoutStatics::Release();
54 NS_IMPL_ISUPPORTS(nsStyleSheetService, nsIStyleSheetService, nsIMemoryReporter)
56 static bool SheetHasURI(StyleSheet* aSheet, nsIURI* aSheetURI) {
57 MOZ_ASSERT(aSheetURI);
59 bool result;
60 nsIURI* uri = aSheet->GetSheetURI();
61 return uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &result)) && result;
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 (SheetHasURI(sheets[i], aSheetURI)) {
69 return i;
73 return -1;
76 nsresult nsStyleSheetService::Init() {
77 // Child processes get their style sheets from the ContentParent.
78 if (XRE_IsContentProcess()) {
79 return NS_OK;
82 RegisterWeakMemoryReporter(this);
84 return NS_OK;
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);
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()) {
124 return rv;
127 for (uint32_t i = 0; i < children.Length(); i++) {
128 Unused << children[i]->SendLoadAndRegisterSheet(aSheetURI, aSheetType);
132 return rv;
135 nsresult nsStyleSheetService::LoadAndRegisterSheetInternal(
136 nsIURI* aSheetURI, uint32_t aSheetType) {
137 NS_ENSURE_ARG_POINTER(aSheetURI);
139 css::SheetParsingMode parsingMode;
140 switch (aSheetType) {
141 case AGENT_SHEET:
142 parsingMode = css::eAgentSheetFeatures;
143 break;
145 case USER_SHEET:
146 parsingMode = css::eUserSheetFeatures;
147 break;
149 case AUTHOR_SHEET:
150 parsingMode = css::eAuthorSheetFeatures;
151 break;
153 default:
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());
165 return NS_OK;
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");
176 // Check to see if we have the sheet.
177 *_retval = (FindSheetByURI(aSheetType, sheetURI) >= 0);
179 return NS_OK;
182 static nsresult GetParsingMode(uint32_t aSheetType,
183 css::SheetParsingMode* aParsingMode) {
184 switch (aSheetType) {
185 case nsStyleSheetService::AGENT_SHEET:
186 *aParsingMode = css::eAgentSheetFeatures;
187 return NS_OK;
189 case nsStyleSheetService::USER_SHEET:
190 *aParsingMode = css::eUserSheetFeatures;
191 return NS_OK;
193 case nsStyleSheetService::AUTHOR_SHEET:
194 *aParsingMode = css::eAuthorSheetFeatures;
195 return NS_OK;
197 default:
198 NS_WARNING("invalid sheet type argument");
199 return NS_ERROR_INVALID_ARG;
203 NS_IMETHODIMP
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);
220 return NS_OK;
223 NS_IMETHODIMP
224 nsStyleSheetService::PreloadSheetAsync(nsIURI* aSheetURI, uint32_t aSheetType,
225 JSContext* aCx,
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);
236 ErrorResult errv;
237 RefPtr<dom::Promise> promise = dom::Promise::Create(global, errv);
238 if (errv.Failed()) {
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;
248 return NS_OK;
251 NS_IMETHODIMP
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()) {
267 if (sheet) {
268 presShell->NotifyStyleSheetServiceSheetRemoved(sheet, aSheetType);
273 if (XRE_IsParentProcess()) {
274 nsTArray<dom::ContentParent*> children;
275 dom::ContentParent::GetAll(children);
277 if (children.IsEmpty()) {
278 return NS_OK;
281 for (uint32_t i = 0; i < children.Length(); i++) {
282 Unused << children[i]->SendUnregisterSheet(aSheetURI, aSheetType);
286 return NS_OK;
289 // static
290 nsStyleSheetService* nsStyleSheetService::GetInstance() {
291 static bool first = true;
292 if (first) {
293 // make sure at first call that it's inited
294 nsCOMPtr<nsIStyleSheetService> dummy =
295 do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
296 first = false;
299 return gInstance;
302 MOZ_DEFINE_MALLOC_SIZE_OF(StyleSheetServiceMallocSizeOf)
304 NS_IMETHODIMP
305 nsStyleSheetService::CollectReports(nsIHandleReportCallback* aHandleReport,
306 nsISupports* aData, bool aAnonymize) {
307 MOZ_COLLECT_REPORT(
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.");
312 return NS_OK;
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) {
321 if (sheet) {
322 n += sheet->SizeOfIncludingThis(aMallocSizeOf);
326 return n;
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);