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
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
12
|
* @since 4.7.0
|
|
13
13
|
*/
|
|
14
|
-
import {v4 as uuid} from 'uuid';
|
|
15
14
|
import {enableFetchMocks} from "jest-fetch-mock";
|
|
16
15
|
import FetchOffscreenService from './fetchOffscreenService';
|
|
17
16
|
import each from "jest-each";
|
|
@@ -19,97 +18,119 @@ import {defaultFetchMessage, defaultFetchResponse} from "./fetchOffscreenService
|
|
|
19
18
|
|
|
20
19
|
beforeEach(() => {
|
|
21
20
|
enableFetchMocks();
|
|
22
|
-
fetch.resetMocks();
|
|
23
21
|
jest.clearAllMocks();
|
|
24
22
|
// Flush runtime memory cache.
|
|
25
23
|
FetchOffscreenService.pollingIntervalId = null;
|
|
26
24
|
FetchOffscreenService.pendingRequestsCount = 0;
|
|
25
|
+
|
|
26
|
+
jest.spyOn(chrome.runtime, "sendMessage").mockImplementation(() => {});
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
describe("FetchOffscreenService", () => {
|
|
30
|
-
describe("::
|
|
31
|
-
it("should
|
|
30
|
+
describe("::handleSuccessResponse", () => {
|
|
31
|
+
it("should send a message through the chrome.runtime", async() => {
|
|
32
32
|
expect.assertions(1);
|
|
33
|
-
const spyOnFetch = jest.spyOn(self, "fetch");
|
|
34
|
-
await FetchOffscreenService.handleFetchRequest({});
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
const message = defaultFetchMessage();
|
|
35
|
+
const fetchResponse = {
|
|
36
|
+
headers: new Array([]),
|
|
37
|
+
status: 200,
|
|
38
|
+
statusText: "OK",
|
|
39
|
+
text: async() => "",
|
|
40
|
+
ok: true,
|
|
41
|
+
url: message.data.resource,
|
|
42
|
+
redirected: false,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const result = await FetchOffscreenService.handleSuccessResponse(fetchResponse);
|
|
46
|
+
|
|
47
|
+
const expectedResult = {
|
|
48
|
+
target: "service-worker-fetch-offscreen-response-handler",
|
|
49
|
+
type: "success",
|
|
50
|
+
data: await FetchOffscreenService.serializeResponse(fetchResponse),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
expect(result).toStrictEqual(expectedResult);
|
|
37
54
|
});
|
|
55
|
+
});
|
|
38
56
|
|
|
39
|
-
|
|
57
|
+
describe("::handleErrorResponse", () => {
|
|
58
|
+
it("should send an error message through the chrome.runtime", async() => {
|
|
40
59
|
expect.assertions(1);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
|
|
61
|
+
const error = new Error("Something went wrong");
|
|
62
|
+
error.name = "A fake error name";
|
|
63
|
+
|
|
64
|
+
const result = await FetchOffscreenService.handleErrorResponse(error);
|
|
65
|
+
|
|
66
|
+
const expectedResult = {
|
|
67
|
+
target: "service-worker-fetch-offscreen-response-handler",
|
|
68
|
+
type: "error",
|
|
69
|
+
data: {
|
|
70
|
+
name: error.name,
|
|
71
|
+
message: error.message
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
expect(result).toStrictEqual(expectedResult);
|
|
44
76
|
});
|
|
77
|
+
});
|
|
45
78
|
|
|
79
|
+
describe("::handleFetchRequest", () => {
|
|
46
80
|
it("should increase and decrease the pending request count", async() => {
|
|
47
81
|
expect.assertions(5);
|
|
48
82
|
const message = defaultFetchMessage();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
expect(
|
|
56
|
-
expect(
|
|
57
|
-
expect(
|
|
83
|
+
jest.spyOn(self, "fetch").mockImplementation(async() => ({header: {}, body: {}}));
|
|
84
|
+
jest.spyOn(FetchOffscreenService, "increaseAwaitingRequests");
|
|
85
|
+
jest.spyOn(FetchOffscreenService, "decreaseAwaitingRequests");
|
|
86
|
+
|
|
87
|
+
await FetchOffscreenService.handleFetchRequest(message.data);
|
|
88
|
+
|
|
89
|
+
expect(self.fetch).toHaveBeenCalledTimes(1);
|
|
90
|
+
expect(self.fetch).toHaveBeenCalledWith(message.data.resource, message.data.options);
|
|
91
|
+
expect(FetchOffscreenService.increaseAwaitingRequests).toHaveBeenCalledTimes(1);
|
|
92
|
+
expect(FetchOffscreenService.decreaseAwaitingRequests).toHaveBeenCalledTimes(1);
|
|
58
93
|
expect(FetchOffscreenService.pendingRequestsCount).toStrictEqual(0);
|
|
59
94
|
});
|
|
60
95
|
|
|
61
96
|
it("should handle a successful response", async() => {
|
|
62
97
|
expect.assertions(3);
|
|
63
98
|
const message = defaultFetchMessage();
|
|
64
|
-
const expectedResponse = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
expect(
|
|
99
|
+
const expectedResponse = {headers: new Array([]), body: {}};
|
|
100
|
+
|
|
101
|
+
jest.spyOn(self, "fetch").mockImplementation(async() => expectedResponse);
|
|
102
|
+
jest.spyOn(FetchOffscreenService, "handleSuccessResponse").mockImplementation(() => {});
|
|
103
|
+
jest.spyOn(FetchOffscreenService, "handleErrorResponse").mockImplementation(() => {});
|
|
104
|
+
|
|
105
|
+
await FetchOffscreenService.handleFetchRequest(message.data);
|
|
106
|
+
|
|
107
|
+
expect(self.fetch).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(FetchOffscreenService.handleSuccessResponse).toHaveBeenCalledWith(expectedResponse);
|
|
109
|
+
expect(FetchOffscreenService.handleErrorResponse).not.toHaveBeenCalled();
|
|
73
110
|
});
|
|
74
111
|
|
|
75
112
|
it("should handle a erroneous response", async() => {
|
|
76
113
|
expect.assertions(3);
|
|
77
114
|
const message = defaultFetchMessage();
|
|
78
115
|
const expectedError = new Error("Something went wrong!");
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expect(
|
|
116
|
+
|
|
117
|
+
jest.spyOn(self, "fetch").mockImplementation(async() => { throw expectedError; });
|
|
118
|
+
jest.spyOn(FetchOffscreenService, "handleSuccessResponse").mockImplementation(() => {});
|
|
119
|
+
jest.spyOn(FetchOffscreenService, "handleErrorResponse").mockImplementation(() => {});
|
|
120
|
+
|
|
121
|
+
await FetchOffscreenService.handleFetchRequest(message.data);
|
|
122
|
+
|
|
123
|
+
expect(self.fetch).toHaveBeenCalledTimes(1);
|
|
124
|
+
expect(FetchOffscreenService.handleSuccessResponse).not.toHaveBeenCalledWith();
|
|
125
|
+
expect(FetchOffscreenService.handleErrorResponse).toHaveBeenCalledWith(expectedError);
|
|
87
126
|
});
|
|
88
127
|
});
|
|
89
128
|
|
|
90
129
|
describe("::validateMessageData", () => {
|
|
91
|
-
it("should validate if the message data respects the format",
|
|
130
|
+
it("should validate if the message data respects the format", () => {
|
|
92
131
|
const message = defaultFetchMessage();
|
|
93
|
-
const validation =
|
|
94
|
-
expect(validation).
|
|
95
|
-
expect(chrome.runtime.sendMessage).not.toHaveBeenCalled();
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
each([
|
|
99
|
-
{scenario: "undefined", id: undefined},
|
|
100
|
-
{scenario: "null", id: null},
|
|
101
|
-
{scenario: "invalid string", id: "invalid"},
|
|
102
|
-
{scenario: "boolean", id: true},
|
|
103
|
-
{scenario: "object", id: {data: crypto.randomUUID()}},
|
|
104
|
-
]).describe("should fail if message id is not valid", _props => {
|
|
105
|
-
it(`should trow if message id: ${_props.scenario}`, async() => {
|
|
106
|
-
const message = defaultFetchMessage();
|
|
107
|
-
message.data.id = _props.id;
|
|
108
|
-
const spyOnErrorResponse = jest.spyOn(FetchOffscreenService, "handleErrorResponse");
|
|
109
|
-
const validation = await FetchOffscreenService.validateMessageData(message.data);
|
|
110
|
-
expect(validation).toBeFalsy();
|
|
111
|
-
expect(spyOnErrorResponse).toHaveBeenCalledWith(message.data.id, expect.any(Error));
|
|
112
|
-
});
|
|
132
|
+
const validation = FetchOffscreenService.validateMessageData(message.data.resource, message.data.options);
|
|
133
|
+
expect(validation).toBeNull();
|
|
113
134
|
});
|
|
114
135
|
|
|
115
136
|
each([
|
|
@@ -122,10 +143,13 @@ describe("FetchOffscreenService", () => {
|
|
|
122
143
|
it(`should fail if message resource: ${_props.scenario}`, async() => {
|
|
123
144
|
const message = defaultFetchMessage();
|
|
124
145
|
message.data.resource = _props.resource;
|
|
125
|
-
|
|
126
|
-
|
|
146
|
+
|
|
147
|
+
jest.spyOn(FetchOffscreenService, "handleErrorResponse").mockImplementation(() => {});
|
|
148
|
+
|
|
149
|
+
const validation = await FetchOffscreenService.validateMessageData(message.data.resource, message.data.options);
|
|
150
|
+
|
|
127
151
|
expect(validation).toBeFalsy();
|
|
128
|
-
expect(
|
|
152
|
+
expect(FetchOffscreenService.handleErrorResponse).toHaveBeenCalledWith(expect.any(Error));
|
|
129
153
|
});
|
|
130
154
|
});
|
|
131
155
|
|
|
@@ -139,68 +163,14 @@ describe("FetchOffscreenService", () => {
|
|
|
139
163
|
it(`should fail if message options: ${_props.scenario}`, async() => {
|
|
140
164
|
const message = defaultFetchMessage();
|
|
141
165
|
message.data.options = _props.options;
|
|
142
|
-
const spyOnErrorResponse = jest.spyOn(FetchOffscreenService, "handleErrorResponse");
|
|
143
|
-
const validation = await FetchOffscreenService.validateMessageData(message.data);
|
|
144
|
-
expect(validation).toBeFalsy();
|
|
145
|
-
expect(spyOnErrorResponse).toHaveBeenCalledWith(message.data.id, expect.any(Error));
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe("::handleSuccessResponse", () => {
|
|
151
|
-
it("should send a message through the chrome.runtime", async() => {
|
|
152
|
-
expect.assertions(2);
|
|
153
|
-
|
|
154
|
-
const message = defaultFetchMessage();
|
|
155
|
-
|
|
156
|
-
const fetchResponse = {
|
|
157
|
-
headers: new Array([]),
|
|
158
|
-
status: 200,
|
|
159
|
-
statusText: "OK",
|
|
160
|
-
text: async() => "",
|
|
161
|
-
ok: true,
|
|
162
|
-
url: message.data.resource,
|
|
163
|
-
redirected: false,
|
|
164
|
-
};
|
|
165
|
-
await FetchOffscreenService.handleSuccessResponse(message.data.id, fetchResponse);
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
target: "service-worker-fetch-offscreen-response-handler",
|
|
169
|
-
id: message.data.id,
|
|
170
|
-
type: "success",
|
|
171
|
-
data: await FetchOffscreenService.serializeResponse(fetchResponse),
|
|
172
|
-
};
|
|
167
|
+
jest.spyOn(FetchOffscreenService, "handleErrorResponse").mockImplementation(() => {});
|
|
173
168
|
|
|
174
|
-
|
|
175
|
-
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith(expectedCall);
|
|
176
|
-
});
|
|
177
|
-
});
|
|
169
|
+
const validation = await FetchOffscreenService.validateMessageData(message.data.resource, message.data.options);
|
|
178
170
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const dataFetch = {
|
|
184
|
-
id: uuid(),
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const error = new Error("Something went wrong");
|
|
188
|
-
error.name = "A fake error name";
|
|
189
|
-
const spyOnChromeRuntime = jest.spyOn(chrome.runtime, "sendMessage");
|
|
190
|
-
await FetchOffscreenService.handleErrorResponse(dataFetch.id, error);
|
|
191
|
-
|
|
192
|
-
const expectedCall = {
|
|
193
|
-
target: "service-worker-fetch-offscreen-response-handler",
|
|
194
|
-
id: dataFetch.id,
|
|
195
|
-
type: "error",
|
|
196
|
-
data: {
|
|
197
|
-
name: error.name,
|
|
198
|
-
message: error.message
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
expect(spyOnChromeRuntime).toHaveBeenCalledTimes(1);
|
|
203
|
-
expect(spyOnChromeRuntime).toHaveBeenCalledWith(expectedCall);
|
|
171
|
+
expect(validation).toBeFalsy();
|
|
172
|
+
expect(FetchOffscreenService.handleErrorResponse).toHaveBeenCalledWith(expect.any(Error));
|
|
173
|
+
});
|
|
204
174
|
});
|
|
205
175
|
});
|
|
206
176
|
|
|
@@ -0,0 +1,85 @@
|
|
|
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 FetchOffscreenService, {SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} from "../network/fetchOffscreenService";
|
|
16
|
+
import WriteClipobardOffscreenService, {SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN} from "../clipboard/writeClipobardOffscreenService";
|
|
17
|
+
import {assertUuid} from "../../../../all/background_page/utils/assertions";
|
|
18
|
+
|
|
19
|
+
export const SEND_MESSAGE_TARGET_OFFSCREEN_ERROR_RESPONSE_HANDLER = "service-worker-offscreen-error-response-handler";
|
|
20
|
+
|
|
21
|
+
export default class HandleOffscreenRequestService {
|
|
22
|
+
/**
|
|
23
|
+
* Handle fetch request.
|
|
24
|
+
* @param {object} message Browser runtime.onMessage listener message.
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
27
|
+
static async handleOffscreenRequest(message) {
|
|
28
|
+
HandleOffscreenRequestService._assertOffscreenRequest(message);
|
|
29
|
+
|
|
30
|
+
const REQUEST_HANDLE_MAP = {
|
|
31
|
+
[SEND_MESSAGE_TARGET_FETCH_OFFSCREEN]: FetchOffscreenService.handleFetchRequest,
|
|
32
|
+
[SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN]: WriteClipobardOffscreenService.handleClipboardRequest,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const requestHandler = REQUEST_HANDLE_MAP[message.target];
|
|
36
|
+
if (!requestHandler) {
|
|
37
|
+
console.debug(`HandleOffscreenRequestService received an unsupported request: "${message.target}".`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const result = await requestHandler(message.data);
|
|
43
|
+
HandleOffscreenRequestService._sendResponseBack(message.id, result);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
HandleOffscreenRequestService._sendErrorResponseBack(message.id, error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Handle fetch success, and send response to the service worker.
|
|
51
|
+
* @param {string} id The fetch offscreen request id
|
|
52
|
+
* @param {object} data the data to send back to the requester.
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
static async _sendResponseBack(id, data) {
|
|
56
|
+
await chrome.runtime.sendMessage({
|
|
57
|
+
id: id,
|
|
58
|
+
...data,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Handle fetch success, and send response to the service worker.
|
|
64
|
+
* @param {string} id The fetch offscreen request id
|
|
65
|
+
* @param {object} data the data to send back to the requester.
|
|
66
|
+
* @returns {Promise<void>}
|
|
67
|
+
*/
|
|
68
|
+
static async _sendErrorResponseBack(id, error) {
|
|
69
|
+
await chrome.runtime.sendMessage({
|
|
70
|
+
id: id,
|
|
71
|
+
target: SEND_MESSAGE_TARGET_OFFSCREEN_ERROR_RESPONSE_HANDLER,
|
|
72
|
+
data: {error: JSON.stringify(error, Object.getOwnPropertyNames(error))},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Asserts that the given message is a valid offscreen response message.
|
|
78
|
+
* @param {object} message
|
|
79
|
+
* @throws {Error} if message.id is not a valid UUID
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
static _assertOffscreenRequest(message) {
|
|
83
|
+
assertUuid(message.id, "HandleOffscreenRequestService: message.id should be a valid uuid.");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
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 FetchOffscreenService, {FETCH_OFFSCREEN_DATA_TYPE_JSON} from "../network/fetchOffscreenService";
|
|
16
|
+
import WriteClipobardOffscreenService from "../clipboard/writeClipobardOffscreenService";
|
|
17
|
+
import HandleOffscreenRequestService, {SEND_MESSAGE_TARGET_OFFSCREEN_ERROR_RESPONSE_HANDLER} from "./handleOffscreenRequestService";
|
|
18
|
+
import {v4 as uuidv4} from "uuid";
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("HandleOffscreenRequestService", () => {
|
|
25
|
+
describe("::handleOffscreenRequest", () => {
|
|
26
|
+
it("should redirect fetch request to FetchOffscreenService", async() => {
|
|
27
|
+
expect.assertions(3);
|
|
28
|
+
|
|
29
|
+
const message = {
|
|
30
|
+
id: uuidv4(),
|
|
31
|
+
target: "fetch-offscreen",
|
|
32
|
+
data: {
|
|
33
|
+
resource: "https://www.passbolt.com",
|
|
34
|
+
options: {
|
|
35
|
+
body: {
|
|
36
|
+
data: "test",
|
|
37
|
+
dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
jest.spyOn(FetchOffscreenService, "handleFetchRequest").mockImplementation(() => {});
|
|
44
|
+
jest.spyOn(WriteClipobardOffscreenService, "handleClipboardRequest");
|
|
45
|
+
|
|
46
|
+
await HandleOffscreenRequestService.handleOffscreenRequest(message);
|
|
47
|
+
|
|
48
|
+
expect(FetchOffscreenService.handleFetchRequest).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(FetchOffscreenService.handleFetchRequest).toHaveBeenCalledWith(message.data);
|
|
50
|
+
expect(WriteClipobardOffscreenService.handleClipboardRequest).not.toHaveBeenCalled();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should redirect clipboard write request to WriteClipobardOffscreenService", async() => {
|
|
54
|
+
expect.assertions(3);
|
|
55
|
+
|
|
56
|
+
const message = {
|
|
57
|
+
id: uuidv4(),
|
|
58
|
+
target: "clipboard-write-offscreen",
|
|
59
|
+
data: {
|
|
60
|
+
clipboardContent: "test",
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
jest.spyOn(FetchOffscreenService, "handleFetchRequest");
|
|
65
|
+
jest.spyOn(WriteClipobardOffscreenService, "handleClipboardRequest").mockImplementation(() => {});
|
|
66
|
+
|
|
67
|
+
await HandleOffscreenRequestService.handleOffscreenRequest(message);
|
|
68
|
+
|
|
69
|
+
expect(FetchOffscreenService.handleFetchRequest).not.toHaveBeenCalled();
|
|
70
|
+
expect(WriteClipobardOffscreenService.handleClipboardRequest).toHaveBeenCalledTimes(1);
|
|
71
|
+
expect(WriteClipobardOffscreenService.handleClipboardRequest).toHaveBeenCalledWith(message.data);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should send back a generic offscreen message if something wrong happens during the offscreen process", async() => {
|
|
75
|
+
expect.assertions(2);
|
|
76
|
+
|
|
77
|
+
const message = {
|
|
78
|
+
id: uuidv4(),
|
|
79
|
+
target: "clipboard-write-offscreen",
|
|
80
|
+
data: {},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const error = new Error("Impossible to copy to clipboard");
|
|
84
|
+
jest.spyOn(WriteClipobardOffscreenService, "handleClipboardRequest").mockImplementation(() => { throw error; });
|
|
85
|
+
jest.spyOn(chrome.runtime, "sendMessage").mockImplementation(() => {});
|
|
86
|
+
|
|
87
|
+
await HandleOffscreenRequestService.handleOffscreenRequest(message);
|
|
88
|
+
|
|
89
|
+
const expectedMessage = {
|
|
90
|
+
id: message.id,
|
|
91
|
+
target: SEND_MESSAGE_TARGET_OFFSCREEN_ERROR_RESPONSE_HANDLER,
|
|
92
|
+
data: {error: JSON.stringify(error, Object.getOwnPropertyNames(error))},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
expect(chrome.runtime.sendMessage).toHaveBeenCalledTimes(1);
|
|
96
|
+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith(expectedMessage);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
const {RequestClipboardOffscreenService} = require("../serviceWorker/service/clipboard/requestClipboardOffscreenService");
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
writeText: async data => RequestClipboardOffscreenService.writeText(data),
|
|
19
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
import CreateOffscreenDocumentService from "../offscreen/createOffscreenDocumentService";
|
|
15
|
+
import HandleOffscreenResponseService from "../offscreen/handleOffscreenResponseService";
|
|
16
|
+
|
|
17
|
+
const {SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN} = require("../../../offscreens/service/clipboard/writeClipobardOffscreenService");
|
|
18
|
+
|
|
19
|
+
export class RequestClipboardOffscreenService {
|
|
20
|
+
/**
|
|
21
|
+
* Run the equivalent of navigator.clipboard::writeText(content) through offscreen.
|
|
22
|
+
* @param {string} clipboardContent the content to copy to clipboard
|
|
23
|
+
* @returns {Promise<void>}
|
|
24
|
+
*/
|
|
25
|
+
static async writeText(clipboardContent) {
|
|
26
|
+
await CreateOffscreenDocumentService.createIfNotExistOffscreenDocument();
|
|
27
|
+
|
|
28
|
+
const requestId = crypto.randomUUID();
|
|
29
|
+
const offscreenClipboardData = {clipboardContent};
|
|
30
|
+
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
// Stack the response listener callbacks.
|
|
33
|
+
HandleOffscreenResponseService.setResponseCallback(requestId, {resolve, reject});
|
|
34
|
+
return RequestClipboardOffscreenService.sendWriteTextOffscreenMessage(requestId, offscreenClipboardData)
|
|
35
|
+
.catch(reject);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Send message to the offscreen document for emulating a navigator.clipboard::writeText(data) operation.
|
|
41
|
+
* @param {object} offscreenData The data to copy to clipboard.
|
|
42
|
+
* @returns {Promise<*>}
|
|
43
|
+
*/
|
|
44
|
+
static async sendWriteTextOffscreenMessage(id, data) {
|
|
45
|
+
return chrome.runtime.sendMessage({
|
|
46
|
+
id: id,
|
|
47
|
+
data: data,
|
|
48
|
+
target: SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/chrome-mv3/serviceWorker/service/clipboard/requestClipboardOffscreenService.test.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
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 Validator from "validator";
|
|
16
|
+
import {RequestClipboardOffscreenService} from "./requestClipboardOffscreenService";
|
|
17
|
+
import {v4 as uuidv4} from "uuid";
|
|
18
|
+
import HandleOffscreenResponseService from "../offscreen/handleOffscreenResponseService";
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("RequestClipboardOffscreenService", () => {
|
|
25
|
+
describe("::sendWriteTextOffscreenMessage", () => {
|
|
26
|
+
it("should send a message to the offscreen document", async() => {
|
|
27
|
+
expect.assertions(1);
|
|
28
|
+
const id = uuidv4();
|
|
29
|
+
const data = {clipboardContent: "test-data"};
|
|
30
|
+
const target = "clipboard-write-offscreen";
|
|
31
|
+
|
|
32
|
+
await RequestClipboardOffscreenService.sendWriteTextOffscreenMessage(id, data);
|
|
33
|
+
|
|
34
|
+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({id, data, target});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("::writeText", () => {
|
|
39
|
+
it("should send a message to the offscreen document and stack the response callback handlers", async() => {
|
|
40
|
+
expect.assertions(5);
|
|
41
|
+
|
|
42
|
+
let sentMessage;
|
|
43
|
+
jest.spyOn(chrome.runtime, "sendMessage").mockImplementationOnce(message => {
|
|
44
|
+
sentMessage = message;
|
|
45
|
+
HandleOffscreenResponseService._offscreenResponsePromisesCallbacks[message.id].resolve();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const clipboardContentToWrite = "clipboard-content";
|
|
49
|
+
const requestPromise = RequestClipboardOffscreenService.writeText(clipboardContentToWrite);
|
|
50
|
+
|
|
51
|
+
expect(requestPromise).toBeInstanceOf(Promise);
|
|
52
|
+
await expect(requestPromise).resolves.not.toThrow();
|
|
53
|
+
|
|
54
|
+
expect(Validator.isUUID(sentMessage.id)).toBe(true);
|
|
55
|
+
expect(chrome.runtime.sendMessage).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
|
|
57
|
+
id: sentMessage.id,
|
|
58
|
+
data: {clipboardContent: clipboardContentToWrite},
|
|
59
|
+
target: "clipboard-write-offscreen",
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should throw if the message cannot be sent to the offscreen document for unexpected reason", async() => {
|
|
64
|
+
expect.assertions(1);
|
|
65
|
+
jest.spyOn(chrome.runtime, "sendMessage").mockImplementationOnce(() => { throw new Error("Test error"); });
|
|
66
|
+
|
|
67
|
+
await expect(() => RequestClipboardOffscreenService.writeText("test")).rejects.toThrow();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
export default class ResponseClipboardOffscreenService {
|
|
16
|
+
/**
|
|
17
|
+
* Handle clipboard::writeText offscreen response message.
|
|
18
|
+
* @param {object} message.
|
|
19
|
+
* @param {{resolve: function, reject: function}} promise the offscreen response callback.
|
|
20
|
+
* @return {void}
|
|
21
|
+
*/
|
|
22
|
+
static handleClipboardResponse(message, promise) {
|
|
23
|
+
promise.resolve();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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 {SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN_RESPONSE_HANDLER} from "../../../offscreens/service/clipboard/writeClipobardOffscreenService";
|
|
16
|
+
|
|
17
|
+
export const defaultClipboardWriteResponseMessage = (message = {}) => ({
|
|
18
|
+
id: crypto.randomUUID(),
|
|
19
|
+
target: SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN_RESPONSE_HANDLER,
|
|
20
|
+
...message
|
|
21
|
+
});
|