Bug 1778489 - Define window.indexedDB as null in Private Browsing Mode r=twisniewski,asuth

https://www.msn.com/ja-jp/feed accesses `indexedDB` as a global variable and thus it throws undefined identifier error when it's not defined. Returning null fixes the regression.

Differential Revision: https://phabricator.services.mozilla.com/D151373
This commit is contained in:
Kagami Sascha Rosylight 2022-07-08 16:05:25 +00:00
parent 28609ba87e
commit f4fcfcd045
8 changed files with 121 additions and 65 deletions

View File

@ -5047,7 +5047,14 @@ Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
return mLocalStorage;
}
IDBFactory* nsGlobalWindowInner::GetIndexedDB(ErrorResult& aError) {
IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
ErrorResult& aError) {
if (!IDBFactory::IsEnabled(aCx, AsGlobal()->GetGlobalJSObject())) {
// Let window.indexedDB be an attribute with a null value, to prevent
// undefined identifier error
return nullptr;
}
if (!mIndexedDB) {
// This may keep mIndexedDB null without setting an error.
auto res = IDBFactory::CreateForWindow(this);

View File

@ -744,7 +744,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
mozilla::dom::Storage* GetSessionStorage(mozilla::ErrorResult& aError);
mozilla::dom::Storage* GetLocalStorage(mozilla::ErrorResult& aError);
mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError);
mozilla::dom::IDBFactory* GetIndexedDB(JSContext* aCx,
mozilla::ErrorResult& aError);
already_AddRefed<nsICSSDeclaration> GetComputedStyle(
mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
mozilla::ErrorResult& aError) override;

View File

@ -2,66 +2,107 @@
const isWorker = typeof DedicatedWorkerGlobalScope === "function";
function check(content, expected, item) {
const exposed = expected ? "is exposed without" : "is not exposed with";
const worker = isWorker ? "in worker" : "in window";
is(
content.eval(`!!globalThis.${item}`),
expected,
`${item} ${exposed} pbmode ${worker}`
);
}
function checkCaches(content, expected) {
check(content, expected, "caches");
check(content, expected, "Cache");
check(content, expected, "CacheStorage");
}
function checkIDB(content, expected) {
check(content, expected, "indexedDB");
check(content, expected, "IDBFactory");
check(content, expected, "IDBKeyRange");
check(content, expected, "IDBOpenDBRequest");
check(content, expected, "IDBRequest");
check(content, expected, "IDBVersionChangeEvent");
// These are always accessed by jakearchibald/idb@v3 without existence checks.
// https://github.com/jakearchibald/idb/blob/e1c7c44dbba38415745afc782b8e247da8c833f2/lib/idb.mjs#L152
check(content, true, "IDBCursor");
check(content, true, "IDBDatabase");
check(content, true, "IDBIndex");
check(content, true, "IDBObjectStore");
check(content, true, "IDBTransaction");
}
function checkSW(content, expected) {
if (isWorker) {
// Currently not supported. Bug 1131324
return;
function checkAll(content, inPrivateBrowsing) {
function check(
item,
{ valueExpected, enumerationExpected, parent = "globalThis" }
) {
const exposed = valueExpected ? "is exposed" : "is not exposed";
const pbmode = inPrivateBrowsing ? "with pbmode" : "without pbmode";
const enumerated = enumerationExpected
? "is enumerated"
: "is not enumerated";
const worker = isWorker ? "in worker" : "in window";
is(
content.eval(`!!${parent}.${item}`),
valueExpected,
`${parent}.${item} ${exposed} ${pbmode} ${worker}`
);
is(
content.eval(`"${item}" in ${parent}`),
enumerationExpected,
`${parent}.${item} ${enumerated} ${pbmode} ${worker}`
);
}
check(content, expected, "navigator.serviceWorker");
check(content, expected, "ServiceWorker");
check(content, expected, "ServiceWorkerContainer");
check(content, expected, "ServiceWorkerRegistration");
check(content, expected, "NavigationPreloadManager");
check(content, expected, "PushManager");
check(content, expected, "PushSubscription");
check(content, expected, "PushSubscriptionOptions");
}
function checkAll(content, expected) {
checkCaches(content, expected);
checkIDB(content, expected);
checkSW(content, expected);
function checkNotExposedInPBM(item, parent) {
check(item, {
valueExpected: !inPrivateBrowsing,
enumerationExpected: !inPrivateBrowsing,
parent,
});
}
function checkCaches() {
checkNotExposedInPBM("caches");
checkNotExposedInPBM("Cache");
checkNotExposedInPBM("CacheStorage");
}
function checkIDB() {
checkNotExposedInPBM("IDBFactory");
checkNotExposedInPBM("IDBKeyRange");
checkNotExposedInPBM("IDBOpenDBRequest");
checkNotExposedInPBM("IDBRequest");
checkNotExposedInPBM("IDBVersionChangeEvent");
// These are always accessed by jakearchibald/idb@v3 without existence checks.
// https://github.com/jakearchibald/idb/blob/e1c7c44dbba38415745afc782b8e247da8c833f2/lib/idb.mjs#L152
check("IDBCursor", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBDatabase", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBIndex", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBObjectStore", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBTransaction", {
valueExpected: true,
enumerationExpected: true,
});
// https://www.msn.com/feed accesses indexedDB as a global variable without existence check
// We need to always expose the attribute itself
check("indexedDB", {
valueExpected: !inPrivateBrowsing,
enumerationExpected: true,
});
}
function checkSW() {
if (isWorker) {
// Currently not supported. Bug 1131324
return;
}
checkNotExposedInPBM("serviceWorker", "navigator");
checkNotExposedInPBM("ServiceWorker");
checkNotExposedInPBM("ServiceWorkerContainer");
checkNotExposedInPBM("ServiceWorkerRegistration");
checkNotExposedInPBM("NavigationPreloadManager");
checkNotExposedInPBM("PushManager");
checkNotExposedInPBM("PushSubscription");
checkNotExposedInPBM("PushSubscriptionOptions");
}
checkCaches();
checkIDB();
checkSW();
}
if (isWorker) {
importScripts("/tests/SimpleTest/WorkerSimpleTest.js");
globalThis.onmessage = ev => {
const { expected } = ev.data;
checkAll(globalThis, expected);
const { inPrivateBrowsing } = ev.data;
checkAll(globalThis, inPrivateBrowsing);
postMessage({
kind: "info",
next: true,

View File

@ -27,11 +27,11 @@
});
}
function runWorkerTest(content, expected) {
function runWorkerTest(content, inPrivateBrowsing) {
return new Promise((resolve, reject) => {
/** @type {Worker} */
const worker = content.eval("new Worker('/chrome/dom/base/test/chrome/file_hide_in_pbmode.js')");
worker.postMessage({ expected });
worker.postMessage({ inPrivateBrowsing });
worker.onerror = reject;
listenForTests(worker);
worker.addEventListener("message", ev => {
@ -46,13 +46,13 @@
async function runTest() {
// sanity check
let win = await openBrowserWindow(contentPage, { private: false });
checkAll(win.content, true);
await runWorkerTest(win.content, true);
checkAll(win.content, false);
await runWorkerTest(win.content, false);
win.close();
win = await openBrowserWindow(contentPage, { private: true });
checkAll(win.content, false);
await runWorkerTest(win.content, false);
checkAll(win.content, true);
await runWorkerTest(win.content, true);
win.close();
SimpleTest.finish();

View File

@ -1600,7 +1600,7 @@ DOMInterfaces = {
'nativeType': 'nsGlobalWindowInner',
'headerFile': 'nsGlobalWindow.h',
'implicitJSContext': [
'requestIdleCallback'
'requestIdleCallback', 'indexedDB'
],
},
@ -1627,7 +1627,7 @@ DOMInterfaces = {
'WorkerGlobalScope': {
'headerFile': 'mozilla/dom/WorkerScope.h',
'implicitJSContext': [ 'importScripts' ],
'implicitJSContext': [ 'importScripts', 'indexedDB' ],
},
'Worklet': {

View File

@ -70,7 +70,7 @@ partial interface mixin WindowOrWorkerGlobalScope {
// http://w3c.github.io/IndexedDB/#factory-interface
partial interface mixin WindowOrWorkerGlobalScope {
// readonly attribute IDBFactory indexedDB; // bug 1776789
[Throws, Func="IDBFactory::IsEnabled"]
[Throws]
readonly attribute IDBFactory? indexedDB;
};

View File

@ -676,9 +676,15 @@ already_AddRefed<Promise> WorkerGlobalScope::Fetch(
}
already_AddRefed<IDBFactory> WorkerGlobalScope::GetIndexedDB(
ErrorResult& aErrorResult) {
JSContext* aCx, ErrorResult& aErrorResult) {
AssertIsOnWorkerThread();
if (!IDBFactory::IsEnabled(aCx, GetGlobalJSObject())) {
// Let window.indexedDB be an attribute with a null value, to prevent
// undefined identifier error
return nullptr;
}
RefPtr<IDBFactory> indexedDB = mIndexedDB;
if (!indexedDB) {

View File

@ -312,7 +312,8 @@ class WorkerGlobalScope : public WorkerGlobalScopeBase {
bool IsSecureContext() const;
already_AddRefed<IDBFactory> GetIndexedDB(ErrorResult& aErrorResult);
already_AddRefed<IDBFactory> GetIndexedDB(JSContext* aCx,
ErrorResult& aErrorResult);
already_AddRefed<cache::CacheStorage> GetCaches(ErrorResult& aRv);