michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsLayoutStylesheetCache.h" michael@0: michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/css/Loader.h" michael@0: #include "nsIFile.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIXULRuntime.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static bool sNumberControlEnabled; michael@0: michael@0: #define NUMBER_CONTROL_PREF "dom.forms.number" michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: nsLayoutStylesheetCache, nsIObserver, nsIMemoryReporter) michael@0: michael@0: nsresult michael@0: nsLayoutStylesheetCache::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (!strcmp(aTopic, "profile-before-change")) { michael@0: mUserContentSheet = nullptr; michael@0: mUserChromeSheet = nullptr; michael@0: } michael@0: else if (!strcmp(aTopic, "profile-do-change")) { michael@0: InitFromProfile(); michael@0: } michael@0: else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 || michael@0: strcmp(aTopic, "chrome-flush-caches") == 0) { michael@0: mScrollbarsSheet = nullptr; michael@0: mFormsSheet = nullptr; michael@0: mNumberControlSheet = nullptr; michael@0: } michael@0: else { michael@0: NS_NOTREACHED("Unexpected observer topic."); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::ScrollbarsSheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: if (!gStyleCache->mScrollbarsSheet) { michael@0: nsCOMPtr sheetURI; michael@0: NS_NewURI(getter_AddRefs(sheetURI), michael@0: NS_LITERAL_CSTRING("chrome://global/skin/scrollbars.css")); michael@0: michael@0: // Scrollbars don't need access to unsafe rules michael@0: if (sheetURI) michael@0: LoadSheet(sheetURI, gStyleCache->mScrollbarsSheet, false); michael@0: NS_ASSERTION(gStyleCache->mScrollbarsSheet, "Could not load scrollbars.css."); michael@0: } michael@0: michael@0: return gStyleCache->mScrollbarsSheet; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::FormsSheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: if (!gStyleCache->mFormsSheet) { michael@0: nsCOMPtr sheetURI; michael@0: NS_NewURI(getter_AddRefs(sheetURI), michael@0: NS_LITERAL_CSTRING("resource://gre-resources/forms.css")); michael@0: michael@0: // forms.css needs access to unsafe rules michael@0: if (sheetURI) michael@0: LoadSheet(sheetURI, gStyleCache->mFormsSheet, true); michael@0: michael@0: NS_ASSERTION(gStyleCache->mFormsSheet, "Could not load forms.css."); michael@0: } michael@0: michael@0: return gStyleCache->mFormsSheet; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::NumberControlSheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: if (!sNumberControlEnabled) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!gStyleCache->mNumberControlSheet) { michael@0: nsCOMPtr sheetURI; michael@0: NS_NewURI(getter_AddRefs(sheetURI), michael@0: NS_LITERAL_CSTRING("resource://gre-resources/number-control.css")); michael@0: michael@0: if (sheetURI) michael@0: LoadSheet(sheetURI, gStyleCache->mNumberControlSheet, false); michael@0: michael@0: NS_ASSERTION(gStyleCache->mNumberControlSheet, "Could not load number-control.css"); michael@0: } michael@0: michael@0: return gStyleCache->mNumberControlSheet; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::UserContentSheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: return gStyleCache->mUserContentSheet; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::UserChromeSheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: return gStyleCache->mUserChromeSheet; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::UASheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: return gStyleCache->mUASheet; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::QuirkSheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: return gStyleCache->mQuirkSheet; michael@0: } michael@0: michael@0: nsCSSStyleSheet* michael@0: nsLayoutStylesheetCache::FullScreenOverrideSheet() michael@0: { michael@0: EnsureGlobal(); michael@0: if (!gStyleCache) michael@0: return nullptr; michael@0: michael@0: return gStyleCache->mFullScreenOverrideSheet; michael@0: } michael@0: michael@0: void michael@0: nsLayoutStylesheetCache::Shutdown() michael@0: { michael@0: NS_IF_RELEASE(gCSSLoader); michael@0: NS_IF_RELEASE(gStyleCache); michael@0: } michael@0: michael@0: MOZ_DEFINE_MALLOC_SIZE_OF(LayoutStylesheetCacheMallocSizeOf) michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutStylesheetCache::CollectReports(nsIHandleReportCallback* aHandleReport, michael@0: nsISupports* aData) michael@0: { michael@0: return MOZ_COLLECT_REPORT( michael@0: "explicit/layout/style-sheet-cache", KIND_HEAP, UNITS_BYTES, michael@0: SizeOfIncludingThis(LayoutStylesheetCacheMallocSizeOf), michael@0: "Memory used for some built-in style sheets."); michael@0: } michael@0: michael@0: michael@0: size_t michael@0: nsLayoutStylesheetCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: michael@0: #define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0; michael@0: michael@0: MEASURE(mScrollbarsSheet); michael@0: MEASURE(mFormsSheet); michael@0: MEASURE(mNumberControlSheet); michael@0: MEASURE(mUserContentSheet); michael@0: MEASURE(mUserChromeSheet); michael@0: MEASURE(mUASheet); michael@0: MEASURE(mQuirkSheet); michael@0: MEASURE(mFullScreenOverrideSheet); michael@0: michael@0: // Measurement of the following members may be added later if DMD finds it is michael@0: // worthwhile: michael@0: // - gCSSLoader michael@0: michael@0: return n; michael@0: } michael@0: michael@0: nsLayoutStylesheetCache::nsLayoutStylesheetCache() michael@0: { michael@0: nsCOMPtr obsSvc = michael@0: mozilla::services::GetObserverService(); michael@0: NS_ASSERTION(obsSvc, "No global observer service?"); michael@0: michael@0: if (obsSvc) { michael@0: obsSvc->AddObserver(this, "profile-before-change", false); michael@0: obsSvc->AddObserver(this, "profile-do-change", false); michael@0: obsSvc->AddObserver(this, "chrome-flush-skin-caches", false); michael@0: obsSvc->AddObserver(this, "chrome-flush-caches", false); michael@0: } michael@0: michael@0: InitFromProfile(); michael@0: michael@0: // And make sure that we load our UA sheets. No need to do this michael@0: // per-profile, since they're profile-invariant. michael@0: nsCOMPtr uri; michael@0: NS_NewURI(getter_AddRefs(uri), "resource://gre-resources/ua.css"); michael@0: if (uri) { michael@0: LoadSheet(uri, mUASheet, true); michael@0: } michael@0: NS_ASSERTION(mUASheet, "Could not load ua.css"); michael@0: michael@0: NS_NewURI(getter_AddRefs(uri), "resource://gre-resources/quirk.css"); michael@0: if (uri) { michael@0: LoadSheet(uri, mQuirkSheet, true); michael@0: } michael@0: NS_ASSERTION(mQuirkSheet, "Could not load quirk.css"); michael@0: michael@0: NS_NewURI(getter_AddRefs(uri), "resource://gre-resources/full-screen-override.css"); michael@0: if (uri) { michael@0: LoadSheet(uri, mFullScreenOverrideSheet, true); michael@0: } michael@0: NS_ASSERTION(mFullScreenOverrideSheet, "Could not load full-screen-override.css"); michael@0: } michael@0: michael@0: nsLayoutStylesheetCache::~nsLayoutStylesheetCache() michael@0: { michael@0: mozilla::UnregisterWeakMemoryReporter(this); michael@0: gStyleCache = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsLayoutStylesheetCache::InitMemoryReporter() michael@0: { michael@0: mozilla::RegisterWeakMemoryReporter(this); michael@0: } michael@0: michael@0: void michael@0: nsLayoutStylesheetCache::EnsureGlobal() michael@0: { michael@0: if (gStyleCache) return; michael@0: michael@0: gStyleCache = new nsLayoutStylesheetCache(); michael@0: if (!gStyleCache) return; michael@0: michael@0: NS_ADDREF(gStyleCache); michael@0: michael@0: gStyleCache->InitMemoryReporter(); michael@0: michael@0: Preferences::AddBoolVarCache(&sNumberControlEnabled, NUMBER_CONTROL_PREF, michael@0: true); michael@0: } michael@0: michael@0: void michael@0: nsLayoutStylesheetCache::InitFromProfile() michael@0: { michael@0: nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1"); michael@0: if (appInfo) { michael@0: bool inSafeMode = false; michael@0: appInfo->GetInSafeMode(&inSafeMode); michael@0: if (inSafeMode) michael@0: return; michael@0: } michael@0: nsCOMPtr contentFile; michael@0: nsCOMPtr chromeFile; michael@0: michael@0: NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, michael@0: getter_AddRefs(contentFile)); michael@0: if (!contentFile) { michael@0: // if we don't have a profile yet, that's OK! michael@0: return; michael@0: } michael@0: michael@0: contentFile->Clone(getter_AddRefs(chromeFile)); michael@0: if (!chromeFile) return; michael@0: michael@0: contentFile->Append(NS_LITERAL_STRING("userContent.css")); michael@0: chromeFile->Append(NS_LITERAL_STRING("userChrome.css")); michael@0: michael@0: LoadSheetFile(contentFile, mUserContentSheet); michael@0: LoadSheetFile(chromeFile, mUserChromeSheet); michael@0: } michael@0: michael@0: void michael@0: nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile, nsRefPtr &aSheet) michael@0: { michael@0: bool exists = false; michael@0: aFile->Exists(&exists); michael@0: michael@0: if (!exists) return; michael@0: michael@0: nsCOMPtr uri; michael@0: NS_NewFileURI(getter_AddRefs(uri), aFile); michael@0: michael@0: LoadSheet(uri, aSheet, false); michael@0: } michael@0: michael@0: void michael@0: nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI, michael@0: nsRefPtr &aSheet, michael@0: bool aEnableUnsafeRules) michael@0: { michael@0: if (!aURI) { michael@0: NS_ERROR("Null URI. Out of memory?"); michael@0: return; michael@0: } michael@0: michael@0: if (!gCSSLoader) { michael@0: gCSSLoader = new mozilla::css::Loader(); michael@0: NS_IF_ADDREF(gCSSLoader); michael@0: } michael@0: michael@0: if (gCSSLoader) { michael@0: gCSSLoader->LoadSheetSync(aURI, aEnableUnsafeRules, true, michael@0: getter_AddRefs(aSheet)); michael@0: } michael@0: } michael@0: michael@0: nsLayoutStylesheetCache* michael@0: nsLayoutStylesheetCache::gStyleCache = nullptr; michael@0: michael@0: mozilla::css::Loader* michael@0: nsLayoutStylesheetCache::gCSSLoader = nullptr;