Files
UXP-Jihad/dom/base/Navigator.cpp
T
athenian200 b787af5702 Issue #1638 - Part 1: Put NPAPI code behind MOZ_ENABLE_NPAPI.
Adds --disable-npapi as an additional build option. The default is still to build in NPAPI compatibility, but with this flag, it's finally possible to build without pulling in GTK2 on systems that don't have the package.

This essentially ifdefs everything associated with NPAPI, following various Mozilla removal bugs.

Ref: BZ 1682030, BZ 1251202, BZ 1217665, BZ 1687239
2024-01-10 04:03:35 -06:00

1512 lines
39 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
// Needs to be first.
#include "base/basictypes.h"
#include "Navigator.h"
#include "nsIXULAppInfo.h"
#ifdef MOZ_ENABLE_NPAPI
#include "nsPluginArray.h"
#endif
#include "nsMimeTypeArray.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/BodyExtractor.h"
#include "mozilla/dom/DesktopNotification.h"
#include "mozilla/dom/FetchBinding.h"
#include "mozilla/dom/File.h"
#include "nsGeolocation.h"
#include "nsIClassOfService.h"
#include "nsIHttpProtocolHandler.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsContentPolicyUtils.h"
#include "nsISupportsPriority.h"
#include "nsICachingChannel.h"
#include "nsIWebContentHandlerRegistrar.h"
#include "nsICookiePermission.h"
#include "nsIScriptSecurityManager.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Clipboard.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadServiceTest.h"
#endif
#include "mozilla/dom/PowerManager.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/TCPSocket.h"
#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/workers/RuntimeService.h"
#include "mozilla/Hal.h"
#include "nsISiteSpecificUserAgent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/SSE.h"
#include "mozilla/StaticPtr.h"
#include "Connection.h"
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
#include "nsGlobalWindow.h"
#include "nsIIdleObserver.h"
#include "nsIPermissionManager.h"
#include "nsMimeTypes.h"
#include "nsNetUtil.h"
#include "nsStringStream.h"
#include "nsComponentManagerUtils.h"
#include "nsIStringStream.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "TimeManager.h"
#include "nsStreamUtils.h"
#include "nsIAppsService.h"
#include "mozIApplication.h"
#include "WidgetUtils.h"
#include "mozilla/dom/MediaDevices.h"
#include "MediaManager.h"
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
#include "AudioChannelManager.h"
#endif
#include "nsIDOMGlobalPropertyInitializer.h"
#include "nsJSUtils.h"
#include "nsScriptNameSpaceManager.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/Promise.h"
#include "nsIUploadChannel2.h"
#include "mozilla/dom/FormData.h"
#include "nsIDocShell.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#if defined(XP_LINUX)
#include "mozilla/Hal.h"
#endif
#include "mozilla/dom/ContentChild.h"
namespace mozilla {
namespace dom {
static void
AddPermission(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime)
{
MOZ_ASSERT(aType);
MOZ_ASSERT(aPrincipal);
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (!permMgr) {
return;
}
permMgr->AddFromPrincipal(aPrincipal, aType, aPermission, aExpireType,
aExpireTime);
}
static uint32_t
GetPermission(nsPIDOMWindowInner* aWindow, const char* aType)
{
MOZ_ASSERT(aType);
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (!permMgr) {
return permission;
}
permMgr->TestPermissionFromWindow(aWindow, aType, &permission);
return permission;
}
static uint32_t
GetPermission(nsIPrincipal* aPrincipal, const char* aType)
{
MOZ_ASSERT(aType);
MOZ_ASSERT(aPrincipal);
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (!permMgr) {
return permission;
}
permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &permission);
return permission;
}
/* static */
void
Navigator::Init()
{
// Add any Preferences::Add*VarCache(&sPref, "pref", default) here if needed.
}
Navigator::Navigator(nsPIDOMWindowInner* aWindow)
: mWindow(aWindow)
{
MOZ_ASSERT(aWindow->IsInnerWindow(), "Navigator must get an inner window!");
}
Navigator::~Navigator()
{
Invalidate();
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigator)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNavigator)
NS_INTERFACE_MAP_ENTRY(nsIDOMNavigator)
NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorNetwork)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Navigator)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Navigator)
NS_IMPL_CYCLE_COLLECTION_CLASS(Navigator)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Navigator)
tmp->Invalidate();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMimeTypes)
#ifdef MOZ_ENABLE_NPAPI
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlugins)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotification)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
#ifdef MOZ_GAMEPAD
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator)
void
Navigator::Invalidate()
{
// Don't clear mWindow here so we know we've got a non-null mWindow
// until we're unlinked.
mMimeTypes = nullptr;
#ifdef MOZ_ENABLE_NPAPI
if (mPlugins) {
mPlugins->Invalidate();
mPlugins = nullptr;
}
#endif
mPermissions = nullptr;
mStorageManager = nullptr;
// If there is a page transition, make sure delete the geolocation object.
if (mGeolocation) {
mGeolocation->Shutdown();
mGeolocation = nullptr;
}
if (mNotification) {
mNotification->Shutdown();
mNotification = nullptr;
}
if (mPowerManager) {
mPowerManager->Shutdown();
mPowerManager = nullptr;
}
if (mConnection) {
mConnection->Shutdown();
mConnection = nullptr;
}
mMediaDevices = nullptr;
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
if (mAudioChannelManager) {
mAudioChannelManager = nullptr;
}
#endif
if (mTimeManager) {
mTimeManager = nullptr;
}
mServiceWorkerContainer = nullptr;
#ifdef MOZ_GAMEPAD
if (mGamepadServiceTest) {
mGamepadServiceTest->Shutdown();
mGamepadServiceTest = nullptr;
}
#endif
mVRGetDisplaysPromises.Clear();
}
//*****************************************************************************
// Navigator::nsIDOMNavigator
//*****************************************************************************
NS_IMETHODIMP
Navigator::GetUserAgent(nsAString& aUserAgent)
{
nsCOMPtr<nsIURI> codebaseURI;
nsCOMPtr<nsPIDOMWindowInner> window;
if (mWindow) {
window = mWindow;
nsIDocShell* docshell = window->GetDocShell();
nsString customUserAgent;
if (docshell) {
docshell->GetCustomUserAgent(customUserAgent);
if (!customUserAgent.IsEmpty()) {
aUserAgent = customUserAgent;
return NS_OK;
}
nsIDocument* doc = mWindow->GetExtantDoc();
if (doc) {
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
}
}
}
return GetUserAgent(window, codebaseURI, nsContentUtils::IsCallerChrome(),
aUserAgent);
}
NS_IMETHODIMP
Navigator::GetAppCodeName(nsAString& aAppCodeName)
{
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString appName;
rv = service->GetAppName(appName);
CopyASCIItoUTF16(appName, aAppCodeName);
return rv;
}
NS_IMETHODIMP
Navigator::GetAppVersion(nsAString& aAppVersion)
{
return GetAppVersion(aAppVersion, /* aUsePrefOverriddenValue */ true);
}
NS_IMETHODIMP
Navigator::GetAppName(nsAString& aAppName)
{
AppName(aAppName, /* aUsePrefOverriddenValue */ true);
return NS_OK;
}
/**
* 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.
*
* An empty array will be returned if there is no valid languages.
*/
/* static */ void
Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages)
{
MOZ_ASSERT(NS_IsMainThread());
aLanguages.Clear();
// E.g. "de-de, en-us,en".
const nsAdoptingString& acceptLang =
Preferences::GetLocalizedString("intl.accept_languages");
// Split values on commas.
nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
while (langTokenizer.hasMoreTokens()) {
nsDependentSubstring lang = langTokenizer.nextToken();
// 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) {
nsCharSeparatedTokenizer localeTokenizer(lang, '-');
int32_t pos = 0;
bool first = true;
while (localeTokenizer.hasMoreTokens()) {
const nsSubstring& code = localeTokenizer.nextToken();
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;
}
}
aLanguages.AppendElement(lang);
}
}
/**
* Do not use UI language (chosen app locale) here but the first value set in
* the Accept Languages header, see ::GetAcceptLanguages().
*
* See RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers" for
* the reasons why.
*/
NS_IMETHODIMP
Navigator::GetLanguage(nsAString& aLanguage)
{
nsTArray<nsString> languages;
GetLanguages(languages);
if (languages.Length() >= 1) {
aLanguage.Assign(languages[0]);
} else {
aLanguage.Truncate();
}
return NS_OK;
}
void
Navigator::GetLanguages(nsTArray<nsString>& aLanguages)
{
GetAcceptLanguages(aLanguages);
// The returned value is cached by the binding code. The window listen 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.
}
NS_IMETHODIMP
Navigator::GetPlatform(nsAString& aPlatform)
{
return GetPlatform(aPlatform, /* aUsePrefOverriddenValue */ true);
}
NS_IMETHODIMP
Navigator::GetOscpu(nsAString& aOSCPU)
{
if (!nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
Preferences::GetString("general.oscpu.override");
if (override) {
aOSCPU = override;
return NS_OK;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString oscpu;
rv = service->GetOscpu(oscpu);
CopyASCIItoUTF16(oscpu, aOSCPU);
return rv;
}
NS_IMETHODIMP
Navigator::GetVendor(nsAString& aVendor)
{
aVendor.Truncate();
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetVendorSub(nsAString& aVendorSub)
{
aVendorSub.Truncate();
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetProduct(nsAString& aProduct)
{
aProduct.AssignLiteral("Gecko");
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetProductSub(nsAString& aProductSub)
{
// Legacy build ID hardcoded for backward compatibility (bug 776376)
aProductSub.AssignLiteral("20100101");
return NS_OK;
}
nsMimeTypeArray*
Navigator::GetMimeTypes(ErrorResult& aRv)
{
if (!mMimeTypes) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mMimeTypes = new nsMimeTypeArray(mWindow);
}
return mMimeTypes;
}
#ifdef MOZ_ENABLE_NPAPI
nsPluginArray*
Navigator::GetPlugins(ErrorResult& aRv)
{
if (!mPlugins) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mPlugins = new nsPluginArray(mWindow);
mPlugins->Init();
}
return mPlugins;
}
#endif
Permissions*
Navigator::GetPermissions(ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!mPermissions) {
mPermissions = new Permissions(mWindow);
}
return mPermissions;
}
StorageManager*
Navigator::Storage()
{
MOZ_ASSERT(mWindow);
if(!mStorageManager) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
MOZ_ASSERT(global);
mStorageManager = new StorageManager(global);
}
return mStorageManager;
}
// Values for the network.cookie.cookieBehavior pref are documented in
// nsCookieService.cpp.
#define COOKIE_BEHAVIOR_REJECT 2
bool
Navigator::CookieEnabled()
{
bool cookieEnabled =
(Preferences::GetInt("network.cookie.cookieBehavior",
COOKIE_BEHAVIOR_REJECT) != COOKIE_BEHAVIOR_REJECT);
// 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 cookieEnabled;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
if (!doc) {
return cookieEnabled;
}
nsCOMPtr<nsIURI> codebaseURI;
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
if (!codebaseURI) {
// Not a codebase, so technically can't set cookies, but let's
// just return the default value.
return cookieEnabled;
}
nsCOMPtr<nsICookiePermission> permMgr =
do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
NS_ENSURE_TRUE(permMgr, cookieEnabled);
// Pass null for the channel, just like the cookie service does.
nsCookieAccess access;
nsresult rv = permMgr->CanAccess(codebaseURI, nullptr, &access);
NS_ENSURE_SUCCESS(rv, cookieEnabled);
if (access != nsICookiePermission::ACCESS_DEFAULT) {
cookieEnabled = access != nsICookiePermission::ACCESS_DENY;
}
return cookieEnabled;
}
bool
Navigator::OnLine()
{
return !NS_IsOffline();
}
NS_IMETHODIMP
Navigator::GetBuildID(nsAString& aBuildID)
{
if (!nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
Preferences::GetString("general.buildID.override");
if (override) {
aBuildID = override;
return NS_OK;
}
}
nsCOMPtr<nsIXULAppInfo> appInfo =
do_GetService("@mozilla.org/xre/app-info;1");
if (!appInfo) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsAutoCString buildID;
nsresult rv = appInfo->GetAppBuildID(buildID);
if (NS_FAILED(rv)) {
return rv;
}
aBuildID.Truncate();
AppendASCIItoUTF16(buildID, aBuildID);
return NS_OK;
}
bool
Navigator::GlobalPrivacyControl()
{
return nsContentUtils::GPCEnabled();
}
bool
Navigator::JavaEnabled(ErrorResult& aRv)
{
// Return true if we have a handler for the java mime
nsAdoptingString javaMIME = Preferences::GetString("plugin.java.mime");
NS_ENSURE_TRUE(!javaMIME.IsEmpty(), false);
if (!mMimeTypes) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return false;
}
mMimeTypes = new nsMimeTypeArray(mWindow);
}
RefreshMIMEArray();
nsMimeType *mimeType = mMimeTypes->NamedItem(javaMIME);
return mimeType && mimeType->GetEnabledPlugin();
}
uint64_t
Navigator::HardwareConcurrency()
{
workers::RuntimeService* rts = workers::RuntimeService::GetOrCreateService();
if (!rts) {
return 1;
}
return rts->ClampedHardwareConcurrency();
}
bool
Navigator::CpuHasSSE2()
{
return mozilla::supports_sse2();
}
void
Navigator::RefreshMIMEArray()
{
if (mMimeTypes) {
mMimeTypes->Refresh();
}
}
void
Navigator::AddIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
CallbackObjectHolder<MozIdleObserver, nsIIdleObserver> holder(&aIdleObserver);
nsCOMPtr<nsIIdleObserver> obs = holder.ToXPCOMCallback();
if (NS_FAILED(mWindow->RegisterIdleObserver(obs))) {
NS_WARNING("Failed to add idle observer.");
}
}
void
Navigator::RemoveIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
CallbackObjectHolder<MozIdleObserver, nsIIdleObserver> holder(&aIdleObserver);
nsCOMPtr<nsIIdleObserver> obs = holder.ToXPCOMCallback();
if (NS_FAILED(mWindow->UnregisterIdleObserver(obs))) {
NS_WARNING("Failed to remove idle observer.");
}
}
//*****************************************************************************
// Pointer Events interface
//*****************************************************************************
uint32_t
Navigator::MaxTouchPoints()
{
nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(mWindow->GetOuterWindow());
NS_ENSURE_TRUE(widget, 0);
return widget->GetMaxTouchPoints();
}
//*****************************************************************************
// Navigator::nsIDOMClientInformation
//*****************************************************************************
void
Navigator::RegisterContentHandler(const nsAString& aMIMEType,
const nsAString& aURI,
const nsAString& aTitle,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
return;
}
nsCOMPtr<nsIWebContentHandlerRegistrar> registrar =
do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
if (!registrar) {
return;
}
aRv = registrar->RegisterContentHandler(aMIMEType, aURI, aTitle,
mWindow->GetOuterWindow());
}
void
Navigator::RegisterProtocolHandler(const nsAString& aProtocol,
const nsAString& aURI,
const nsAString& aTitle,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
return;
}
nsCOMPtr<nsIWebContentHandlerRegistrar> registrar =
do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
if (!registrar) {
return;
}
aRv = registrar->RegisterProtocolHandler(aProtocol, aURI, aTitle,
mWindow->GetOuterWindow());
}
Geolocation*
Navigator::GetGeolocation(ErrorResult& aRv)
{
if (mGeolocation) {
return mGeolocation;
}
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mGeolocation = new Geolocation();
if (NS_FAILED(mGeolocation->Init(mWindow))) {
mGeolocation = nullptr;
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return mGeolocation;
}
class BeaconStreamListener final : public nsIStreamListener
{
~BeaconStreamListener() {}
public:
BeaconStreamListener() : mLoadGroup(nullptr) {}
void SetLoadGroup(nsILoadGroup* aLoadGroup) {
mLoadGroup = aLoadGroup;
}
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
private:
nsCOMPtr<nsILoadGroup> mLoadGroup;
};
NS_IMPL_ISUPPORTS(BeaconStreamListener,
nsIStreamListener,
nsIRequestObserver)
NS_IMETHODIMP
BeaconStreamListener::OnStartRequest(nsIRequest *aRequest,
nsISupports *aContext)
{
// release the loadgroup first
mLoadGroup = nullptr;
aRequest->Cancel(NS_ERROR_NET_INTERRUPT);
return NS_BINDING_ABORTED;
}
NS_IMETHODIMP
BeaconStreamListener::OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatus)
{
return NS_OK;
}
NS_IMETHODIMP
BeaconStreamListener::OnDataAvailable(nsIRequest *aRequest,
nsISupports *ctxt,
nsIInputStream *inStr,
uint64_t sourceOffset,
uint32_t count)
{
MOZ_ASSERT(false);
return NS_OK;
}
bool
Navigator::SendBeacon(const nsAString& aUrl,
const Nullable<fetch::BodyInit>& aData,
ErrorResult& aRv)
{
if (aData.IsNull()) {
return SendBeaconInternal(aUrl, nullptr, eBeaconTypeOther, aRv);
}
if (aData.Value().IsArrayBuffer()) {
BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
if (aData.Value().IsArrayBufferView()) {
BodyExtractor<const ArrayBufferView> body(&aData.Value().GetAsArrayBufferView());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
if (aData.Value().IsBlob()) {
BodyExtractor<nsIXHRSendable> body(&aData.Value().GetAsBlob());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
if (aData.Value().IsFormData()) {
BodyExtractor<nsIXHRSendable> body(&aData.Value().GetAsFormData());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
if (aData.Value().IsUSVString()) {
BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
if (aData.Value().IsURLSearchParams()) {
BodyExtractor<nsIXHRSendable> body(&aData.Value().GetAsURLSearchParams());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
MOZ_CRASH("Invalid data type.");
return false;
}
bool
Navigator::SendBeaconInternal(const nsAString& aUrl,
BodyExtractorBase* aBody,
BeaconType aType,
ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
if (!doc) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
}
nsIURI* documentURI = doc->GetDocumentURI();
if (!documentURI) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
getter_AddRefs(uri),
aUrl,
doc,
doc->GetDocBaseURI());
if (NS_FAILED(rv)) {
aRv.ThrowTypeError<MSG_INVALID_URL>(aUrl);
return false;
}
// Spec disallows any schemes save for HTTP/HTTPs
bool isValidScheme;
if (!(NS_SUCCEEDED(uri->SchemeIs("http", &isValidScheme)) && isValidScheme) &&
!(NS_SUCCEEDED(uri->SchemeIs("https", &isValidScheme)) && isValidScheme)) {
aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>( NS_LITERAL_STRING("Beacon"), aUrl);
return false;
}
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
// No need to use CORS for sendBeacon unless it's a BLOB
nsSecurityFlags securityFlags = aType == eBeaconTypeBlob
? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
: nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
doc,
securityFlags,
nsIContentPolicy::TYPE_BEACON,
nullptr, // aLoadGroup
nullptr, // aCallbacks
loadFlags);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
if (!httpChannel) {
// Beacon spec only supports HTTP requests at this time
aRv.Throw(NS_ERROR_DOM_BAD_URI);
return false;
}
httpChannel->SetReferrer(documentURI);
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())) {
return false;
}
if (aType == eBeaconTypeArrayBuffer) {
MOZ_ASSERT(contentTypeWithCharset.IsEmpty());
MOZ_ASSERT(charset.IsEmpty());
contentTypeWithCharset.Assign("application/octet-stream");
}
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel);
if (!uploadChannel) {
aRv.Throw(NS_ERROR_FAILURE);
return false;
}
uploadChannel->ExplicitSetUploadStream(in, contentTypeWithCharset, length,
NS_LITERAL_CSTRING("POST"),
false);
} else {
httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
}
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->AsyncOpen2(beaconListener);
// do not throw if security checks fail within asyncOpen2
NS_ENSURE_SUCCESS(rv, false);
// make the beaconListener hold a strong reference to the loadgroup
// which is released in ::OnStartRequest
beaconListener->SetLoadGroup(loadGroup);
return true;
}
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,
ErrorResult& aRv)
{
CallbackObjectHolder<NavigatorUserMediaSuccessCallback,
nsIDOMGetUserMediaSuccessCallback> holder1(&aOnSuccess);
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onsuccess =
holder1.ToXPCOMCallback();
CallbackObjectHolder<NavigatorUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
MediaManager* manager = MediaManager::Get();
aRv = manager->GetUserMedia(mWindow, aConstraints, onsuccess, onerror);
}
void
Navigator::MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints,
MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
uint64_t aInnerWindowID,
const nsAString& aCallID,
ErrorResult& aRv)
{
CallbackObjectHolder<MozGetUserMediaDevicesSuccessCallback,
nsIGetUserMediaDevicesSuccessCallback> holder1(&aOnSuccess);
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onsuccess =
holder1.ToXPCOMCallback();
CallbackObjectHolder<NavigatorUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
MediaManager* manager = MediaManager::Get();
aRv = manager->GetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror,
aInnerWindowID, aCallID);
}
DesktopNotificationCenter*
Navigator::GetMozNotification(ErrorResult& aRv)
{
if (mNotification) {
return mNotification;
}
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mNotification = new DesktopNotificationCenter(mWindow);
return mNotification;
}
PowerManager*
Navigator::GetMozPower(ErrorResult& aRv)
{
if (!mPowerManager) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mPowerManager = PowerManager::CreateInstance(mWindow);
if (!mPowerManager) {
// We failed to get the power manager service?
aRv.Throw(NS_ERROR_UNEXPECTED);
}
}
return mPowerManager;
}
already_AddRefed<WakeLock>
Navigator::RequestWakeLock(const nsAString &aTopic, ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
RefPtr<power::PowerManagerService> pmService =
power::PowerManagerService::GetInstance();
// Maybe it went away for some reason... Or maybe we're just called
// from our XPCOM method.
if (!pmService) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
return pmService->NewWakeLock(aTopic, mWindow, aRv);
}
already_AddRefed<LegacyMozTCPSocket>
Navigator::MozTCPSocket()
{
RefPtr<LegacyMozTCPSocket> socket = new LegacyMozTCPSocket(GetWindow());
return socket.forget();
}
#ifdef MOZ_GAMEPAD
void
Navigator::GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads,
ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
NS_ENSURE_TRUE_VOID(mWindow->GetDocShell());
nsGlobalWindow* win = nsGlobalWindow::Cast(mWindow);
win->SetHasGamepadEventListener(true);
win->GetGamepads(aGamepads);
}
GamepadServiceTest*
Navigator::RequestGamepadServiceTest()
{
if (!mGamepadServiceTest) {
mGamepadServiceTest = GamepadServiceTest::CreateTestService(mWindow);
}
return mGamepadServiceTest;
}
#endif
//*****************************************************************************
// Navigator::nsIMozNavigatorNetwork
//*****************************************************************************
NS_IMETHODIMP
Navigator::GetProperties(nsINetworkProperties** aProperties)
{
ErrorResult rv;
NS_IF_ADDREF(*aProperties = GetConnection(rv));
return NS_OK;
}
network::Connection*
Navigator::GetConnection(ErrorResult& aRv)
{
if (!mConnection) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mConnection = new network::Connection(mWindow);
}
return mConnection;
}
#ifdef MOZ_TIME_MANAGER
time::TimeManager*
Navigator::GetMozTime(ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!mTimeManager) {
mTimeManager = new time::TimeManager(mWindow);
}
return mTimeManager;
}
#endif
already_AddRefed<ServiceWorkerContainer>
Navigator::ServiceWorker()
{
MOZ_ASSERT(mWindow);
if (!mServiceWorkerContainer) {
mServiceWorkerContainer = new ServiceWorkerContainer(mWindow);
}
RefPtr<ServiceWorkerContainer> ref = mServiceWorkerContainer;
return ref.forget();
}
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::SetWindow(nsPIDOMWindowInner *aInnerWindow)
{
NS_ASSERTION(aInnerWindow->IsInnerWindow(),
"Navigator must get an inner window!");
mWindow = aInnerWindow;
}
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());
}
}
bool
Navigator::CheckPermission(const char* type)
{
return CheckPermission(mWindow, type);
}
/* static */
bool
Navigator::CheckPermission(nsPIDOMWindowInner* aWindow, const char* aType)
{
if (!aWindow) {
return false;
}
uint32_t permission = GetPermission(aWindow, aType);
return permission == nsIPermissionManager::ALLOW_ACTION;
}
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
system::AudioChannelManager*
Navigator::GetMozAudioChannelManager(ErrorResult& aRv)
{
if (!mAudioChannelManager) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mAudioChannelManager = new system::AudioChannelManager();
mAudioChannelManager->Init(mWindow);
}
return mAudioChannelManager;
}
#endif
JSObject*
Navigator::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
{
return NavigatorBinding::Wrap(cx, this, aGivenProto);
}
/* static */
bool
Navigator::HasWakeLockSupport(JSContext* /* unused*/, JSObject* /*unused */)
{
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
// No service means no wake lock support
return !!pmService;
}
/* static */
bool
Navigator::HasWifiManagerSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
// On XBL scope, the global object is NOT |window|. So we have
// to use nsContentUtils::GetObjectPrincipal to get the principal
// and test directly with permission manager.
nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal);
uint32_t permission = GetPermission(principal, "wifi-manage");
return permission == nsIPermissionManager::ALLOW_ACTION;
}
/* static */
bool
Navigator::HasUserMediaSupport(JSContext* /* unused */,
JSObject* /* unused */)
{
// Make enabling peerconnection enable getUserMedia() as well
return Preferences::GetBool("media.navigator.enabled", false) ||
Preferences::GetBool("media.peerconnection.enabled", false);
}
/* static */
bool
Navigator::IsE10sEnabled(JSContext* aCx, JSObject* aGlobal)
{
// We no longer support this, so always false.
// TODO: Remove this.
return false;
}
/* static */
already_AddRefed<nsPIDOMWindowInner>
Navigator::GetWindowFromGlobal(JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindowInner> win =
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(aGlobal));
MOZ_ASSERT(!win || win->IsInnerWindow());
return win.forget();
}
nsresult
Navigator::GetPlatform(nsAString& aPlatform, bool aUsePrefOverriddenValue)
{
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue && !nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.platform.override");
if (override) {
aPlatform = override;
return NS_OK;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
// Sorry for the #if platform ugliness, but Communicator is likewise
// hardcoded and we are seeking backward compatibility here (bug 47080).
#if defined(_WIN64)
aPlatform.AssignLiteral("Win64");
#elif defined(WIN32)
aPlatform.AssignLiteral("Win32");
#elif defined(XP_MACOSX) && defined(__ppc__)
aPlatform.AssignLiteral("MacPPC");
#elif defined(XP_MACOSX)
// Always return "MacIntel", even on ARM64 macOS like Safari does.
aPlatform.AssignLiteral("MacIntel");
#else
// XXX Communicator uses compiled-in build-time string defines
// to indicate the platform it was compiled *for*, not what it is
// currently running *on* which is what this does.
nsAutoCString plat;
rv = service->GetOscpu(plat);
CopyASCIItoUTF16(plat, aPlatform);
#endif
return rv;
}
/* static */ nsresult
Navigator::GetAppVersion(nsAString& aAppVersion, bool aUsePrefOverriddenValue)
{
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue && !nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.appversion.override");
if (override) {
aAppVersion = override;
return NS_OK;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString str;
rv = service->GetAppVersion(str);
CopyASCIItoUTF16(str, aAppVersion);
NS_ENSURE_SUCCESS(rv, rv);
aAppVersion.AppendLiteral(" (");
rv = service->GetPlatform(str);
NS_ENSURE_SUCCESS(rv, rv);
AppendASCIItoUTF16(str, aAppVersion);
aAppVersion.Append(char16_t(')'));
return rv;
}
/* static */ void
Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
{
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue && !nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.appname.override");
if (override) {
aAppName = override;
return;
}
}
aAppName.AssignLiteral("Netscape");
}
void
Navigator::ClearUserAgentCache()
{
NavigatorBinding::ClearCachedUserAgentValue(this);
}
nsresult
Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
bool aIsCallerChrome,
nsAString& aUserAgent)
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString ua;
rv = service->GetUserAgent(ua);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
CopyASCIItoUTF16(ua, aUserAgent);
if (!aWindow || !aURI) {
return NS_OK;
}
MOZ_ASSERT(aWindow->GetDocShell());
nsCOMPtr<nsISiteSpecificUserAgent> siteSpecificUA =
do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
if (!siteSpecificUA) {
return NS_OK;
}
return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
}
Clipboard*
Navigator::Clipboard()
{
if (!mClipboard) {
mClipboard = new dom::Clipboard(GetWindow());
}
return mClipboard;
}
} // namespace dom
} // namespace mozilla