Bug 1267941 - Implement Storage API estimate() feature, r=janv,baku

This commit is contained in:
Shawn Huang 2016-06-14 13:57:36 +01:00
parent 4667ca7c1b
commit ba9fa0d68a
22 changed files with 640 additions and 3 deletions

View File

@ -47,6 +47,7 @@
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/Presentation.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/TCPSocket.h"
#include "mozilla/dom/Telephony.h"
#include "mozilla/dom/Voicemail.h"
@ -232,6 +233,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTVManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputPortManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
#ifdef MOZ_B2G_RIL
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileConnections)
#endif
@ -276,6 +278,8 @@ Navigator::Invalidate()
mPermissions = nullptr;
mStorageManager = nullptr;
// If there is a page transition, make sure delete the geolocation object.
if (mGeolocation) {
mGeolocation->Shutdown();
@ -658,6 +662,21 @@ Navigator::GetPermissions(ErrorResult& aRv)
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

View File

@ -101,6 +101,7 @@ class DeviceStorageAreaListener;
class Presentation;
class LegacyMozTCPSocket;
class VRDisplay;
class StorageManager;
namespace time {
class TimeManager;
@ -287,6 +288,8 @@ public:
bool MozE10sEnabled();
StorageManager* Storage();
static void GetAcceptLanguages(nsTArray<nsString>& aLanguages);
// WebIDL helper methods
@ -377,6 +380,7 @@ private:
#endif
nsTArray<RefPtr<Promise> > mVRGetDisplaysPromises;
nsTArray<uint32_t> mRequestedVibrationPattern;
RefPtr<StorageManager> mStorageManager;
};
} // namespace dom

View File

