passbolt-browser-extension 5.2.0 → 5.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -1
- package/Gruntfile.js +1 -1
- package/RELEASE_NOTES.md +27 -71
- package/doc/browser-extension-class-diagram.md +9 -0
- package/package.json +5 -5
- package/src/all/_locales/sl/messages.json +2 -2
- package/src/all/background_page/controller/clipboard/cancelClipboardContentFlushController.js +51 -0
- package/src/all/background_page/controller/clipboard/cancelClipboardContentFlushController.test.js +46 -0
- package/src/all/background_page/controller/clipboard/copyTemporarilyToClipboardController.js +53 -0
- package/src/all/background_page/controller/clipboard/copyTemporarilyToClipboardController.test.js +47 -0
- package/src/all/background_page/controller/clipboard/{clipboardController.js → copyToClipboardController.js} +8 -8
- package/src/all/background_page/controller/clipboard/copyToClipboardController.test.js +47 -0
- package/src/all/background_page/controller/extension/getExtensionVersionController.test.js +1 -1
- package/src/all/background_page/controller/metadata/getOrFindMetadataKeysSettingsController.js +53 -0
- package/src/all/background_page/controller/metadata/getOrFindMetadataKeysSettingsController.test.js +106 -0
- package/src/all/background_page/controller/resource/updateResourceLocalStorageByFolderParentIdController.js +70 -0
- package/src/all/background_page/controller/resource/updateResourceLocalStorageByFolderParentIdController.test.js +78 -0
- package/src/all/background_page/controller/share/shareOneFolderController.test.js +2 -1
- package/src/all/background_page/controller/share/shareResourcesController.test.js +1 -1
- package/src/all/background_page/controller/user/deleteDryRunUserController.js +73 -0
- package/src/all/background_page/controller/user/deleteDryRunUserController.test.js +129 -0
- package/src/all/background_page/controller/user/deleteUserController.js +76 -0
- package/src/all/background_page/controller/user/deleteUserController.test.js +141 -0
- package/src/all/background_page/event/actionLogEvents.js +5 -12
- package/src/all/background_page/event/appEvents.js +33 -0
- package/src/all/background_page/event/findAllForActionLogController.js +58 -0
- package/src/all/background_page/event/findAllForActionLogController.test.js +43 -0
- package/src/all/background_page/event/quickAccessEvents.js +32 -0
- package/src/all/background_page/event/resourceEvents.js +11 -0
- package/src/all/background_page/event/userEvents.js +7 -20
- package/src/all/background_page/event/webIntegrationEvents.js +11 -0
- package/src/all/background_page/model/actionLog/{actionLogModel.js → findActionLogService.js} +25 -5
- package/src/all/background_page/model/actionLog/findActionLogService.test.js +61 -0
- package/src/all/background_page/model/comment/commentModel.js +5 -5
- package/src/all/background_page/model/entity/actionLog/actionLogsCollection.test.data.js +18 -0
- package/src/all/background_page/model/entity/actionLog/defaultActionLogEntity.test.data.js +34 -0
- package/src/all/background_page/model/entity/resource/resourcesCollection.js +25 -0
- package/src/all/background_page/model/entity/resource/resourcesCollection.test.js +34 -0
- package/src/all/background_page/model/import/resources/resourcesKdbxImportParser.test.js +0 -1
- package/src/all/background_page/model/user/userModel.js +0 -60
- package/src/all/background_page/pagemod/appPagemod.js +0 -2
- package/src/all/background_page/pagemod/appPagemod.test.js +2 -6
- package/src/all/background_page/service/alarm/globalAlarmService.js +2 -0
- package/src/all/background_page/service/api/actionLog/{actionLogService.js → actionLogApiService.js} +5 -4
- package/src/all/background_page/service/api/actionLog/actionLogApiService.test.js +55 -0
- package/src/all/background_page/service/api/comment/{commentService.js → commentApiService.js} +6 -7
- package/src/all/background_page/service/api/comment/commentApiService.test.js +122 -0
- package/src/all/background_page/service/api/resource/resourceService.js +1 -0
- package/src/all/background_page/service/auth/postLogoutService.js +2 -0
- package/src/all/background_page/service/auth/postLogoutService.test.js +4 -1
- package/src/all/background_page/service/browser/browserService.js +22 -0
- package/src/all/background_page/service/clipboard/clipboardProviderService.js +40 -0
- package/src/all/background_page/service/clipboard/clipboardProviderService.test.js +61 -0
- package/src/all/background_page/service/clipboard/copyToClipboardService.js +123 -0
- package/src/all/background_page/service/clipboard/copyToClipboardService.test.js +174 -0
- package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.js +52 -1
- package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.test.js +100 -0
- package/src/all/background_page/service/resource/findResourcesService.js +53 -19
- package/src/all/background_page/service/resource/findResourcesService.test.js +191 -0
- package/src/all/background_page/service/resourceType/updateResourceTypesService.test.js +1 -1
- package/src/all/background_page/service/share/shareResourceService.test.js +1 -1
- package/src/all/background_page/service/user/deleteUserService.js +97 -0
- package/src/all/background_page/service/user/deleteUserService.test.js +178 -0
- package/src/all/locales/de-DE/common.json +2 -2
- package/src/all/locales/es-ES/common.json +2 -2
- package/src/all/locales/fr-FR/common.json +5 -5
- package/src/all/locales/it-IT/common.json +2 -2
- package/src/all/locales/ja-JP/common.json +2 -2
- package/src/all/locales/ko-KR/common.json +2 -2
- package/src/all/locales/lt-LT/common.json +2 -2
- package/src/all/locales/nl-NL/common.json +2 -2
- package/src/all/locales/pl-PL/common.json +5 -5
- package/src/all/locales/pt-BR/common.json +2 -2
- package/src/all/locales/ro-RO/common.json +2 -2
- package/src/all/locales/ru-RU/common.json +2 -2
- package/src/all/locales/sl-SI/common.json +33 -33
- package/src/all/locales/sv-SE/common.json +2 -2
- package/src/all/locales/uk-UA/common.json +3 -3
- package/src/chrome/manifest.json +1 -1
- package/src/chrome/polyfill/clipboard/edgeBackgroundPageClipboardService.js +31 -0
- package/src/chrome/polyfill/clipboard/edgeBackgroundPageClipboardService.test.js +51 -0
- package/src/chrome-mv3/index.js +3 -3
- package/src/chrome-mv3/manifest.json +1 -1
- package/src/chrome-mv3/offscreens/{fetch.html → offscreen.html} +1 -1
- package/src/chrome-mv3/offscreens/{fetch.js → offscreen.js} +2 -2
- package/src/chrome-mv3/offscreens/service/clipboard/writeClipobardOffscreenService.js +54 -0
- package/src/chrome-mv3/offscreens/service/clipboard/writeClipobardOffscreenService.test.js +56 -0
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.js +36 -44
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.data.js +0 -1
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.js +90 -120
- package/src/chrome-mv3/offscreens/service/offscreen/handleOffscreenRequestService.js +85 -0
- package/src/chrome-mv3/offscreens/service/offscreen/handleOffscreenRequestService.test.js +99 -0
- package/src/chrome-mv3/polyfill/clipboardOffscreenPolyfill.js +19 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/requestClipboardOffscreenService.js +51 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/requestClipboardOffscreenService.test.js +70 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.js +25 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.test.data.js +21 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.test.js +33 -0
- package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.js +25 -50
- package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.test.js +16 -39
- package/src/chrome-mv3/serviceWorker/service/network/responseFetchOffscreenService.js +14 -45
- package/src/chrome-mv3/serviceWorker/service/network/responseFetchOffscreenService.test.js +5 -37
- package/src/chrome-mv3/serviceWorker/service/offscreen/createOffscreenDocumentService.js +43 -0
- package/src/chrome-mv3/serviceWorker/service/offscreen/createOffscreenDocumentService.test.js +48 -0
- package/src/chrome-mv3/serviceWorker/service/offscreen/handleOffscreenResponseService.js +119 -0
- package/src/chrome-mv3/serviceWorker/service/offscreen/handleOffscreenResponseService.test.js +159 -0
- package/src/firefox/manifest.json +1 -1
- package/src/safari/manifest.json +1 -1
- package/test/jest.setup.js +4 -0
- package/test/mocks/mockNavigatorClipboard.js +40 -0
- package/test/mocks/mockWebExtensionPolyfill.js +2 -1
- package/{webpack-offscreens.fetch.config.js → webpack-offscreens.config.js} +1 -1
- package/webpack.service-worker.config.js +1 -0
- package/src/all/background_page/controller/clipboard/clipboardController.test.js +0 -68
- package/src/all/background_page/event/clipboardEvents.js +0 -28
package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.test.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.3.2
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import ResponseClipboardOffscreenService from "./responseClipboardOffscreenService";
|
|
16
|
+
import {defaultClipboardWriteResponseMessage} from "./responseClipboardOffscreenService.test.data";
|
|
17
|
+
import {defaultCallbacks} from "../network/responseFetchOffscreenService.test.data";
|
|
18
|
+
|
|
19
|
+
describe("ResponseClipboardOffscreenService", () => {
|
|
20
|
+
describe("::handleClipboardResponse", () => {
|
|
21
|
+
it("should resolve the promise from the response callback", () => {
|
|
22
|
+
expect.assertions(1);
|
|
23
|
+
|
|
24
|
+
const id = crypto.randomUUID();
|
|
25
|
+
const callbacks = defaultCallbacks();
|
|
26
|
+
|
|
27
|
+
const message = defaultClipboardWriteResponseMessage({id});
|
|
28
|
+
ResponseClipboardOffscreenService.handleClipboardResponse(message, callbacks);
|
|
29
|
+
|
|
30
|
+
expect(callbacks.resolve).toHaveBeenCalledTimes(1);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -16,27 +16,21 @@ import {
|
|
|
16
16
|
FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA,
|
|
17
17
|
FETCH_OFFSCREEN_DATA_TYPE_JSON
|
|
18
18
|
} from "../../../offscreens/service/network/fetchOffscreenService";
|
|
19
|
+
import CreateOffscreenDocumentService from "../offscreen/createOffscreenDocumentService";
|
|
20
|
+
import HandleOffscreenResponseService from "../offscreen/handleOffscreenResponseService";
|
|
19
21
|
|
|
20
22
|
const {SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} = require("../../../offscreens/service/network/fetchOffscreenService");
|
|
21
23
|
|
|
22
24
|
export const IS_FETCH_OFFSCREEN_PREFERRED_STORAGE_KEY = "IS_FETCH_OFFSCREEN_PREFERRED_STORAGE_KEY";
|
|
23
|
-
const LOCK_CREATE_OFFSCREEN_FETCH_DOCUMENT = "LOCK_CREATE_OFFSCREEN_FETCH_DOCUMENT";
|
|
24
|
-
const FETCH_OFFSCREEN_DOCUMENT_REASON = "WORKERS";
|
|
25
|
-
const OFFSCREEN_URL = "offscreens/fetch.html";
|
|
26
25
|
|
|
27
26
|
export class RequestFetchOffscreenService {
|
|
28
27
|
/**
|
|
29
28
|
* Preferred strategy cache.
|
|
30
29
|
* @type {boolean|null}
|
|
30
|
+
* @private
|
|
31
31
|
*/
|
|
32
32
|
static isFetchOffscreenPreferredCache = null;
|
|
33
33
|
|
|
34
|
-
/**
|
|
35
|
-
* The stack of requests promises callbacks using the request id as reference.
|
|
36
|
-
* @type {object}
|
|
37
|
-
*/
|
|
38
|
-
static offscreenRequestsPromisesCallbacks = {};
|
|
39
|
-
|
|
40
34
|
/**
|
|
41
35
|
* Fetch external service through fetch offscreen document.
|
|
42
36
|
* @param {string} resource The fetch url resource, similar to the native fetch resource parameter.
|
|
@@ -54,6 +48,7 @@ export class RequestFetchOffscreenService {
|
|
|
54
48
|
/**
|
|
55
49
|
* Check if the fetch offscreen strategy is preferred.
|
|
56
50
|
* @returns {Promise<boolean>}
|
|
51
|
+
* @private
|
|
57
52
|
*/
|
|
58
53
|
static async isFetchOffscreenPreferred() {
|
|
59
54
|
if (RequestFetchOffscreenService.isFetchOffscreenPreferredCache === null) {
|
|
@@ -69,6 +64,7 @@ export class RequestFetchOffscreenService {
|
|
|
69
64
|
* @param {string} resource The fetch url resource, similar to the native fetch resource parameter.
|
|
70
65
|
* @param {object} options The fetch options, similar to the native fetch option parameter.
|
|
71
66
|
* @returns {Promise<Response>}
|
|
67
|
+
* @private
|
|
72
68
|
*/
|
|
73
69
|
static async fetchNative(resource, options) {
|
|
74
70
|
try {
|
|
@@ -89,53 +85,30 @@ export class RequestFetchOffscreenService {
|
|
|
89
85
|
* @param {string} resource The fetch url resource, similar to the native fetch resource parameter.
|
|
90
86
|
* @param {object} options The fetch options, similar to the native fetch option parameter.
|
|
91
87
|
* @returns {Promise<Response>}
|
|
88
|
+
* @private
|
|
92
89
|
*/
|
|
93
90
|
static async fetchOffscreen(resource, options) {
|
|
94
|
-
|
|
95
|
-
await navigator.locks.request(
|
|
96
|
-
LOCK_CREATE_OFFSCREEN_FETCH_DOCUMENT,
|
|
97
|
-
RequestFetchOffscreenService.createIfNotExistOffscreenDocument);
|
|
91
|
+
await CreateOffscreenDocumentService.createIfNotExistOffscreenDocument();
|
|
98
92
|
|
|
99
|
-
const
|
|
100
|
-
const offscreenFetchData = await RequestFetchOffscreenService.buildOffscreenData(
|
|
93
|
+
const requestId = crypto.randomUUID();
|
|
94
|
+
const offscreenFetchData = await RequestFetchOffscreenService.buildOffscreenData(resource, options);
|
|
101
95
|
|
|
102
96
|
return new Promise((resolve, reject) => {
|
|
103
97
|
// Stack the response listener callbacks.
|
|
104
|
-
|
|
105
|
-
return RequestFetchOffscreenService.sendOffscreenMessage(offscreenFetchData)
|
|
98
|
+
HandleOffscreenResponseService.setResponseCallback(requestId, {resolve, reject});
|
|
99
|
+
return RequestFetchOffscreenService.sendOffscreenMessage(requestId, offscreenFetchData)
|
|
106
100
|
.catch(reject);
|
|
107
101
|
});
|
|
108
102
|
}
|
|
109
103
|
|
|
110
|
-
/**
|
|
111
|
-
* Create fetch offscreen document if it does not exist yet.
|
|
112
|
-
* @returns {Promise<void>}
|
|
113
|
-
*/
|
|
114
|
-
static async createIfNotExistOffscreenDocument() {
|
|
115
|
-
const existingContexts = await chrome.runtime.getContexts({
|
|
116
|
-
contextTypes: ["OFFSCREEN_DOCUMENT"],
|
|
117
|
-
documentUrls: [chrome.runtime.getURL(OFFSCREEN_URL)]
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
if (existingContexts.length > 0) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
await chrome.offscreen.createDocument({
|
|
125
|
-
url: OFFSCREEN_URL,
|
|
126
|
-
reasons: [FETCH_OFFSCREEN_DOCUMENT_REASON],
|
|
127
|
-
justification: "Used to perform fetch to services such as the passbolt API serving invalid certificate.",
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
104
|
/**
|
|
132
105
|
* Build offscreen message data.
|
|
133
|
-
* @param {string} id The identifier of the offscreen fetch request.
|
|
134
106
|
* @param {string} resource The fetch url resource, similar to the native fetch resource parameter.
|
|
135
|
-
* @param {object} fetchOptions The fetch options, similar to the native fetch option parameter.
|
|
136
|
-
* @returns {object}
|
|
107
|
+
* @param {object} [fetchOptions = {}] The fetch options, similar to the native fetch option parameter.
|
|
108
|
+
* @returns {Promise<object>}
|
|
109
|
+
* @private
|
|
137
110
|
*/
|
|
138
|
-
static async buildOffscreenData(
|
|
111
|
+
static async buildOffscreenData(resource, fetchOptions = {}) {
|
|
139
112
|
const options = JSON.parse(JSON.stringify(fetchOptions));
|
|
140
113
|
|
|
141
114
|
// Format FormData fetch options to allow its serialization.
|
|
@@ -152,27 +125,29 @@ export class RequestFetchOffscreenService {
|
|
|
152
125
|
};
|
|
153
126
|
}
|
|
154
127
|
|
|
155
|
-
return {
|
|
128
|
+
return {resource, options};
|
|
156
129
|
}
|
|
157
130
|
|
|
158
131
|
/**
|
|
159
132
|
* Send message to the offscreen fetch document.
|
|
160
|
-
* @param {
|
|
161
|
-
* @param {string} offscreenData.id The identifier of the offscreen fetch request.
|
|
133
|
+
* @param {string} id the identified of the request
|
|
162
134
|
* @param {string} offscreenData.resource The fetch url resource, similar to the native fetch resource parameter.
|
|
163
|
-
* @param {object} offscreenData.
|
|
164
|
-
* @returns {Promise
|
|
135
|
+
* @param {object} offscreenData.options The fetch options, similar to the native fetch option parameter.
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
* @private
|
|
165
138
|
*/
|
|
166
|
-
static async sendOffscreenMessage(
|
|
139
|
+
static async sendOffscreenMessage(id, data) {
|
|
167
140
|
return chrome.runtime.sendMessage({
|
|
141
|
+
id: id,
|
|
142
|
+
data: data,
|
|
168
143
|
target: SEND_MESSAGE_TARGET_FETCH_OFFSCREEN,
|
|
169
|
-
data: offscreenData
|
|
170
144
|
});
|
|
171
145
|
}
|
|
172
146
|
|
|
173
147
|
/**
|
|
174
148
|
* Mark the fetch offscreen strategy as preferred.
|
|
175
|
-
*
|
|
149
|
+
* @returns {Promise<void>}
|
|
150
|
+
* @private
|
|
176
151
|
*/
|
|
177
152
|
static async markFetchOffscreenStrategyAsPreferred() {
|
|
178
153
|
RequestFetchOffscreenService.isFetchOffscreenPreferredCache = true;
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
} from "../../../offscreens/service/network/fetchOffscreenService";
|
|
26
26
|
import {fetchOptionsWithBodyFormData, fetchOptionWithBodyData} from "./requestFetchOffscreenService.test.data";
|
|
27
27
|
import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
|
|
28
|
+
import {v4 as uuidv4} from "uuid";
|
|
29
|
+
import HandleOffscreenResponseService from "../offscreen/handleOffscreenResponseService";
|
|
28
30
|
|
|
29
31
|
beforeEach(() => {
|
|
30
32
|
enableFetchMocks();
|
|
@@ -79,54 +81,25 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
79
81
|
});
|
|
80
82
|
});
|
|
81
83
|
|
|
82
|
-
describe("::createIfNotExistOffscreenDocument", () => {
|
|
83
|
-
it("should create the offscreen document if it does not exist yet ", async() => {
|
|
84
|
-
expect.assertions(2);
|
|
85
|
-
jest.spyOn(chrome.runtime, "getContexts").mockImplementationOnce(() => []);
|
|
86
|
-
await RequestFetchOffscreenService.createIfNotExistOffscreenDocument();
|
|
87
|
-
|
|
88
|
-
const expectedGetContextsData = {
|
|
89
|
-
contextTypes: ["OFFSCREEN_DOCUMENT"],
|
|
90
|
-
documentUrls: ["chrome-extension://didegimhafipceonhjepacocaffmoppf/offscreens/fetch.html"]
|
|
91
|
-
};
|
|
92
|
-
const expectedCreateDocumentData = {
|
|
93
|
-
url: "offscreens/fetch.html",
|
|
94
|
-
reasons: ["WORKERS"],
|
|
95
|
-
justification: "Used to perform fetch to services such as the passbolt API serving invalid certificate."
|
|
96
|
-
};
|
|
97
|
-
expect(chrome.runtime.getContexts).toHaveBeenCalledWith(expectedGetContextsData);
|
|
98
|
-
expect(chrome.offscreen.createDocument).toHaveBeenCalledWith(expectedCreateDocumentData);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should not create the offscreen document if it already exist ", async() => {
|
|
102
|
-
expect.assertions(1);
|
|
103
|
-
jest.spyOn(chrome.runtime, "getContexts").mockImplementationOnce(() => ["shallow-offscreen-document-mock"]);
|
|
104
|
-
await RequestFetchOffscreenService.createIfNotExistOffscreenDocument();
|
|
105
|
-
expect(chrome.offscreen.createDocument).not.toHaveBeenCalled();
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
84
|
describe("::buildOffscreenData", () => {
|
|
110
85
|
it("should build data to send to the offscreen document", async() => {
|
|
111
86
|
expect.assertions(1);
|
|
112
|
-
const id = crypto.randomUUID();
|
|
113
87
|
const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
|
|
114
88
|
const options = fetchOptionWithBodyData();
|
|
115
|
-
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(
|
|
89
|
+
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(resource, options);
|
|
116
90
|
options.body = {
|
|
117
91
|
data: options.body,
|
|
118
92
|
dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
|
|
119
93
|
};
|
|
120
94
|
// Ensure body remains a form data after serialization.
|
|
121
|
-
expect(offscreenData).toEqual({
|
|
95
|
+
expect(offscreenData).toEqual({resource, options});
|
|
122
96
|
});
|
|
123
97
|
|
|
124
98
|
it("should ensure given fetch options body will not be altered", async() => {
|
|
125
99
|
expect.assertions(2);
|
|
126
|
-
const id = crypto.randomUUID();
|
|
127
100
|
const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
|
|
128
101
|
const fetchOptions = fetchOptionsWithBodyFormData();
|
|
129
|
-
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(
|
|
102
|
+
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(resource, fetchOptions);
|
|
130
103
|
// Ensure body remains a form data after serialization.
|
|
131
104
|
expect(offscreenData.options.body.data).toBeInstanceOf(Array);
|
|
132
105
|
expect(offscreenData.options.body.dataType).toStrictEqual(FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA);
|
|
@@ -134,14 +107,12 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
134
107
|
|
|
135
108
|
it("should transform FormData body into serialized encoded url parameters", async() => {
|
|
136
109
|
expect.assertions(1);
|
|
137
|
-
const id = crypto.randomUUID();
|
|
138
110
|
const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
|
|
139
111
|
const options = fetchOptionsWithBodyFormData();
|
|
140
112
|
|
|
141
|
-
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(
|
|
113
|
+
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(resource, options);
|
|
142
114
|
// eslint-disable-next-line object-shorthand
|
|
143
115
|
const expectedOffscreenMessageData = {
|
|
144
|
-
id,
|
|
145
116
|
resource,
|
|
146
117
|
options: {
|
|
147
118
|
...options,
|
|
@@ -161,21 +132,27 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
161
132
|
describe("::sendOffscreenMessage", () => {
|
|
162
133
|
it("should send a message to the offscreen document", async() => {
|
|
163
134
|
expect.assertions(1);
|
|
135
|
+
const id = uuidv4();
|
|
164
136
|
const data = {prop1: "value1"};
|
|
165
|
-
await RequestFetchOffscreenService.sendOffscreenMessage(data);
|
|
166
137
|
const target = SEND_MESSAGE_TARGET_FETCH_OFFSCREEN;
|
|
167
|
-
|
|
138
|
+
|
|
139
|
+
await RequestFetchOffscreenService.sendOffscreenMessage(id, data);
|
|
140
|
+
|
|
141
|
+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({id, target, data});
|
|
168
142
|
});
|
|
169
143
|
});
|
|
170
144
|
|
|
171
145
|
describe("::fetchOffscreen", () => {
|
|
172
146
|
it("should send a message to the offscreen document and stack the response callback handlers", async() => {
|
|
173
147
|
expect.assertions(4);
|
|
148
|
+
|
|
174
149
|
const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
|
|
175
150
|
const options = fetchOptionsWithBodyFormData();
|
|
151
|
+
|
|
176
152
|
jest.spyOn(chrome.runtime, "sendMessage").mockImplementationOnce(message => {
|
|
177
|
-
expect(Validator.isUUID(message.
|
|
153
|
+
expect(Validator.isUUID(message.id)).toBe(true);
|
|
178
154
|
const expectedMessageData = {
|
|
155
|
+
id: message.id,
|
|
179
156
|
target: SEND_MESSAGE_TARGET_FETCH_OFFSCREEN,
|
|
180
157
|
data: {
|
|
181
158
|
...message.data,
|
|
@@ -195,7 +172,7 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
195
172
|
},
|
|
196
173
|
};
|
|
197
174
|
expect(message).toEqual(expectedMessageData);
|
|
198
|
-
|
|
175
|
+
HandleOffscreenResponseService._offscreenResponsePromisesCallbacks[message.id].resolve();
|
|
199
176
|
});
|
|
200
177
|
const requestPromise = RequestFetchOffscreenService.fetchOffscreen(resource, options);
|
|
201
178
|
expect(requestPromise).toBeInstanceOf(Promise);
|
|
@@ -12,85 +12,54 @@
|
|
|
12
12
|
* @since 4.7.0
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import {assertUuid} from "../../../../all/background_page/utils/assertions";
|
|
16
15
|
import {
|
|
17
16
|
FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR,
|
|
18
17
|
FETCH_OFFSCREEN_RESPONSE_TYPE_SUCCESS,
|
|
19
|
-
SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_RESPONSE_HANDLER,
|
|
20
|
-
SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_POLLING_HANDLER,
|
|
21
18
|
} from "../../../offscreens/service/network/fetchOffscreenService";
|
|
22
|
-
|
|
19
|
+
|
|
20
|
+
const FETCH_OFFSCREEN_RESPONSE_TYPES = [FETCH_OFFSCREEN_RESPONSE_TYPE_SUCCESS, FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR];
|
|
23
21
|
|
|
24
22
|
export default class ResponseFetchOffscreenService {
|
|
25
23
|
/**
|
|
26
24
|
* Handle fetch offscreen response message.
|
|
27
25
|
* @param {object} message The message itself.
|
|
26
|
+
* @param {{resolve: function, reject: function}} message The message itself.
|
|
28
27
|
* @return {void}
|
|
29
28
|
*/
|
|
30
|
-
static handleFetchResponse(message) {
|
|
31
|
-
// This is a polling message for long request to keep the service worker alive.
|
|
32
|
-
if (message.target === SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_POLLING_HANDLER) {
|
|
33
|
-
console.debug("ResponseFetchOffscreenService: polled");
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Return early if this message isn't meant for the offscreen document.
|
|
38
|
-
if (message.target !== SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_RESPONSE_HANDLER) {
|
|
39
|
-
console.debug("ResponseFetchOffscreenService: received message not specific to the service worker fetch offscreen response handler.");
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
29
|
+
static handleFetchResponse(message, callbacks) {
|
|
43
30
|
ResponseFetchOffscreenService.assertMessage(message);
|
|
44
|
-
|
|
45
|
-
const
|
|
31
|
+
|
|
32
|
+
const {type, data} = message;
|
|
46
33
|
|
|
47
34
|
if (type === FETCH_OFFSCREEN_RESPONSE_TYPE_SUCCESS) {
|
|
48
|
-
|
|
49
|
-
} else
|
|
50
|
-
|
|
35
|
+
callbacks.resolve(ResponseFetchOffscreenService.buildFetchResponse(data));
|
|
36
|
+
} else {
|
|
37
|
+
callbacks.reject(new Error(data.message));
|
|
51
38
|
}
|
|
52
39
|
}
|
|
53
40
|
|
|
54
41
|
/**
|
|
55
42
|
* Assert message data.
|
|
56
43
|
* @param {object} message The message.
|
|
57
|
-
* @
|
|
58
|
-
* @throws {Error} If the message
|
|
59
|
-
* @
|
|
60
|
-
* @throws {Error} If the message type is not valid.
|
|
44
|
+
* @throws {Error} If the message.data is not an object.
|
|
45
|
+
* @throws {Error} If the message.type is not valid.
|
|
46
|
+
* @private
|
|
61
47
|
*/
|
|
62
48
|
static assertMessage(message) {
|
|
63
|
-
const FETCH_OFFSCREEN_RESPONSE_TYPES = [FETCH_OFFSCREEN_RESPONSE_TYPE_SUCCESS, FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR];
|
|
64
|
-
|
|
65
49
|
if (!FETCH_OFFSCREEN_RESPONSE_TYPES.includes(message?.type)) {
|
|
66
50
|
throw new Error(`ResponseFetchOffscreenService: message.type should be one of the following ${FETCH_OFFSCREEN_RESPONSE_TYPES.join(", ")}.`);
|
|
67
51
|
}
|
|
68
|
-
|
|
52
|
+
|
|
69
53
|
if (!(message?.data instanceof Object)) {
|
|
70
54
|
throw new Error("ResponseFetchOffscreenService: message.data should be an object.");
|
|
71
55
|
}
|
|
72
56
|
}
|
|
73
57
|
|
|
74
|
-
/**
|
|
75
|
-
* Consume the offscreen request promise callbacks or fail.
|
|
76
|
-
* @param {string} id The identifier of the offscreen fetch request.
|
|
77
|
-
* @returns {object}
|
|
78
|
-
* @throws {Error} If no request promise callbacks can be found for the given offscreen fetch request id.
|
|
79
|
-
*/
|
|
80
|
-
static consumeRequestPromiseCallbacksOrFail(id) {
|
|
81
|
-
const offscreenRequestPromiseCallback = RequestFetchOffscreenService.offscreenRequestsPromisesCallbacks[id];
|
|
82
|
-
if (!offscreenRequestPromiseCallback) {
|
|
83
|
-
throw new Error("ResponseFetchOffscreenService: No request promise callbacks found for the given offscreen fetch request id.");
|
|
84
|
-
}
|
|
85
|
-
delete RequestFetchOffscreenService.offscreenRequestsPromisesCallbacks[id];
|
|
86
|
-
|
|
87
|
-
return offscreenRequestPromiseCallback;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
58
|
/**
|
|
91
59
|
* Build native fetch response object based on offscreen message response data.
|
|
92
60
|
* @param {object} data The fetch offscreen message response data.
|
|
93
61
|
* @returns {Response}
|
|
62
|
+
* @private
|
|
94
63
|
*/
|
|
95
64
|
static buildFetchResponse(data) {
|
|
96
65
|
return new Response(data.text, {
|
|
@@ -109,25 +109,6 @@ describe("ResponseFetchOffscreenService", () => {
|
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
describe("::consumeRequestPromiseCallbacksOrFail", () => {
|
|
113
|
-
it("should consume the response handler associated to the given id", () => {
|
|
114
|
-
expect.assertions(3);
|
|
115
|
-
const id = crypto.randomUUID();
|
|
116
|
-
const callbacks = defaultCallbacks();
|
|
117
|
-
RequestFetchOffscreenService.offscreenRequestsPromisesCallbacks[id] = callbacks;
|
|
118
|
-
const consumedCallbacks = ResponseFetchOffscreenService.consumeRequestPromiseCallbacksOrFail(id);
|
|
119
|
-
expect(consumedCallbacks).not.toBeNull();
|
|
120
|
-
expect(consumedCallbacks).toEqual(callbacks);
|
|
121
|
-
expect(Object.keys(RequestFetchOffscreenService.offscreenRequestsPromisesCallbacks).length).toEqual(0);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("should throw if no associated callbacks found for the given id", () => {
|
|
125
|
-
expect.assertions(1);
|
|
126
|
-
const id = crypto.randomUUID();
|
|
127
|
-
expect(() => ResponseFetchOffscreenService.consumeRequestPromiseCallbacksOrFail(id)).toThrow();
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
112
|
describe("::buildFetchResponse", () => {
|
|
132
113
|
it("should build the fetch response object based on the offscreen message data", async() => {
|
|
133
114
|
expect.assertions(8);
|
|
@@ -149,9 +130,9 @@ describe("ResponseFetchOffscreenService", () => {
|
|
|
149
130
|
expect.assertions(1);
|
|
150
131
|
const id = crypto.randomUUID();
|
|
151
132
|
const callbacks = defaultCallbacks();
|
|
152
|
-
RequestFetchOffscreenService.offscreenRequestsPromisesCallbacks[id] = callbacks;
|
|
153
133
|
const message = defaultResponseMessage({id});
|
|
154
|
-
|
|
134
|
+
|
|
135
|
+
ResponseFetchOffscreenService.handleFetchResponse(message, callbacks);
|
|
155
136
|
expect(callbacks.resolve).toHaveBeenCalledWith(expect.any(Response));
|
|
156
137
|
});
|
|
157
138
|
|
|
@@ -159,23 +140,10 @@ describe("ResponseFetchOffscreenService", () => {
|
|
|
159
140
|
expect.assertions(1);
|
|
160
141
|
const id = crypto.randomUUID();
|
|
161
142
|
const callbacks = defaultCallbacks();
|
|
162
|
-
|
|
163
|
-
// eslint-disable-next-line object-shorthand
|
|
164
|
-
const message = defaultResponseMessage({id, type: FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR});
|
|
165
|
-
ResponseFetchOffscreenService.handleFetchResponse(message);
|
|
166
|
-
expect(callbacks.reject).toHaveBeenCalledWith(expect.any(Error));
|
|
167
|
-
});
|
|
143
|
+
const message = defaultResponseMessage({id: id, type: FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR});
|
|
168
144
|
|
|
169
|
-
|
|
170
|
-
expect.
|
|
171
|
-
const id = crypto.randomUUID();
|
|
172
|
-
const callbacks = defaultCallbacks();
|
|
173
|
-
RequestFetchOffscreenService.offscreenRequestsPromisesCallbacks[id] = callbacks;
|
|
174
|
-
// eslint-disable-next-line object-shorthand
|
|
175
|
-
const message = defaultResponseMessage({id, target: "other-target"});
|
|
176
|
-
ResponseFetchOffscreenService.handleFetchResponse(message);
|
|
177
|
-
expect(callbacks.resolve).not.toHaveBeenCalled();
|
|
178
|
-
expect(callbacks.reject).not.toHaveBeenCalled();
|
|
145
|
+
ResponseFetchOffscreenService.handleFetchResponse(message, callbacks);
|
|
146
|
+
expect(callbacks.reject).toHaveBeenCalledWith(expect.any(Error));
|
|
179
147
|
});
|
|
180
148
|
});
|
|
181
149
|
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.3.2
|
|
13
|
+
*/
|
|
14
|
+
const CLIPBOARD_OFFSCREEN_DOCUMENT_REASON = "CLIPBOARD";
|
|
15
|
+
const FETCH_OFFSCREEN_DOCUMENT_REASON = "WORKERS";
|
|
16
|
+
const OFFSCREEN_URL = "offscreens/offscreen.html";
|
|
17
|
+
const LOCK_CREATE_OFFSCREEN_DOCUMENT = "LOCK_CREATE_OFFSCREEN_DOCUMENT";
|
|
18
|
+
|
|
19
|
+
export default class CreateOffscreenDocumentService {
|
|
20
|
+
/**
|
|
21
|
+
* Create clipboard offscreen document if it does not exist yet.
|
|
22
|
+
* @returns {Promise<void>}
|
|
23
|
+
*/
|
|
24
|
+
static async createIfNotExistOffscreenDocument() {
|
|
25
|
+
// Create offscreen document if it does not already exist.
|
|
26
|
+
await navigator.locks.request(LOCK_CREATE_OFFSCREEN_DOCUMENT, async() => {
|
|
27
|
+
const existingContexts = await chrome.runtime.getContexts({
|
|
28
|
+
contextTypes: ["OFFSCREEN_DOCUMENT"],
|
|
29
|
+
documentUrls: [chrome.runtime.getURL(OFFSCREEN_URL)]
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (existingContexts.length > 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await chrome.offscreen.createDocument({
|
|
37
|
+
url: OFFSCREEN_URL,
|
|
38
|
+
reasons: [FETCH_OFFSCREEN_DOCUMENT_REASON, CLIPBOARD_OFFSCREEN_DOCUMENT_REASON],
|
|
39
|
+
justification: "1. Read/write clipboard as clipboard API is unavailable in MV3 service workers 2. Perform requests to self hosted Passbolt API serving invalid certificate.",
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.3.2
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import CreateOffscreenDocumentService from "./createOffscreenDocumentService";
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("CreateOffscreenDocumentService", () => {
|
|
22
|
+
describe("::createIfNotExistOffscreenDocument", () => {
|
|
23
|
+
it("should create the offscreen document if it does not exist yet ", async() => {
|
|
24
|
+
expect.assertions(2);
|
|
25
|
+
jest.spyOn(chrome.runtime, "getContexts").mockImplementationOnce(() => []);
|
|
26
|
+
await CreateOffscreenDocumentService.createIfNotExistOffscreenDocument();
|
|
27
|
+
|
|
28
|
+
const expectedGetContextsData = {
|
|
29
|
+
contextTypes: ["OFFSCREEN_DOCUMENT"],
|
|
30
|
+
documentUrls: ["chrome-extension://didegimhafipceonhjepacocaffmoppf/offscreens/offscreen.html"]
|
|
31
|
+
};
|
|
32
|
+
const expectedCreateDocumentData = {
|
|
33
|
+
url: "offscreens/offscreen.html",
|
|
34
|
+
reasons: ["WORKERS", "CLIPBOARD"],
|
|
35
|
+
justification: "1. Read/write clipboard as clipboard API is unavailable in MV3 service workers 2. Perform requests to self hosted Passbolt API serving invalid certificate.",
|
|
36
|
+
};
|
|
37
|
+
expect(chrome.runtime.getContexts).toHaveBeenCalledWith(expectedGetContextsData);
|
|
38
|
+
expect(chrome.offscreen.createDocument).toHaveBeenCalledWith(expectedCreateDocumentData);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should not create the offscreen document if it already exist ", async() => {
|
|
42
|
+
expect.assertions(1);
|
|
43
|
+
jest.spyOn(chrome.runtime, "getContexts").mockImplementationOnce(() => ["shallow-offscreen-document-mock"]);
|
|
44
|
+
await CreateOffscreenDocumentService.createIfNotExistOffscreenDocument();
|
|
45
|
+
expect(chrome.offscreen.createDocument).not.toHaveBeenCalled();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|