/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "ScopedPrefs.h" #include "nsIURI.h" #include "nsIEffectiveTLDService.h" #include "mozilla/Components.h" #include "mozilla/Logging.h" #include "mozilla/net/UrlClassifierCommon.h" #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/WindowGlobalParent.h" #include "nsIChannel.h" static mozilla::LazyLogModule gScopedPrefsLog("ScopedPrefs"); namespace mozilla { // =========== // ScopedPrefs // =========== NS_IMPL_ISUPPORTS(ScopedPrefs, nsIScopedPrefs); // static bool ScopedPrefs::BoolPrefScoped(const nsIScopedPrefs::Pref aPref, nsIChannel* aChannel) { NS_ENSURE_TRUE(aChannel, false); bool value = false; if (NS_SUCCEEDED(GetBoolPrefScopedInternal(aPref, aChannel, &value))) { return value; } DebugOnly rv = GetBoolPrefFallback(aPref, NS_UsePrivateBrowsing(aChannel), &value); MOZ_ASSERT(NS_SUCCEEDED(rv)); return value; } // static nsresult ScopedPrefs::GetBoolPrefScopedInternal( const nsIScopedPrefs::Pref aPref, nsIChannel* aChannel, bool* aValue) { NS_ENSURE_ARG_POINTER(aChannel); NS_ENSURE_ARG_POINTER(aValue); // retrieve ScopedPref object for this channel nsCOMPtr loadInfo = aChannel->LoadInfo(); RefPtr bc; nsresult rv = loadInfo->GetBrowsingContext(getter_AddRefs(bc)); if (NS_FAILED(rv)) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetBoolPrefScopedInternal: Failed to get BrowsingContext")); return rv; } if (!bc) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetBoolPrefScopedInternal: BrowsingContext is null")); return NS_ERROR_FAILURE; } RefPtr cbc = bc->Canonical(); nsCOMPtr scopedPrefs = cbc->GetScopedPrefs(); if (!scopedPrefs) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetBoolPrefScopedInternal: ScopedPrefs is null")); return NS_ERROR_FAILURE; } MOZ_TRY(scopedPrefs->GetBoolPrefScoped(aPref, bc, aValue)); return NS_OK; } // static nsresult ScopedPrefs::GetBoolPrefFallback(const nsIScopedPrefs::Pref aPref, bool aIsPrivate, bool* aValue) { switch (aPref) { case nsIScopedPrefs::PRIVACY_TRACKINGPROTECTION_ENABLED: // In general the `ScopedPrefs` implementation should stay generic. // However, we are inheriting the behavior of the tracking-protection pref // differentiate between private and non-private mode. *aValue = StaticPrefs::privacy_trackingprotection_enabled() || (aIsPrivate && StaticPrefs::privacy_trackingprotection_pbmode_enabled()); MOZ_LOG(gScopedPrefsLog, LogLevel::Debug, ("GetBoolPrefFallback: pref=%d, isPrivate=%d, value=%d", aPref, aIsPrivate, *aValue)); return NS_OK; case nsIScopedPrefs::NUM_SCOPED_BOOL_PREFS: // not a valid value return NS_ERROR_FAILURE; } return NS_ERROR_FAILURE; } // static nsresult ScopedPrefs::GetTopSite(nsIChannel* aChannel, nsACString& aOutSite) { nsCOMPtr uri; nsresult rv = net::UrlClassifierCommon::GetTopWindowURI(aChannel, getter_AddRefs(uri)); if (NS_FAILED(rv)) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetTopSite: Failed to get top window URI")); return rv; } nsCOMPtr etld = components::EffectiveTLD::Service(); if (!etld) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetTopSite: EffectiveTLDService not available")); return NS_ERROR_NOT_AVAILABLE; } rv = etld->GetSite(uri, aOutSite); if (NS_FAILED(rv)) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetTopSite: Failed to get site from URI")); return rv; } return NS_OK; } // static nsresult ScopedPrefs::GetTopSite(dom::BrowsingContext* aBc, nsACString& aOutSite) { NS_ENSURE_ARG_POINTER(aBc); dom::CanonicalBrowsingContext* top = aBc->Canonical()->Top(); dom::WindowGlobalParent* wgp = top->GetCurrentWindowGlobal(); if (!wgp) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetTopSite: WindowGlobalParent is null")); return NS_ERROR_FAILURE; } nsIURI* uri = wgp->GetDocumentURI(); if (!uri) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetTopSite: Document URI is null")); return NS_ERROR_FAILURE; } nsCOMPtr etld = components::EffectiveTLD::Service(); if (!etld) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetTopSite: EffectiveTLDService not available")); return NS_ERROR_NOT_AVAILABLE; } nsresult rv = etld->GetSite(uri, aOutSite); if (NS_FAILED(rv)) { MOZ_LOG(gScopedPrefsLog, LogLevel::Warning, ("GetTopSite: Failed to get site from URI")); return rv; } return NS_OK; } // -------------- // nsIScopedPrefs // -------------- nsresult ScopedPrefs::GetBoolPrefScoped(const nsIScopedPrefs::Pref pref, dom::BrowsingContext* aBc, bool* aValue) { MOZ_ASSERT(XRE_IsParentProcess()); NS_ENSURE_ARG_POINTER(aBc); if (pref >= nsIScopedPrefs::NUM_SCOPED_BOOL_PREFS) { return NS_ERROR_INVALID_ARG; } if (!mBoolPrefValue[pref].IsEmpty()) { nsAutoCString site; if (NS_SUCCEEDED(ScopedPrefs::GetTopSite(aBc, site))) { if (auto siteConfig = mBoolPrefValue[pref].Lookup(site)) { *aValue = siteConfig.Data(); MOZ_LOG(gScopedPrefsLog, LogLevel::Debug, ("GetBoolPrefScoped: pref=%d, site=%s, value=%d", pref, site.get(), *aValue)); return NS_OK; } } } bool isPrivate = false; aBc->GetUsePrivateBrowsing(&isPrivate); // fallback to global preferences, when there is no override return GetBoolPrefFallback(pref, isPrivate, aValue); } nsresult ScopedPrefs::SetBoolPrefScoped(const nsIScopedPrefs::Pref pref, dom::BrowsingContext* aBc, bool aValue) { MOZ_ASSERT(XRE_IsParentProcess()); NS_ENSURE_ARG_POINTER(aBc); if (pref >= nsIScopedPrefs::NUM_SCOPED_BOOL_PREFS) { return NS_ERROR_INVALID_ARG; } nsAutoCString site; if (NS_WARN_IF(NS_FAILED(ScopedPrefs::GetTopSite(aBc, site)))) { return NS_ERROR_FAILURE; } mBoolPrefValue[pref].InsertOrUpdate(site, aValue); MOZ_LOG(gScopedPrefsLog, LogLevel::Debug, ("SetBoolPrefScoped: pref=%d, site=%s, value=%d", pref, site.get(), aValue)); return NS_OK; } nsresult ScopedPrefs::ClearScoped() { for (auto& i : mBoolPrefValue) { i.Clear(); } return NS_OK; } nsresult ScopedPrefs::ClearScopedPref(const nsIScopedPrefs::Pref pref) { MOZ_LOG(gScopedPrefsLog, LogLevel::Debug, ("ClearScopedPref: pref=%i", pref)); if (pref >= nsIScopedPrefs::NUM_SCOPED_BOOL_PREFS) { return NS_ERROR_INVALID_ARG; } mBoolPrefValue[pref].Clear(); return NS_OK; } nsresult ScopedPrefs::ClearScopedByHost(const nsACString& aHost) { MOZ_LOG(gScopedPrefsLog, LogLevel::Debug, ("ClearScopedByHost: host=%s", PromiseFlatCString(aHost).get())); for (auto& pref : mBoolPrefValue) { pref.Remove(aHost); } return NS_OK; } nsresult ScopedPrefs::ClearScopedPrefByHost(const nsIScopedPrefs::Pref pref, const nsACString& aHost) { MOZ_LOG(gScopedPrefsLog, LogLevel::Debug, ("ClearScopedPrefByHost: pref=%d, host=%s", pref, PromiseFlatCString(aHost).get())); if (pref >= nsIScopedPrefs::NUM_SCOPED_BOOL_PREFS) { return NS_ERROR_INVALID_ARG; } mBoolPrefValue[pref].Remove(aHost); return NS_OK; } } // namespace mozilla