@ -43,6 +43,32 @@ function storageUsage() {
});
}
function groupUsage() {
return new Promise(function(resolve, reject) {
navigator.storage.estimate().then(storageEstimation => {
resolve(storageEstimation.usage, 0);
});
});
}
function workerGroupUsage() {
return new Promise(function(resolve, reject) {
function workerScript() {
navigator.storage.estimate().then(storageEstimation => {
postMessage(storageEstimation.usage);
});
}
let url =
URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
let worker = new Worker(url);
worker.onmessage = function (e) {
resolve(e.data, 0);
};
});
}
function resetStorage() {
return new Promise(function(resolve, reject) {
var qms = SpecialPowers.Services.qms;
@ -142,6 +168,26 @@ SpecialPowers.pushPrefEnv({
ok(fullUsage > initialUsage, 'disk usage should have grown');
})
// Test groupUsage()
.then(function() {
return resetStorage();
}).then(function() {
return groupUsage();
}).then(function(usage) {
fullUsage = usage;
ok(fullUsage > initialUsage, 'disk group usage should have grown');
})
// Test workerGroupUsage()
.then(function() {
return resetStorage();
}).then(function() {
return workerGroupUsage();
}).then(function(usage) {
fullUsage = usage;
ok(fullUsage > initialUsage, 'disk group usage on worker should have grown');
})
// Now perform a new Cache operation that will reopen the origin. This
// should clean up the orphaned body.
.then(function() {

View File

@ -96,6 +96,7 @@ support-files =
unit/test_setVersion_events.js
unit/test_setVersion_exclusion.js
unit/test_setVersion_throw.js
unit/test_storage_manager_estimate.js
unit/test_success_events_after_abort.js
unit/test_table_locks.js
unit/test_table_rollback.js
@ -355,6 +356,8 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_setVersion_throw.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_storage_manager_estimate.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_success_events_after_abort.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_table_locks.html]

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Test for StorageManager</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7" src="unit/test_storage_manager_estimate.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,56 @@
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname :
"test_storage_manager_estimate.js";
const objectStoreName = "storagesManager";
const arraySize = 1e6;
ok('estimate' in navigator.storage, 'Has estimate function');
is(typeof navigator.storage.estimate, 'function', 'estimate is function');
ok(navigator.storage.estimate() instanceof Promise,
'estimate() method exists and returns a Promise');
navigator.storage.estimate().then(estimation => {
testGenerator.send(estimation.usage);
});
let before = yield undefined;
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = continueToNextStep;
let event = yield undefined;
let db = event.target.result;
db.onerror = errorHandler;
let objectStore = db.createObjectStore(objectStoreName, { });
yield undefined;
navigator.storage.estimate().then(estimation => {
testGenerator.send(estimation.usage);
});
let usageAfterCreate = yield undefined;
ok(usageAfterCreate > before, 'estimated usage must increase after createObjectStore');
let txn = db.transaction(objectStoreName, "readwrite");
objectStore = txn.objectStore(objectStoreName);
objectStore.put(new Uint8Array(arraySize), 'k');
txn.oncomplete = continueToNextStep;
txn.onabort = errorHandler;
txn.onerror = errorHandler;
event = yield undefined;
navigator.storage.estimate().then(estimation => {
testGenerator.send(estimation.usage);
});
let usageAfterPut = yield undefined;
ok(usageAfterPut > usageAfterCreate, 'estimated usage must increase after putting large object');
db.close();
finishTest();
yield undefined;
}

View File

@ -503,7 +503,6 @@ QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aCallback);
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
RefPtr<UsageRequest> request = new UsageRequest(aPrincipal, aCallback);

View File

@ -0,0 +1,353 @@
/* -*- 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/. */
#include "StorageManager.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/dom/StorageManagerBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ErrorResult.h"
#include "nsIQuotaCallbacks.h"
#include "nsIQuotaRequests.h"
#include "nsPIDOMWindow.h"
using namespace mozilla::dom::workers;
namespace mozilla {
namespace dom {
namespace {
// This class is used to get quota usage callback.
class EstimateResolver final
: public nsIQuotaUsageCallback
{
class FinishWorkerRunnable;
// If this resolver was created for a window then mPromise must be non-null.
// Otherwise mProxy must be non-null.
RefPtr<Promise> mPromise;
RefPtr<PromiseWorkerProxy> mProxy;
nsresult mResultCode;
StorageEstimate mStorageEstimate;
public:
explicit EstimateResolver(Promise* aPromise)
: mPromise(aPromise)
, mResultCode(NS_OK)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPromise);
}
explicit EstimateResolver(PromiseWorkerProxy* aProxy)
: mProxy(aProxy)
, mResultCode(NS_OK)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aProxy);
}
void
ResolveOrReject(Promise* aPromise);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIQUOTAUSAGECALLBACK
private:
~EstimateResolver()
{ }
};
// This class is used to return promise on worker thread.
class EstimateResolver::FinishWorkerRunnable final
: public WorkerRunnable
{
RefPtr<EstimateResolver> mResolver;
public:
explicit FinishWorkerRunnable(EstimateResolver* aResolver)
: WorkerRunnable(aResolver->mProxy->GetWorkerPrivate())
, mResolver(aResolver)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aResolver);
}
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
};
class EstimateWorkerMainThreadRunnable
: public WorkerMainThreadRunnable
{
RefPtr<PromiseWorkerProxy> mProxy;
public:
EstimateWorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aProxy)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("StorageManager :: Estimate"))
, mProxy(aProxy)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aProxy);
}
virtual bool
MainThreadRun() override;
};
nsresult
GetUsageForPrincipal(nsIPrincipal* aPrincipal,
nsIQuotaUsageCallback* aCallback,
nsIQuotaUsageRequest** aRequest)
{
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aCallback);
MOZ_ASSERT(aRequest);
nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate();
if (NS_WARN_IF(!qms)) {
return NS_ERROR_FAILURE;
}
nsresult rv = qms->GetUsageForPrincipal(aPrincipal, aCallback, true, aRequest);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
};
nsresult
GetStorageEstimate(nsIQuotaUsageRequest* aRequest,
StorageEstimate& aStorageEstimate)
{
MOZ_ASSERT(aRequest);
uint64_t usage;
nsresult rv = aRequest->GetUsage(&usage);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
uint64_t limit;
rv = aRequest->GetLimit(&limit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
aStorageEstimate.mUsage.Construct() = usage;
aStorageEstimate.mQuota.Construct() = limit;
return NS_OK;
}
} // namespace
/*******************************************************************************
* Local class implementations
******************************************************************************/
void
EstimateResolver::ResolveOrReject(Promise* aPromise)
{
MOZ_ASSERT(aPromise);
if (NS_SUCCEEDED(mResultCode)) {
aPromise->MaybeResolve(mStorageEstimate);
} else {
aPromise->MaybeReject(mResultCode);
}
}
NS_IMPL_ISUPPORTS(EstimateResolver, nsIQuotaUsageCallback)
NS_IMETHODIMP
EstimateResolver::OnUsageResult(nsIQuotaUsageRequest *aRequest)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequest);
nsresult rv = aRequest->GetResultCode(&mResultCode);
if (NS_WARN_IF(NS_FAILED(rv))) {
mResultCode = rv;
} else if (NS_SUCCEEDED(mResultCode)) {
rv = GetStorageEstimate(aRequest, mStorageEstimate);
if (NS_WARN_IF(NS_FAILED(rv))) {
mResultCode = rv;
}
}
// In a main thread request.
if (!mProxy) {
MOZ_ASSERT(mPromise);
ResolveOrReject(mPromise);
return NS_OK;
}
// In a worker thread request.
MutexAutoLock lock(mProxy->Lock());
if (NS_WARN_IF(mProxy->CleanedUp())) {
return NS_ERROR_FAILURE;
}
RefPtr<FinishWorkerRunnable> runnable = new FinishWorkerRunnable(this);
if (NS_WARN_IF(!runnable->Dispatch())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
EstimateResolver::
FinishWorkerRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<PromiseWorkerProxy> proxy = mResolver->mProxy;
MOZ_ASSERT(proxy);
RefPtr<Promise> promise = proxy->WorkerPromise();
MOZ_ASSERT(promise);
mResolver->ResolveOrReject(promise);
proxy->CleanUp();
return true;
}
bool
EstimateWorkerMainThreadRunnable::MainThreadRun()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPrincipal> principal;
{
MutexAutoLock lock(mProxy->Lock());
if (mProxy->CleanedUp()) {
return true;
}
principal = mProxy->GetWorkerPrivate()->GetPrincipal();
}
MOZ_ASSERT(principal);
RefPtr<EstimateResolver> resolver = new EstimateResolver(mProxy);
RefPtr<nsIQuotaUsageRequest> request;
nsresult rv =
GetUsageForPrincipal(principal, resolver, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return true;
}
/*******************************************************************************
* StorageManager
******************************************************************************/
StorageManager::StorageManager(nsIGlobalObject* aGlobal)
: mOwner(aGlobal)
{
MOZ_ASSERT(aGlobal);
}
StorageManager::~StorageManager()
{
}
already_AddRefed<Promise>
StorageManager::Estimate(ErrorResult& aRv)
{
MOZ_ASSERT(mOwner);
RefPtr<Promise> promise = Promise::Create(mOwner, aRv);
if (NS_WARN_IF(!promise)) {
return nullptr;
}
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mOwner);
if (NS_WARN_IF(!window)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
MOZ_ASSERT(principal);
RefPtr<EstimateResolver> resolver = new EstimateResolver(promise);
RefPtr<nsIQuotaUsageRequest> request;
nsresult rv =
GetUsageForPrincipal(principal, resolver, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
return promise.forget();
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (NS_WARN_IF(!promiseProxy)) {
return nullptr;
}
RefPtr<EstimateWorkerMainThreadRunnable> runnnable =
new EstimateWorkerMainThreadRunnable(promiseProxy->GetWorkerPrivate(),
promiseProxy);
runnnable->Dispatch(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return promise.forget();
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(StorageManager, mOwner)
NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageManager)
NS_IMPL_CYCLE_COLLECTING_RELEASE(StorageManager)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StorageManager)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
StorageManager::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return StorageManagerBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,55 @@
/* -*- 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/. */
#ifndef mozilla_dom_StorageManager_h
#define mozilla_dom_StorageManager_h
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupports.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class Promise;
struct StorageEstimate;
class StorageManager final
: public nsISupports
, public nsWrapperCache
{
nsCOMPtr<nsIGlobalObject> mOwner;
public:
explicit
StorageManager(nsIGlobalObject* aGlobal);
nsIGlobalObject*
GetParentObject() const
{
return mOwner;
}
// WebIDL
already_AddRefed<Promise>
Estimate(ErrorResult& aRv);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StorageManager)
// nsWrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
private:
~StorageManager();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_StorageManager_h

View File

@ -12,6 +12,10 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'dom_quota'
EXPORTS.mozilla.dom += [
'StorageManager.h',
]
EXPORTS.mozilla.dom.quota += [
'ActorsParent.h',
'Client.h',
@ -32,6 +36,7 @@ UNIFIED_SOURCES += [
'FileStreams.cpp',
'QuotaManagerService.cpp',
'QuotaRequests.cpp',
'StorageManager.cpp',
]
IPDL_SOURCES += [
@ -44,6 +49,7 @@ include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'../workers',
'/caps',
]

View File

@ -1069,6 +1069,8 @@ var interfaceNamesInGlobalScope =
"Storage",
// IMPORTANT: Do not change this list without review from a DOM peer!
"StorageEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"StorageManager",
// IMPORTANT: Do not change this list without review from a DOM peer!
"StyleSheet",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -30,6 +30,7 @@ Navigator implements NavigatorOnLine;
Navigator implements NavigatorContentUtils;
Navigator implements NavigatorStorageUtils;
Navigator implements NavigatorConcurrentHardware;
Navigator implements NavigatorStorage;
[NoInterfaceObject, Exposed=(Window,Worker)]
interface NavigatorID {
@ -85,6 +86,11 @@ interface NavigatorContentUtils {
//void unregisterContentHandler(DOMString mimeType, DOMString url);
};
[NoInterfaceObject, Exposed=(Window,Worker)]
interface NavigatorStorage {
readonly attribute StorageManager storage;
};
[NoInterfaceObject]
interface NavigatorStorageUtils {
// NOT IMPLEMENTED

View File

@ -0,0 +1,24 @@
/* -*- Mode: IDL; tab-width: 2; 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/.
*
* The origin of this IDL file is
* https://storage.spec.whatwg.org/#storagemanager
*
*/
[Exposed=(Window,Worker)]
interface StorageManager {
// [Throws]
// Promise<boolean> persisted();
// [Throws]
// [Exposed=Window] Promise<boolean> persist();
[Throws]
Promise<StorageEstimate> estimate();
};
dictionary StorageEstimate {
unsigned long long usage;
unsigned long long quota;
};

View File

@ -11,3 +11,4 @@ WorkerNavigator implements NavigatorID;
WorkerNavigator implements NavigatorLanguage;
WorkerNavigator implements NavigatorOnLine;
WorkerNavigator implements NavigatorConcurrentHardware;
WorkerNavigator implements NavigatorStorage;

View File

@ -433,6 +433,7 @@ WEBIDL_FILES = [
'StereoPannerNode.webidl',
'Storage.webidl',
'StorageEvent.webidl',
'StorageManager.webidl',
'StorageType.webidl',
'StyleSheet.webidl',
'StyleSheetList.webidl',

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/WorkerNavigator.h"
#include "mozilla/dom/WorkerNavigatorBinding.h"
@ -26,8 +27,7 @@ namespace dom {
using namespace mozilla::dom::workers;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerNavigator)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WorkerNavigator, mStorageManager);
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerNavigator, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerNavigator, Release)
@ -164,5 +164,21 @@ WorkerNavigator::HardwareConcurrency() const
return rts->ClampedHardwareConcurrency();
}
StorageManager*
WorkerNavigator::Storage()
{
if (!mStorageManager) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<nsIGlobalObject> global = workerPrivate->GlobalScope();
MOZ_ASSERT(global);
mStorageManager = new StorageManager(global);
}
return mStorageManager;
}
} // namespace dom
} // namespace mozilla

