/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 http://mozilla.org/MPL/2.0/. */
void Navigator::Invalidate() { // Don't clear mWindow here so we know we've got a non-null mWindow // until we're unlinked.
mPlugins = nullptr;
mPermissions = nullptr;
if (mStorageManager) {
mStorageManager->Shutdown();
mStorageManager = nullptr;
}
// If there is a page transition, make sure delete the geolocation object. if (mGeolocation) {
mGeolocation->Shutdown();
mGeolocation = nullptr;
}
if (mBatteryManager) {
mBatteryManager->Shutdown();
mBatteryManager = nullptr;
}
mBatteryPromise = nullptr;
if (mConnection) {
mConnection->Shutdown();
mConnection = nullptr;
}
mMediaDevices = nullptr;
mServiceWorkerContainer = nullptr;
if (mMediaKeySystemAccessManager) {
mMediaKeySystemAccessManager->Shutdown();
mMediaKeySystemAccessManager = nullptr;
}
if (mGamepadServiceTest) {
mGamepadServiceTest->Shutdown();
mGamepadServiceTest = nullptr;
}
mVRGetDisplaysPromises.Clear();
if (mVRServiceTest) {
mVRServiceTest->Shutdown();
mVRServiceTest = nullptr;
}
if (mXRSystem) {
mXRSystem->Shutdown();
mXRSystem = nullptr;
}
mMediaCapabilities = nullptr;
if (mMediaSession) {
mMediaSession->Shutdown();
mMediaSession = nullptr;
}
mAddonManager = nullptr;
mWebGpu = nullptr;
if (mLocks) { // Unloading a page does not immediately destruct the lock manager actor, // but we want to abort the lock requests as soon as possible. Explicitly // call Shutdown() to do that.
mLocks->Shutdown();
mLocks = nullptr;
}
/** * Returns the value of Accept-Languages (HTTP header) as a nsTArray of * languages. The value is set in the preference by the user ("Content * Languages"). * * "en", "en-US" and "i-cherokee" and "" are valid languages tokens. * * If there is no valid language, the value of getWebExposedLocales is * used to ensure that locale spoofing is honored and to reduce * fingerprinting. * * See RFC 7231, Section 9.7 "Browser Fingerprinting" and * RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers" * for more detail.
*/ /* static */ void Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages) {
MOZ_ASSERT(NS_IsMainThread());
aLanguages.Clear();
// E.g. "de-de, en-us,en".
nsAutoString acceptLang;
Preferences::GetLocalizedString("intl.accept_languages", acceptLang);
// Split values on commas. for (nsDependentSubstring lang :
nsCharSeparatedTokenizer(acceptLang, ',').ToRange()) { // Replace "_" with "-" to avoid POSIX/Windows "en_US" notation. // NOTE: we should probably rely on the pref being set correctly. if (lang.Length() > 2 && lang[2] == char16_t('_')) {
lang.Replace(2, 1, char16_t('-'));
}
// Use uppercase for country part, e.g. "en-US", not "en-us", see BCP47 // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe". // NOTE: we should probably rely on the pref being set correctly. if (lang.Length() > 2) {
int32_t pos = 0; bool first = true; for (const nsAString& code :
nsCharSeparatedTokenizer(lang, '-').ToRange()) { if (code.Length() == 2 && !first) {
nsAutoString upper(code);
ToUpperCase(upper);
lang.Replace(pos, code.Length(), upper);
}
pos += code.Length() + 1; // 1 is the separator
first = false;
}
}
/** * Returns the first language from GetAcceptLanguages. * * Full details above in GetAcceptLanguages.
*/ void Navigator::GetLanguage(nsAString& aLanguage) {
nsTArray<nsString> languages;
GetLanguages(languages);
MOZ_ASSERT(languages.Length() >= 1);
aLanguage.Assign(languages[0]);
}
// The returned value is cached by the binding code. The window listens to the // accept languages change and will clear the cache when needed. It has to // take care of dispatching the DOM event already and the invalidation and the // event has to be timed correctly.
}
void Navigator::GetPlatform(nsAString& aPlatform, CallerType aCallerType,
ErrorResult& aRv) const { if (mWindow) {
BrowsingContext* bc = mWindow->GetBrowsingContext();
nsString customPlatform; if (bc) {
bc->GetCustomPlatform(customPlatform);
if (!customPlatform.IsEmpty()) {
aPlatform = customPlatform; return;
}
}
}
void Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType,
ErrorResult& aRv) const { if (aCallerType != CallerType::System) { // If fingerprinting resistance is on, we will spoof this value. See // nsRFPService.h for details about spoofed values. if (nsContentUtils::ShouldResistFingerprinting(GetDocShell(),
RFPTarget::NavigatorOscpu)) {
aOSCPU.AssignLiteral(SPOOFED_OSCPU); return;
}
if (!mStorageManager) {
mStorageManager = new StorageManager(mWindow->AsGlobal());
}
return mStorageManager;
}
bool Navigator::CookieEnabled() { // Check whether an exception overrides the global cookie behavior // Note that the code for getting the URI here matches that in // nsHTMLDocument::SetCookie. if (!mWindow || !mWindow->GetDocShell()) { return nsICookieManager::GetCookieBehavior(false) !=
nsICookieService::BEHAVIOR_REJECT;
}
nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); if (!doc) { return cookieEnabled;
}
uint32_t rejectedReason = 0; bool granted = false;
nsresult rv = doc->NodePrincipal()->HasFirstpartyStorageAccess(
mWindow, &rejectedReason, &granted); if (NS_FAILED(rv)) { // Not a content, so technically can't set cookies, but let's // just return the default value. return cookieEnabled;
}
// We should return true if the cookie is partitioned because the cookie is // still available in this case. if (!granted &&
StoragePartitioningEnabled(rejectedReason, doc->CookieJarSettings())) {
granted = true;
}
bool Navigator::OnLine() { if (mWindow) { // Check if this tab is set to be offline.
BrowsingContext* bc = mWindow->GetBrowsingContext(); if (bc && bc->Top()->GetForceOffline()) { returnfalse;
}
} // Return the default browser value return !NS_IsOffline();
}
void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
ErrorResult& aRv) const { if (aCallerType != CallerType::System) { // If fingerprinting resistance is on, we will spoof this value. See // nsRFPService.h for details about spoofed values. if (nsContentUtils::ShouldResistFingerprinting(
GetDocShell(), RFPTarget::NavigatorBuildID)) {
aBuildID.AssignLiteral(LEGACY_BUILD_ID); return;
}
nsAutoCString host; bool isHTTPS = false; if (mWindow) {
nsCOMPtr<Document> doc = mWindow->GetDoc(); if (doc) {
nsIURI* uri = doc->GetDocumentURI(); if (uri) {
isHTTPS = uri->SchemeIs("https"); if (isHTTPS) {
MOZ_ALWAYS_SUCCEEDS(uri->GetHost(host));
}
}
}
}
// Spoof the buildID on pages not loaded from "https://*.mozilla.org". if (!isHTTPS || !StringEndsWith(host, ".mozilla.org"_ns)) {
aBuildID.AssignLiteral(LEGACY_BUILD_ID); return;
}
}
nsCOMPtr<nsIXULAppInfo> appInfo =
do_GetService("@mozilla.org/xre/app-info;1"); if (!appInfo) {
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); return;
}
if (!MayVibrate(doc)) { // It's important that we call CancelVibrate(), not Vibrate() with an // empty list, because Vibrate() will fail if we're no longer focused, but // CancelVibrate() will succeed, so long as nobody else has started a new // vibration pattern.
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
hal::CancelVibrate(window);
RemoveListener();
gVibrateWindowListener = nullptr; // Careful: The line above might have deleted |this|!
}
return NS_OK;
}
void VibrateWindowListener::RemoveListener() {
nsCOMPtr<Document> target(mDocument); if (!target) { return;
}
constexpr auto visibilitychange = u"visibilitychange"_ns;
target->RemoveSystemEventListener(visibilitychange, this, true/* use capture */);
}
if (aPermitted) { // Add a listener to cancel the vibration if the document becomes hidden, // and remove the old visibility listener, if there was one. if (!gVibrateWindowListener) { // If gVibrateWindowListener is null, this is the first time we've // vibrated, and we need to register a listener to clear // gVibrateWindowListener on shutdown.
ClearOnShutdown(&gVibrateWindowListener);
} else {
gVibrateWindowListener->RemoveListener();
}
gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
hal::Vibrate(pattern, mWindow);
}
if (permission == nsIPermissionManager::DENY_ACTION) { // Abort without observer service or on denied session permission.
SetVibrationPermission(false/* permitted */, false /* persistent */); returnfalse;
}
// Responsive Design Mode overrides the maxTouchPoints property when // touch simulation is enabled. if (bc && bc->InRDMPane()) { return bc->GetMaxTouchPointsOverride();
}
// The maxTouchPoints is going to reveal the detail of users' hardware. So, // we will spoof it into 0 if fingerprinting resistance is on. if (aCallerType != CallerType::System &&
nsContentUtils::ShouldResistFingerprinting(GetDocShell(),
RFPTarget::PointerEvents)) { return SPOOFED_MAX_TOUCH_POINTS;
}
// This list should be kept up-to-date with the spec: // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers // If you change this list, please also update the copy in E10SUtils.sys.mjs. staticconstchar* const kSafeSchemes[] = { // clang-format off "bitcoin", "ftp", "ftps", "geo", "im", "irc", "ircs", "magnet", "mailto", "matrix", "mms", "news", "nntp", "openpgp4fpr", "sftp", "sip", "sms", "smsto", "ssh", "tel", "urn", "webcal", "wtai", "xmpp", // clang-format on
};
void Navigator::CheckProtocolHandlerAllowed(const nsAString& aScheme,
nsIURI* aHandlerURI,
nsIURI* aDocumentURI,
ErrorResult& aRv) { auto raisePermissionDeniedHandler = [&] {
nsAutoCString spec;
aHandlerURI->GetSpec(spec);
nsPrintfCString message("Permission denied to add %s as a protocol handler",
spec.get());
aRv.ThrowSecurityError(message);
};
auto raisePermissionDeniedScheme = [&] {
nsPrintfCString message( "Permission denied to add a protocol handler for %s",
NS_ConvertUTF16toUTF8(aScheme).get());
aRv.ThrowSecurityError(message);
};
if (!aDocumentURI || !aHandlerURI) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return;
}
nsCString spec;
aHandlerURI->GetSpec(spec); // If the uri doesn't contain '%s', it won't be a good handler - the %s // gets replaced with the handled URI. if (!FindInReadable("%s"_ns, spec)) {
aRv.ThrowSyntaxError("Handler URI does not contain \"%s\"."); return;
}
// For security reasons we reject non-http(s) urls (see bug 354316),
nsAutoCString docScheme;
nsAutoCString handlerScheme;
aDocumentURI->GetScheme(docScheme);
aHandlerURI->GetScheme(handlerScheme); if ((!docScheme.EqualsLiteral("https") && !docScheme.EqualsLiteral("http")) ||
(!handlerScheme.EqualsLiteral("https") &&
!handlerScheme.EqualsLiteral("http"))) {
raisePermissionDeniedHandler(); return;
}
// Should be same-origin:
nsAutoCString handlerHost;
aHandlerURI->GetHostPort(handlerHost);
nsAutoCString documentHost;
aDocumentURI->GetHostPort(documentHost); if (!handlerHost.Equals(documentHost) || !handlerScheme.Equals(docScheme)) {
raisePermissionDeniedHandler(); return;
}
// Having checked the handler URI, check the scheme:
nsAutoCString scheme;
ToLowerCase(NS_ConvertUTF16toUTF8(aScheme), scheme); if (StringBeginsWith(scheme, "web+"_ns)) { // Check for non-ascii
nsReadingIterator<char> iter;
nsReadingIterator<char> iterEnd; auto remainingScheme = Substring(scheme, 4 /* web+ */);
remainingScheme.BeginReading(iter);
remainingScheme.EndReading(iterEnd); // Scheme suffix must be non-empty if (iter == iterEnd) {
raisePermissionDeniedScheme(); return;
} for (; iter != iterEnd; iter++) { if (*iter < 'a' || *iter > 'z') {
raisePermissionDeniedScheme(); return;
}
}
} else { bool matches = false; for (constchar* safeScheme : kSafeSchemes) { if (scheme.Equals(safeScheme)) {
matches = true; break;
}
} if (!matches) {
raisePermissionDeniedScheme(); return;
}
}
// check if we have prefs set saying not to add this. bool defaultExternal =
Preferences::GetBool("network.protocol-handler.external-default");
nsPrintfCString specificPref("network.protocol-handler.external.%s",
scheme.get()); if (!Preferences::GetBool(specificPref.get(), defaultExternal)) {
raisePermissionDeniedScheme(); return;
}
// Check to make sure this isn't already handled internally (we don't // want to let them take over, say "chrome"). In theory, the checks above // should have already taken care of this.
nsCOMPtr<nsIExternalProtocolHandler> externalHandler =
do_QueryInterface(handler);
MOZ_RELEASE_ASSERT(
externalHandler, "We should never allow overriding a builtin protocol handler");
}
void Navigator::RegisterProtocolHandler(const nsAString& aScheme, const nsAString& aURI,
ErrorResult& aRv) { if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell() ||
!mWindow->GetDoc()) { return;
}
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow); if (loadContext->UsePrivateBrowsing()) { // If we're a private window, don't alert the user or webpage. We log to the // console so that web developers have some way to tell what's going wrong.
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, "DOM"_ns, mWindow->GetDoc(),
nsContentUtils::eDOM_PROPERTIES, "RegisterProtocolHandlerPrivateBrowsingWarning"); return;
}
nsCOMPtr<Document> doc = mWindow->GetDoc();
// Determine if doc is allowed to assign this handler
nsIURI* docURI = doc->GetDocumentURIObject();
nsCOMPtr<nsIURI> handlerURI;
NS_NewURI(getter_AddRefs(handlerURI), NS_ConvertUTF16toUTF8(aURI),
doc->GetDocumentCharacterSet(), docURI);
CheckProtocolHandlerAllowed(aScheme, handlerURI, docURI, aRv); if (aRv.Failed()) { return;
}
// Determine a title from the document URI.
nsAutoCString docDisplayHostPort;
docURI->GetDisplayHostPort(docDisplayHostPort);
NS_ConvertASCIItoUTF16 title(docDisplayHostPort);
// Spec disallows any schemes save for HTTP/HTTPs if (!uri->SchemeIs("http") && !uri->SchemeIs("https")) {
aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Beacon",
uri->GetSpecOrDefault()); returnfalse;
}
nsCOMPtr<nsIInputStream> in;
nsAutoCString contentTypeWithCharset;
nsAutoCString charset;
uint64_t length = 0; if (aBody) {
aRv = aBody->GetAsStream(getter_AddRefs(in), &length,
contentTypeWithCharset, charset); if (NS_WARN_IF(aRv.Failed())) { returnfalse;
}
}
nsSecurityFlags securityFlags = nsILoadInfo::SEC_COOKIES_INCLUDE; // Ensure that only streams with content types that are safelisted ignore CORS // rules if (aBody && !contentTypeWithCharset.IsVoid() &&
!nsContentUtils::IsCORSSafelistedRequestHeader("content-type"_ns,
contentTypeWithCharset)) {
securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
} else {
securityFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
}
if (NS_FAILED(rv)) {
aRv.Throw(rv); returnfalse;
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); if (!httpChannel) { // Beacon spec only supports HTTP requests at this time
aRv.Throw(NS_ERROR_DOM_BAD_URI); returnfalse;
}
auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (aBody) {
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel); if (!uploadChannel) {
aRv.Throw(NS_ERROR_FAILURE); returnfalse;
}
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(channel); if (p) {
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
}
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel)); if (cos) {
cos->AddClassFlags(nsIClassOfService::Background);
}
// The channel needs to have a loadgroup associated with it, so that we can // cancel the channel and any redirected channels it may create.
nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
nsCOMPtr<nsIInterfaceRequestor> callbacks =
do_QueryInterface(mWindow->GetDocShell());
loadGroup->SetNotificationCallbacks(callbacks);
channel->SetLoadGroup(loadGroup);
RefPtr<BeaconStreamListener> beaconListener = new BeaconStreamListener();
rv = channel->AsyncOpen(beaconListener); // do not throw if security checks fail within asyncOpen
NS_ENSURE_SUCCESS(rv, false);
// make the beaconListener hold a strong reference to the loadgroup // which is released in ::OnStartRequest
beaconListener->SetLoadGroup(loadGroup);
returntrue;
}
MediaDevices* Navigator::GetMediaDevices(ErrorResult& aRv) { if (!mMediaDevices) { if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr;
}
mMediaDevices = new MediaDevices(mWindow);
} return mMediaDevices;
}
void Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints,
NavigatorUserMediaSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
CallerType aCallerType, ErrorResult& aRv) {
MOZ_ASSERT(NS_IsMainThread()); if (!mWindow || !mWindow->IsFullyActive()) {
aRv.ThrowInvalidStateError("The document is not fully active."); return;
}
GetMediaDevices(aRv); if (aRv.Failed()) { return;
}
MOZ_ASSERT(mMediaDevices); if (Document* doc = mWindow->GetExtantDoc()) { if (!mWindow->IsSecureContext()) {
doc->SetUseCounter(eUseCounter_custom_MozGetUserMediaInsec);
}
}
RefPtr<MediaManager::StreamPromise> sp; if (!MediaManager::IsOn(aConstraints.mVideo) &&
!MediaManager::IsOn(aConstraints.mAudio)) {
sp = MediaManager::StreamPromise::CreateAndReject(
MakeRefPtr<MediaMgrError>(MediaMgrError::Name::TypeError, "audio and/or video is required"),
__func__);
} else {
sp = mMediaDevices->GetUserMedia(mWindow, aConstraints, aCallerType);
}
RefPtr<NavigatorUserMediaSuccessCallback> onsuccess(&aOnSuccess);
RefPtr<NavigatorUserMediaErrorCallback> onerror(&aOnError);
if (!mBatteryManager) {
mBatteryManager = new battery::BatteryManager(mWindow);
mBatteryManager->Init();
}
mBatteryPromise->MaybeResolve(mBatteryManager);
return mBatteryPromise;
}
//***************************************************************************** // Navigator::Share() - Web Share API //*****************************************************************************
already_AddRefed<Promise> Navigator::Share(const ShareData& aData,
ErrorResult& aRv) { if (!mWindow || !mWindow->IsFullyActive()) {
aRv.ThrowInvalidStateError("The document is not fully active."); return nullptr;
}
if (NS_WARN_IF(!mWindow->GetDocShell() || !mWindow->GetExtantDoc())) {
aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr;
}
if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(),
u"web-share"_ns)) {
aRv.ThrowNotAllowedError( "Document's Permissions Policy does not allow calling " "share() from this context."); return nullptr;
}
if (mSharePromise) {
NS_WARNING("Only one share picker at a time per navigator instance");
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr;
}
if (StaticPrefs::dom_webshare_requireinteraction() &&
!doc->ConsumeTransientUserGestureActivation()) {
aRv.ThrowNotAllowedError( "User activation was already consumed " "or share() was not activated by a user gesture."); return nullptr;
}
ValidateShareData(aData, aRv);
if (aRv.Failed()) { return nullptr;
}
// TODO: Process file member, which we don't currently support.
// If data's url member is present, try to resolve it...
nsCOMPtr<nsIURI> url; if (aData.mUrl.WasPassed()) { auto result = doc->ResolveWithBaseURI(aData.mUrl.Value());
url = result.unwrap();
MOZ_ASSERT(url);
}
// Process the title member...
nsCString title; if (aData.mTitle.WasPassed()) {
title.Assign(NS_ConvertUTF16toUTF8(aData.mTitle.Value()));
} else {
title.SetIsVoid(true);
}
// Process the text member...
nsCString text; if (aData.mText.WasPassed()) {
text.Assign(NS_ConvertUTF16toUTF8(aData.mText.Value()));
} else {
text.SetIsVoid(true);
}
// Let mSharePromise be a new promise.
mSharePromise = Promise::Create(mWindow->AsGlobal(), aRv); if (aRv.Failed()) { return nullptr;
}
IPCWebShareData data(title, text, url); auto wgc = mWindow->GetWindowGlobalChild(); if (!wgc) {
aRv.Throw(NS_ERROR_FAILURE); return nullptr;
}
// Do the share
wgc->SendShare(data)->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}](
PWindowGlobalChild::SharePromise::ResolveOrRejectValue&& aResult) { if (aResult.IsResolve()) { if (NS_SUCCEEDED(aResult.ResolveValue())) {
self->mSharePromise->MaybeResolveWithUndefined();
} else {
self->mSharePromise->MaybeReject(aResult.ResolveValue());
}
} elseif (self->mSharePromise) { // IPC died
self->mSharePromise->MaybeReject(NS_BINDING_ABORTED);
}
self->mSharePromise = nullptr;
}); return do_AddRef(mSharePromise);
}
//***************************************************************************** // Navigator::CanShare() - Web Share API //***************************************************************************** bool Navigator::CanShare(const ShareData& aData) { if (!mWindow || !mWindow->IsFullyActive()) { returnfalse;
}
if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(),
u"web-share"_ns)) { returnfalse;
}
void Navigator::ValidateShareData(const ShareData& aData, ErrorResult& aRv) { // TODO: remove this check when we support files share. if (aData.mFiles.WasPassed() && !aData.mFiles.Value().IsEmpty()) {
aRv.ThrowTypeError("Passing files is currently not supported."); return;
}
// At least one member must be present. if (!titleTextOrUrlPassed) {
aRv.ThrowTypeError( "Must have a title, text, or url member in the ShareData dictionary"); return;
}
// If data's url member is present, try to resolve it...
nsCOMPtr<nsIURI> url; if (aData.mUrl.WasPassed()) {
Document* doc = mWindow->GetExtantDoc();
Result<OwningNonNull<nsIURI>, nsresult> result =
doc->ResolveWithBaseURI(aData.mUrl.Value()); if (NS_WARN_IF(result.isErr())) {
aRv.ThrowTypeError<MSG_INVALID_URL>(
NS_ConvertUTF16toUTF8(aData.mUrl.Value())); return;
}
url = result.unwrap(); // Check that we only share loadable URLs (e.g., http/https). // we also exclude blobs, as it doesn't make sense to share those outside // the context of the browser. const uint32_t flags =
nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
nsIScriptSecurityManager::DISALLOW_SCRIPT; if (NS_FAILED(
nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal(
doc->NodePrincipal(), url, flags, doc->InnerWindowID())) ||
url->SchemeIs("blob")) {
aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Share",
url->GetSpecOrDefault()); return;
}
}
}
if (!FeaturePolicyUtils::IsFeatureAllowed(win->GetExtantDoc(),
u"gamepad"_ns)) {
aRv.ThrowSecurityError( "Document's Permission Policy does not allow calling " "getGamepads() from this context."); return;
}
// We need to set the flag to trigger the parent process to start monitoring // gamepads. Otherwise, we cannot get any gamepad information.
win->SetHasGamepadEventListener(true); return win->RequestAllGamepads(aRv);
}
void Navigator::FinishGetVRDisplays(bool isWebVRSupportedInwindow, Promise* p) { if (!isWebVRSupportedInwindow) { // WebVR in this window is not supported, so resolve the promise // with no displays available
nsTArray<RefPtr<VRDisplay>> vrDisplaysEmpty;
p->MaybeResolve(vrDisplaysEmpty); return;
}
// Since FinishGetVRDisplays can be called asynchronously after an IPC // response, it's possible that the Window can be torn down before this // call. In that case, the Window's cyclic references to VR objects are // also torn down and should not be recreated via // NotifyHasXRSession.
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); if (win->IsDying()) { // The Window has been torn down, so there is no further work that can // be done.
p->MaybeRejectWithTypeError( "Unable to return VRDisplays for a closed window."); return;
}
void Navigator::OnXRPermissionRequestAllow() { // The permission request that results in this callback could have // been instantiated by WebVR, WebXR, or both.
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); bool usingWebXR = false;
if (mXRSystem) {
usingWebXR = mXRSystem->OnXRPermissionRequestAllow();
}
bool rejectWebVR = true; // If WebVR and WebXR both requested permission, only grant it to // WebXR, which takes priority. if (!usingWebXR) { // We pass mWindow's id to RefreshVRDisplays, so NotifyVRDisplaysUpdated // will be called asynchronously, resolving the promises in // mVRGetDisplaysPromises.
rejectWebVR = !VRDisplay::RefreshVRDisplays(win->WindowID());
} // Even if WebXR took priority, reject requests for WebVR in case they were // made simultaneously and coelesced into a single permission prompt. if (rejectWebVR) { for (auto& p : mVRGetDisplaysPromises) { // Failed to refresh, reject the promise now
p->MaybeRejectWithTypeError("Failed to find attached VR displays.");
}
mVRGetDisplaysPromises.Clear();
}
}
void Navigator::OnXRPermissionRequestCancel() { if (mXRSystem) {
mXRSystem->OnXRPermissionRequestCancel();
}
nsTArray<RefPtr<VRDisplay>> vrDisplays; for (auto& p : mVRGetDisplaysPromises) { // Resolve the promise with no vr displays when // the user blocks access.
p->MaybeResolve(vrDisplays);
}
mVRGetDisplaysPromises.Clear();
}
void Navigator::GetActiveVRDisplays(
nsTArray<RefPtr<VRDisplay>>& aDisplays) const { /** * Get only the active VR displays. * GetActiveVRDisplays should only enumerate displays that * are already active without causing any other hardware to be * activated. * We must not call nsGlobalWindowInner::NotifyHasXRSession here, * as that would cause enumeration and activation of other VR hardware. * Activating VR hardware is intrusive to the end user, as it may * involve physically powering on devices that the user did not * intend to use.
*/ if (!mWindow || !mWindow->GetDocShell()) { return;
}
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
nsTArray<RefPtr<VRDisplay>> displays; if (win->UpdateVRDisplays(displays)) { for (auto display : displays) { if (display->IsPresenting()) {
aDisplays.AppendElement(display);
}
}
}
}
void Navigator::NotifyVRDisplaysUpdated() { // Synchronize the VR devices and resolve the promises in // mVRGetDisplaysPromises
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
nsTArray<RefPtr<VRDisplay>> vrDisplays; if (win->UpdateVRDisplays(vrDisplays)) { for (auto p : mVRGetDisplaysPromises) {
p->MaybeResolve(vrDisplays);
}
} else { for (auto p : mVRGetDisplaysPromises) {
p->MaybeReject(NS_ERROR_FAILURE);
}
}
mVRGetDisplaysPromises.Clear();
}
// Ensure that the Mock VR devices are not released prematurely
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
win->NotifyHasXRSession();
if (!mVRServiceTest) {
mVRServiceTest = VRServiceTest::CreateTestService(mWindow);
} return mVRServiceTest;
}
size_t Navigator::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
size_t n = aMallocSizeOf(this);
// TODO: add SizeOfIncludingThis() to nsMimeTypeArray, bug 674113. // TODO: add SizeOfIncludingThis() to nsPluginArray, bug 674114. // TODO: add SizeOfIncludingThis() to Geolocation, bug 674115. // TODO: add SizeOfIncludingThis() to DesktopNotificationCenter, bug 674116.
return n;
}
void Navigator::OnNavigation() { if (!mWindow) { return;
}
// If MediaManager is open let it inform any live streams or pending callbacks
MediaManager* manager = MediaManager::GetIfExists(); if (manager) {
manager->OnNavigation(mWindow->WindowID());
}
}
// navigator.platform is the same for default and spoofed values. The // "general.platform.override" pref should override the default platform, // but the spoofed platform should override the pref. if (aUsePrefOverriddenValue &&
!ShouldResistFingerprinting(aCallerDoc, RFPTarget::NavigatorPlatform)) {
nsAutoString override;
nsresult rv =
mozilla::Preferences::GetString("general.platform.override", override);
if (NS_SUCCEEDED(rv)) {
aPlatform = override; return NS_OK;
}
}
#ifdefined(WIN32)
aPlatform.AssignLiteral("Win32"); #elifdefined(XP_MACOSX) // Always return "MacIntel", even on ARM64 macOS like Safari does.
aPlatform.AssignLiteral("MacIntel"); #elifdefined(ANDROID)
aPlatform.AssignLiteral("Linux armv81"); #else
aPlatform.AssignLiteral("Linux x86_64"); #endif
if (aUsePrefOverriddenValue) { // If fingerprinting resistance is on, we will spoof this value. See // nsRFPService.h for details about spoofed values. if (ShouldResistFingerprinting(aCallerDoc,
RFPTarget::NavigatorAppVersion)) {
aAppVersion.AssignLiteral(SPOOFED_APPVERSION); return NS_OK;
}
nsAutoString override;
nsresult rv = mozilla::Preferences::GetString("general.appversion.override",
override);
if (NS_SUCCEEDED(rv)) {
aAppVersion = override; return NS_OK;
}
}
// We will skip the override and pass to httpHandler to get spoofed userAgent // when 'privacy.resistFingerprinting' is true. if (!shouldResistFingerprinting) {
nsAutoString override;
nsresult rv =
mozilla::Preferences::GetString("general.useragent.override", override);
if (NS_SUCCEEDED(rv)) {
aUserAgent = override; return NS_OK;
}
}
// When the caller is content and 'privacy.resistFingerprinting' is true, // return a spoofed userAgent which reveals the platform but not the // specific OS version, etc. if (shouldResistFingerprinting) {
nsAutoCString spoofedUA;
nsRFPService::GetSpoofedUserAgent(spoofedUA);
CopyASCIItoUTF16(spoofedUA, aUserAgent); return NS_OK;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.