View File

@ -15,12 +15,14 @@
namespace mozilla {
namespace dom {
class Promise;
class StorageManager;
class WorkerNavigator final : public nsWrapperCache
{
typedef struct workers::RuntimeService::NavigatorProperties NavigatorProperties;
NavigatorProperties mProperties;
RefPtr<StorageManager> mStorageManager;
bool mOnline;
WorkerNavigator(const NavigatorProperties& aProperties,
@ -101,6 +103,8 @@ public:
void SetLanguages(const nsTArray<nsString>& aLanguages);
uint64_t HardwareConcurrency() const;
StorageManager* Storage();
};
} // namespace dom

View File

@ -15,6 +15,7 @@ var supportedProps = [
"language",
"languages",
"hardwareConcurrency",
"storage"
];
self.onmessage = function(event) {

View File

@ -98,6 +98,19 @@ function workerTestGetIsB2G(cb) {
});
}
function workerTestGetStorageManager(cb) {
addEventListener('message', function workerTestGetStorageManagerCB(e) {
if (e.data.type !== 'returnStorageManager') {
return;
}
removeEventListener('message', workerTestGetStorageManagerCB);
cb(e.data.result);
});
client.postMessage({
type: 'getStorageManager'
});
}
addEventListener('message', function workerWrapperOnMessage(e) {
removeEventListener('message', workerWrapperOnMessage);
var data = e.data;

View File

@ -195,6 +195,8 @@ var interfaceNamesInGlobalScope =
"ServiceWorkerGlobalScope",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ServiceWorkerRegistration",
// IMPORTANT: Do not change this list without review from a DOM peer!
"StorageManager",
// IMPORTANT: Do not change this list without review from a DOM peer!
"SubtleCrypto",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -40,6 +40,11 @@ Tests of DOM Worker Navigator
return;
}
if (args.name === "storage") {
is(typeof navigator.storage, typeof args.value, "storage type matches");
return;
}
is(navigator[args.name], args.value,
"Mismatched navigator string for " + args.name + "!");
};

View File

@ -179,6 +179,8 @@ var interfaceNamesInGlobalScope =
"Response",
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "ServiceWorkerRegistration", b2g: false },
// IMPORTANT: Do not change this list without review from a DOM peer!
"StorageManager",
// IMPORTANT: Do not change this list without review from a DOM peer!
"SubtleCrypto",
// IMPORTANT: Do not change this list without review from a DOM peer!