@vacantthinker/firefox-addon-framework-easy 2026.609.706 → 2026.610.749
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/README.md +25 -27
- package/index.js +0 -1
- package/package.json +1 -1
- package/src/browserRuntime.js +1 -1
- package/src/browserTab.js +44 -20
- package/src/generate.js +0 -201
- package/src/opTab.js +127 -88
- package/src/serviceUserSettings.js +0 -33
package/README.md
CHANGED
|
@@ -69,13 +69,16 @@ export function browserRuntimeOnMessageCommon(act, message, sendResponse) { }
|
|
|
69
69
|
|
|
70
70
|
### 📄 File: `src/browserTab.js`
|
|
71
71
|
```javascript
|
|
72
|
-
export async function browserTabSendMessage(
|
|
72
|
+
export async function browserTabSendMessage(
|
|
73
|
+
tabId,
|
|
74
|
+
message,
|
|
75
|
+
) { }
|
|
73
76
|
|
|
74
77
|
export function browserTabWaitReloadThenSendMessageToContentJs(message) { }
|
|
75
78
|
|
|
76
79
|
export async function browserTabCreateToDownload(message) { }
|
|
77
80
|
|
|
78
|
-
export async function browserTabCreateNearSendMessageToContentJs(message
|
|
81
|
+
export async function browserTabCreateNearSendMessageToContentJs(message = { }
|
|
79
82
|
|
|
80
83
|
export function browserTabWaitReloadThenRemoveIt({ }
|
|
81
84
|
|
|
@@ -89,12 +92,6 @@ export class DomainORM extends BaseORM { }
|
|
|
89
92
|
|
|
90
93
|
### 📄 File: `src/generate.js`
|
|
91
94
|
```javascript
|
|
92
|
-
export async function generateHtmlByUserSettings(
|
|
93
|
-
userSettings,
|
|
94
|
-
radioItemClickCallback,
|
|
95
|
-
checkboxItemClickCallback,
|
|
96
|
-
) { }
|
|
97
|
-
|
|
98
95
|
export function generateMkvScriptForSystemWindows({ }
|
|
99
96
|
|
|
100
97
|
export function generateMkvScriptForSystemFedora({ }
|
|
@@ -121,7 +118,7 @@ export async function stoOpSetNull(k) { }
|
|
|
121
118
|
|
|
122
119
|
### 📄 File: `src/opTab.js`
|
|
123
120
|
```javascript
|
|
124
|
-
export
|
|
121
|
+
export function tabOpEnhance(tab) { }
|
|
125
122
|
|
|
126
123
|
export async function tabOpCreate(properties) { }
|
|
127
124
|
|
|
@@ -131,31 +128,40 @@ export async function tabOpCreateActiveFalse(properties) { }
|
|
|
131
128
|
|
|
132
129
|
export async function tabOpCreateByWindow(url) { }
|
|
133
130
|
|
|
134
|
-
export
|
|
131
|
+
export function tabOpGet(tabId) { }
|
|
135
132
|
|
|
136
|
-
export
|
|
133
|
+
export function tabOpQueryAll() { }
|
|
137
134
|
|
|
138
135
|
export async function tabOpQueryUrl(urlQuery) { }
|
|
139
136
|
|
|
140
137
|
export async function tabOpQueryUrlThenRemove(urlQuery) { }
|
|
141
138
|
|
|
142
|
-
export
|
|
139
|
+
export function tabOpReload(tabId) { }
|
|
143
140
|
|
|
144
|
-
export
|
|
141
|
+
export function tabOpReloadByPassCacheTrue(tabId) { }
|
|
145
142
|
|
|
146
|
-
export
|
|
143
|
+
export function tabOpRemove(tabId) { }
|
|
147
144
|
|
|
148
|
-
export
|
|
145
|
+
export function tabOpHide(tabId) { }
|
|
149
146
|
|
|
150
|
-
export async function tabOpUpdate(
|
|
147
|
+
export async function tabOpUpdate(
|
|
148
|
+
tabId,
|
|
149
|
+
updateProperties,
|
|
150
|
+
) { }
|
|
151
151
|
|
|
152
|
-
export
|
|
152
|
+
export function tabOpUpdateActiveFalse(tabId) { }
|
|
153
153
|
|
|
154
154
|
export async function tabOpFocus(tabId) { }
|
|
155
155
|
|
|
156
|
-
export
|
|
156
|
+
export function tabOpInsertCssCode(
|
|
157
|
+
tabId,
|
|
158
|
+
code,
|
|
159
|
+
) { }
|
|
157
160
|
|
|
158
|
-
export
|
|
161
|
+
export function tabOpRemoveCssCode(
|
|
162
|
+
tabId,
|
|
163
|
+
code,
|
|
164
|
+
) { }
|
|
159
165
|
|
|
160
166
|
```
|
|
161
167
|
|
|
@@ -227,11 +233,3 @@ export async function serviceUpdataALLNodeBackgroundColor(message) { }
|
|
|
227
233
|
|
|
228
234
|
```
|
|
229
235
|
|
|
230
|
-
### 📄 File: `src/serviceUserSettings.js`
|
|
231
|
-
```javascript
|
|
232
|
-
export async function serviceInitUserSettings(userSettings) { }
|
|
233
|
-
|
|
234
|
-
export async function serviceGetUserSettings(userSettings) { }
|
|
235
|
-
|
|
236
|
-
```
|
|
237
|
-
|
package/index.js
CHANGED
package/package.json
CHANGED
package/src/browserRuntime.js
CHANGED
package/src/browserTab.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import {browserNotificationCreate} from './browserNotification.js';
|
|
4
3
|
import {tabOpCreateNear, tabOpGet, tabOpRemove} from './opTab.js';
|
|
5
4
|
|
|
6
|
-
export async function browserTabSendMessage(
|
|
5
|
+
export async function browserTabSendMessage(
|
|
6
|
+
tabId,
|
|
7
|
+
message,
|
|
8
|
+
) {
|
|
7
9
|
await browser.tabs.sendMessage(tabId, message);
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export function browserTabWaitReloadThenSendMessageToContentJs(message) {
|
|
11
13
|
let tabId = message.tabId;
|
|
12
14
|
browser.tabs.onUpdated.addListener(
|
|
13
|
-
async function lis(
|
|
15
|
+
async function lis(
|
|
16
|
+
tabId,
|
|
17
|
+
changeInfo,
|
|
18
|
+
) {
|
|
14
19
|
if (changeInfo.status === 'complete') {
|
|
15
20
|
browser.tabs.onUpdated.removeListener(lis);
|
|
16
21
|
await browserTabSendMessage(tabId, message);
|
|
@@ -47,7 +52,10 @@ export async function browserTabCreateToDownload(message) {
|
|
|
47
52
|
|
|
48
53
|
let {tabId} = await tabOpCreateNear(properties);
|
|
49
54
|
browser.tabs.onUpdated.addListener(
|
|
50
|
-
async function lis(
|
|
55
|
+
async function lis(
|
|
56
|
+
tabId,
|
|
57
|
+
changeInfo,
|
|
58
|
+
) {
|
|
51
59
|
if (changeInfo.status === 'complete') {
|
|
52
60
|
browser.tabs.onUpdated.removeListener(lis);
|
|
53
61
|
// todo code here
|
|
@@ -58,18 +66,21 @@ export async function browserTabCreateToDownload(message) {
|
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
/**
|
|
61
|
-
*
|
|
62
|
-
* @param message
|
|
63
|
-
* @param message.tabId
|
|
64
|
-
* @param message.url
|
|
65
|
-
* @param message.options.
|
|
69
|
+
* Safely handles incoming messages to create tabs and send messages to content scripts.
|
|
70
|
+
* * @param {Object} message - The message payload.
|
|
71
|
+
* @param {number} [message.tabId] - Optional Tab ID to attach to.
|
|
72
|
+
* @param {string} [message.url] - The URL to open.
|
|
73
|
+
* @param {Object} [message.options] - Additional options.
|
|
74
|
+
* @param {boolean} [message.options.focusNewTab] - Whether the newly created tab should be active.
|
|
66
75
|
* @returns {Promise<void>}
|
|
67
76
|
*/
|
|
68
|
-
export async function browserTabCreateNearSendMessageToContentJs(message) {
|
|
77
|
+
export async function browserTabCreateNearSendMessageToContentJs(message = {}) {
|
|
69
78
|
let properties = {
|
|
70
79
|
url: message.url,
|
|
71
80
|
};
|
|
72
|
-
|
|
81
|
+
|
|
82
|
+
// Strictly check for tabId presence to prevent passing undefined.
|
|
83
|
+
if (message.tabId !== undefined) {
|
|
73
84
|
let tabId = message.tabId;
|
|
74
85
|
try {
|
|
75
86
|
await tabOpGet(tabId);
|
|
@@ -78,22 +89,32 @@ export async function browserTabCreateNearSendMessageToContentJs(message) {
|
|
|
78
89
|
delete message.tabId;
|
|
79
90
|
}
|
|
80
91
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
|
|
93
|
+
// Use optional chaining to safely extract nested properties.
|
|
94
|
+
// Only assign 'active' if 'focusNewTab' was explicitly provided.
|
|
95
|
+
if (message?.options?.focusNewTab !== undefined) {
|
|
96
|
+
Object.assign(properties, {active: message.options.focusNewTab});
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
let {tabId} = await tabOpCreateNear(properties);
|
|
100
|
+
|
|
87
101
|
browser.tabs.onUpdated.addListener(
|
|
88
|
-
async function lis(
|
|
102
|
+
async function lis(
|
|
103
|
+
updatedTabId,
|
|
104
|
+
changeInfo,
|
|
105
|
+
) {
|
|
89
106
|
if (changeInfo.status === 'complete') {
|
|
90
107
|
browser.tabs.onUpdated.removeListener(lis);
|
|
91
|
-
|
|
108
|
+
|
|
109
|
+
// Execute further logic once the tab is completely loaded.
|
|
92
110
|
await browserTabSendMessage(
|
|
93
|
-
|
|
111
|
+
updatedTabId,
|
|
112
|
+
Object.assign({}, message, {tabId: updatedTabId}),
|
|
113
|
+
);
|
|
94
114
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
115
|
+
},
|
|
116
|
+
{tabId, properties: ['status']},
|
|
117
|
+
);
|
|
97
118
|
}
|
|
98
119
|
|
|
99
120
|
/**
|
|
@@ -103,7 +124,10 @@ export async function browserTabCreateNearSendMessageToContentJs(message) {
|
|
|
103
124
|
*/
|
|
104
125
|
export function browserTabWaitReloadThenRemoveIt({tabId}) {
|
|
105
126
|
browser.tabs.onUpdated.addListener(
|
|
106
|
-
async function lis(
|
|
127
|
+
async function lis(
|
|
128
|
+
tabId,
|
|
129
|
+
changeInfo,
|
|
130
|
+
) {
|
|
107
131
|
if (changeInfo.status === 'complete') {
|
|
108
132
|
browser.tabs.onUpdated.removeListener(lis);
|
|
109
133
|
// todo code here
|
package/src/generate.js
CHANGED
|
@@ -1,204 +1,3 @@
|
|
|
1
|
-
import {stoOpGet, stoOpSet} from './opStorage.js';
|
|
2
|
-
|
|
3
|
-
// [Optimization 1] Introduce a debounce function to prevent triggering storage writes on every keystroke.
|
|
4
|
-
function debounce(func, wait) {
|
|
5
|
-
let timeout;
|
|
6
|
-
return function(...args) {
|
|
7
|
-
clearTimeout(timeout);
|
|
8
|
-
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Generates HTML elements based on a user settings schema object.
|
|
14
|
-
*
|
|
15
|
-
* @param {Object} userSettings
|
|
16
|
-
* @param {Function} radioItemClickCallback
|
|
17
|
-
* @returns {Promise<DocumentFragment>}
|
|
18
|
-
*/
|
|
19
|
-
export async function generateHtmlByUserSettings(
|
|
20
|
-
userSettings,
|
|
21
|
-
radioItemClickCallback,
|
|
22
|
-
checkboxItemClickCallback,
|
|
23
|
-
) {
|
|
24
|
-
const elementsMap = {};
|
|
25
|
-
|
|
26
|
-
// [Optimization 2] Use a DocumentFragment for off-screen DOM construction to improve rendering performance.
|
|
27
|
-
const fragment = document.createDocumentFragment();
|
|
28
|
-
|
|
29
|
-
const keys = Object.keys(userSettings);
|
|
30
|
-
|
|
31
|
-
// [Optimization 3] Batch fetch all stored values.
|
|
32
|
-
// Wait for all queries to complete to prevent async callbacks from interrupting the rendering pipeline.
|
|
33
|
-
const valuesArray = await Promise.all(keys.map((key) => stoOpGet(key)));
|
|
34
|
-
const storageData = {};
|
|
35
|
-
keys.forEach((key, index) => {
|
|
36
|
-
storageData[key] = valuesArray[index];
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// First pass: Pre-create all container wrappers.
|
|
40
|
-
// This ensures all target elements exist in the DOM references before executing triggerVisibility later.
|
|
41
|
-
keys.forEach((storageKey) => {
|
|
42
|
-
const eleWrap = document.createElement('fieldset');
|
|
43
|
-
const eleTitle = document.createElement('legend');
|
|
44
|
-
eleTitle.textContent = storageKey;
|
|
45
|
-
eleWrap.append(eleTitle);
|
|
46
|
-
|
|
47
|
-
elementsMap[storageKey] = eleWrap;
|
|
48
|
-
fragment.append(eleWrap);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Second pass: Synchronously populate form content and attach event listeners.
|
|
52
|
-
keys.forEach((storageKey) => {
|
|
53
|
-
const storageValue = userSettings[storageKey];
|
|
54
|
-
const type = storageValue.type || 'text';
|
|
55
|
-
const eleWrap = elementsMap[storageKey];
|
|
56
|
-
|
|
57
|
-
// Retrieve pre-loaded value; fallback to the schema's default value if undefined.
|
|
58
|
-
const storedValue = storageData[storageKey];
|
|
59
|
-
const initialValue =
|
|
60
|
-
storedValue !== undefined && storedValue !== null
|
|
61
|
-
? storedValue
|
|
62
|
-
: storageValue.selected;
|
|
63
|
-
|
|
64
|
-
// --- CONDITION 1: CHECKBOX & RADIO ---
|
|
65
|
-
if (type === 'checkbox' || type === 'radio') {
|
|
66
|
-
/** @type {string[]} */
|
|
67
|
-
const options = storageValue.options || [];
|
|
68
|
-
|
|
69
|
-
options.forEach((option) => {
|
|
70
|
-
const eleLabel = document.createElement('label');
|
|
71
|
-
eleLabel.textContent = option;
|
|
72
|
-
const eleInput = document.createElement('input');
|
|
73
|
-
eleInput.name = storageKey;
|
|
74
|
-
eleInput.type = type;
|
|
75
|
-
eleInput.value = option;
|
|
76
|
-
|
|
77
|
-
if (type === 'checkbox') {
|
|
78
|
-
const initialArray = Array.from(initialValue || []);
|
|
79
|
-
const set = new Set(initialArray);
|
|
80
|
-
eleInput.checked = set.has(option); // Apply state synchronously
|
|
81
|
-
|
|
82
|
-
eleInput.addEventListener('change', async () => {
|
|
83
|
-
// Derive state in real-time from the DOM (faster than reading from storage again)
|
|
84
|
-
const allCheckboxes = eleWrap.querySelectorAll(
|
|
85
|
-
'input[type="checkbox"]',
|
|
86
|
-
);
|
|
87
|
-
const valueNew = Array.from(allCheckboxes)
|
|
88
|
-
.filter((cb) => cb.checked)
|
|
89
|
-
.map((cb) => cb.value);
|
|
90
|
-
|
|
91
|
-
await stoOpSet(storageKey, valueNew);
|
|
92
|
-
if (typeof checkboxItemClickCallback === 'function') {
|
|
93
|
-
checkboxItemClickCallback(storageKey, option);
|
|
94
|
-
}
|
|
95
|
-
triggerVisibility(storageKey, valueNew);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
else if (type === 'radio') {
|
|
99
|
-
if (option === initialValue) {
|
|
100
|
-
eleInput.checked = true; // Apply state synchronously
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// It is recommended to bind the 'change' event to the input rather than 'onclick' to the label
|
|
104
|
-
eleInput.addEventListener('change', () => {
|
|
105
|
-
stoOpSet(storageKey, option).then(() => {
|
|
106
|
-
if (typeof radioItemClickCallback === 'function') {
|
|
107
|
-
radioItemClickCallback(storageKey, option);
|
|
108
|
-
}
|
|
109
|
-
triggerVisibility(storageKey, option);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Fix: Native HTML typically places the input element before the text node inside a label.
|
|
115
|
-
eleLabel.prepend(eleInput);
|
|
116
|
-
eleWrap.append(eleLabel);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// --- CONDITION 2: TOGGLE BUTTON ---
|
|
121
|
-
else if (type === 'button') {
|
|
122
|
-
const eleButton = document.createElement('button');
|
|
123
|
-
eleButton.type = 'button';
|
|
124
|
-
|
|
125
|
-
let currentStatus = initialValue === true || initialValue === 'true';
|
|
126
|
-
eleButton.textContent = String(currentStatus); // Apply state synchronously
|
|
127
|
-
|
|
128
|
-
eleButton.addEventListener('click', async () => {
|
|
129
|
-
currentStatus = !currentStatus;
|
|
130
|
-
eleButton.textContent = String(currentStatus);
|
|
131
|
-
await stoOpSet(storageKey, currentStatus);
|
|
132
|
-
triggerVisibility(storageKey, currentStatus);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
eleWrap.append(eleButton);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// --- CONDITION 3: NUMBER & TEXT INPUTS ---
|
|
139
|
-
else if (type === 'number' || type === 'text') {
|
|
140
|
-
const eleInput = document.createElement('input');
|
|
141
|
-
eleInput.type = type;
|
|
142
|
-
eleInput.name = storageKey;
|
|
143
|
-
eleInput.value = initialValue !== undefined ? initialValue : ''; // Apply state synchronously
|
|
144
|
-
|
|
145
|
-
// [Optimization 4] Wrap the write operation with debounce, delaying storage writes by 500ms.
|
|
146
|
-
// This eliminates UI freezing regardless of typing speed.
|
|
147
|
-
const debouncedSave = debounce(async (val) => {
|
|
148
|
-
const finalizedValue = type === 'number' ? Number(val) : val;
|
|
149
|
-
await stoOpSet(storageKey, finalizedValue);
|
|
150
|
-
}, 500);
|
|
151
|
-
|
|
152
|
-
eleInput.addEventListener('input', () => {
|
|
153
|
-
const rawValue = eleInput.value;
|
|
154
|
-
debouncedSave(rawValue);
|
|
155
|
-
triggerVisibility(storageKey, rawValue); // Visual UI updates do not require a delay
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
eleWrap.append(eleInput);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// --- CONDITION 4: SPAN / READ-ONLY TEXT ---
|
|
162
|
-
else if (type === 'span') {
|
|
163
|
-
const eleSpan = document.createElement('span');
|
|
164
|
-
eleSpan.textContent = String(initialValue); // Apply state synchronously
|
|
165
|
-
eleWrap.append(eleSpan);
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// [Optimization 5] Third pass: Globally trigger a single visibility check.
|
|
170
|
-
// At this point, all DOM elements exist and all initialValues are fully derived,
|
|
171
|
-
// preventing undefined errors or invalid target hiding.
|
|
172
|
-
keys.forEach((storageKey) => {
|
|
173
|
-
const storedValue = storageData[storageKey];
|
|
174
|
-
const initialValue =
|
|
175
|
-
storedValue !== undefined && storedValue !== null
|
|
176
|
-
? storedValue
|
|
177
|
-
: userSettings[storageKey].selected;
|
|
178
|
-
|
|
179
|
-
triggerVisibility(storageKey, initialValue);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Evaluates the visibility rules for a given source key based on its current
|
|
184
|
-
* value.
|
|
185
|
-
*/
|
|
186
|
-
function triggerVisibility(sourceKey, currentValue) {
|
|
187
|
-
const config = userSettings[sourceKey];
|
|
188
|
-
if (config && config.visibilityControl) {
|
|
189
|
-
const {targetField, expectedValue} = config.visibilityControl;
|
|
190
|
-
const targetElement = elementsMap[targetField];
|
|
191
|
-
|
|
192
|
-
if (targetElement) {
|
|
193
|
-
const shouldBeVisible = String(currentValue) === String(expectedValue);
|
|
194
|
-
targetElement.style.display = shouldBeVisible ? '' : 'none';
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return fragment;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
1
|
export function generateMkvScriptForSystemWindows({vid, title}) {
|
|
203
2
|
let args = {vid, title};
|
|
204
3
|
|
package/src/opTab.js
CHANGED
|
@@ -1,198 +1,237 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Enhances a tab object by explicitly attaching its ID to the 'tabId' property.
|
|
3
3
|
*
|
|
4
|
-
* @param
|
|
5
|
-
* @returns {
|
|
4
|
+
* @param {browser.tabs.Tab} tab
|
|
5
|
+
* @returns {browser.tabs.Tab & {tabId: number}|null}
|
|
6
6
|
*/
|
|
7
|
-
export
|
|
8
|
-
|
|
7
|
+
export function tabOpEnhance(tab) {
|
|
8
|
+
if (!tab || typeof tab.id !== 'number') return null;
|
|
9
|
+
return {...tab, tabId: tab.id};
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Creates a normal tab using
|
|
13
|
+
* Creates a normal tab using a properties object.
|
|
13
14
|
*
|
|
14
|
-
* @param {
|
|
15
|
-
* @returns {Promise<
|
|
15
|
+
* @param {Object} properties
|
|
16
|
+
* @returns {Promise<browser.tabs.Tab & {tabId: number}>}
|
|
16
17
|
*/
|
|
17
18
|
export async function tabOpCreate(properties) {
|
|
18
|
-
|
|
19
|
-
let tab = await browser.tabs.create(properties);
|
|
19
|
+
const tab = await browser.tabs.create(properties);
|
|
20
20
|
return tabOpEnhance(tab);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
+
* Creates a new tab positioned immediately after a specified existing tab.
|
|
25
|
+
* Does not mutate the original properties object.
|
|
24
26
|
*
|
|
25
|
-
* @param {
|
|
26
|
-
* @returns {Promise<
|
|
27
|
+
* @param {Object & {tabId?: number}} properties
|
|
28
|
+
* @returns {Promise<browser.tabs.Tab & {tabId: number}>}
|
|
27
29
|
*/
|
|
28
30
|
export async function tabOpCreateNear(properties) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
// Destructure tabId out to avoid mutating the original properties object
|
|
32
|
+
const {tabId, ...restProperties} = properties;
|
|
33
|
+
|
|
34
|
+
if (tabId) {
|
|
35
|
+
const tabPrev = await tabOpGet(tabId);
|
|
36
|
+
if (tabPrev) {
|
|
37
|
+
Object.assign(restProperties, {
|
|
38
|
+
index: tabPrev.index + 1,
|
|
39
|
+
openerTabId: tabPrev.id,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
const tab = await browser.tabs.create(restProperties);
|
|
39
45
|
return tabOpEnhance(tab);
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* @
|
|
49
|
+
* Creates a tab in the background (inactive and muted).
|
|
50
|
+
*
|
|
51
|
+
* @param {Object} properties
|
|
52
|
+
* @returns {Promise<browser.tabs.Tab & {tabId: number}>}
|
|
46
53
|
*/
|
|
47
54
|
export async function tabOpCreateActiveFalse(properties) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
const mergedProperties = {
|
|
56
|
+
...properties,
|
|
57
|
+
active: false,
|
|
58
|
+
muted: true,
|
|
59
|
+
};
|
|
60
|
+
const tab = await browser.tabs.create(mergedProperties);
|
|
54
61
|
return tabOpEnhance(tab);
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
/**
|
|
58
|
-
* Creates a
|
|
65
|
+
* Creates a completely new window and returns its initial tab.
|
|
59
66
|
*
|
|
60
67
|
* @param {string} url
|
|
61
|
-
* @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
|
|
68
|
+
* @returns {Promise<(browser.tabs.Tab & {tabId: number})|undefined>}
|
|
62
69
|
*/
|
|
63
70
|
export async function tabOpCreateByWindow(url) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
let tab = window.tabs.shift();
|
|
70
|
-
return tabOpEnhance(tab);
|
|
71
|
+
if (typeof url !== 'string') return undefined;
|
|
72
|
+
|
|
73
|
+
const window = await browser.windows.create({url});
|
|
74
|
+
if (window && window.tabs && window.tabs.length > 0) {
|
|
75
|
+
return tabOpEnhance(window.tabs[0]);
|
|
71
76
|
}
|
|
77
|
+
return undefined;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
/**
|
|
81
|
+
* Retrieves details about the specified tab.
|
|
75
82
|
*
|
|
76
|
-
* @param
|
|
77
|
-
* @
|
|
83
|
+
* @param {number} tabId
|
|
84
|
+
* @returns {Promise<browser.tabs.Tab>}
|
|
78
85
|
*/
|
|
79
|
-
export
|
|
80
|
-
return
|
|
86
|
+
export function tabOpGet(tabId) {
|
|
87
|
+
return browser.tabs.get(tabId);
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
/**
|
|
91
|
+
* Retrieves all tabs across all windows.
|
|
84
92
|
*
|
|
85
93
|
* @returns {Promise<browser.tabs.Tab[]>}
|
|
86
94
|
*/
|
|
87
|
-
export
|
|
88
|
-
return
|
|
95
|
+
export function tabOpQueryAll() {
|
|
96
|
+
return browser.tabs.query({});
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
/**
|
|
100
|
+
* Retrieves an array of tab IDs that match the specified URL pattern.
|
|
92
101
|
*
|
|
93
|
-
* @param
|
|
102
|
+
* @param {string} urlQuery
|
|
94
103
|
* @returns {Promise<number[]>}
|
|
95
104
|
*/
|
|
96
105
|
export async function tabOpQueryUrl(urlQuery) {
|
|
97
|
-
|
|
98
|
-
return tabs.map(
|
|
106
|
+
const tabs = await browser.tabs.query({url: urlQuery});
|
|
107
|
+
return tabs.map(tab => tab.id);
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
/**
|
|
111
|
+
* Finds all tabs matching the URL pattern and removes them simultaneously.
|
|
112
|
+
* [Optimization]: browser.tabs.remove accepts an array of numbers, which is
|
|
113
|
+
* significantly faster than looping and awaiting individually.
|
|
102
114
|
*
|
|
103
|
-
* @param
|
|
115
|
+
* @param {string} urlQuery
|
|
104
116
|
* @returns {Promise<void>}
|
|
105
117
|
*/
|
|
106
118
|
export async function tabOpQueryUrlThenRemove(urlQuery) {
|
|
107
|
-
|
|
108
|
-
ids.
|
|
119
|
+
const ids = await tabOpQueryUrl(urlQuery);
|
|
120
|
+
if (ids.length > 0) {
|
|
121
|
+
await browser.tabs.remove(ids);
|
|
122
|
+
}
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
/**
|
|
126
|
+
* Reloads the given tab.
|
|
112
127
|
*
|
|
113
|
-
* @param
|
|
128
|
+
* @param {number} tabId
|
|
114
129
|
* @returns {Promise<void>}
|
|
115
130
|
*/
|
|
116
|
-
export
|
|
117
|
-
|
|
131
|
+
export function tabOpReload(tabId) {
|
|
132
|
+
return browser.tabs.reload(tabId);
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
/**
|
|
136
|
+
* Reloads the given tab, bypassing the local web cache.
|
|
121
137
|
*
|
|
122
|
-
* @param
|
|
138
|
+
* @param {number} tabId
|
|
123
139
|
* @returns {Promise<void>}
|
|
124
140
|
*/
|
|
125
|
-
export
|
|
126
|
-
|
|
141
|
+
export function tabOpReloadByPassCacheTrue(tabId) {
|
|
142
|
+
return browser.tabs.reload(tabId, {bypassCache: true});
|
|
127
143
|
}
|
|
128
144
|
|
|
129
145
|
/**
|
|
146
|
+
* Closes one or more tabs.
|
|
130
147
|
*
|
|
131
|
-
* @param
|
|
148
|
+
* @param {number|number[]} tabId
|
|
132
149
|
* @returns {Promise<void>}
|
|
133
150
|
*/
|
|
134
|
-
export
|
|
135
|
-
|
|
151
|
+
export function tabOpRemove(tabId) {
|
|
152
|
+
return browser.tabs.remove(tabId);
|
|
136
153
|
}
|
|
137
154
|
|
|
138
155
|
/**
|
|
156
|
+
* Hides one or more tabs (Firefox specific API).
|
|
139
157
|
*
|
|
140
|
-
* @param
|
|
141
|
-
* @returns {Promise<
|
|
158
|
+
* @param {number|number[]} tabId
|
|
159
|
+
* @returns {Promise<number[]>}
|
|
142
160
|
*/
|
|
143
|
-
export
|
|
144
|
-
|
|
161
|
+
export function tabOpHide(tabId) {
|
|
162
|
+
return browser.tabs.hide(tabId);
|
|
145
163
|
}
|
|
146
164
|
|
|
147
165
|
/**
|
|
166
|
+
* Modifies the properties of a tab.
|
|
148
167
|
*
|
|
149
|
-
* @param {number}tabId
|
|
150
|
-
* @param {
|
|
151
|
-
* @returns {Promise<browser.tabs.Tab&{tabId: number}>}
|
|
152
|
-
*/
|
|
153
|
-
export async function tabOpUpdate(
|
|
154
|
-
|
|
168
|
+
* @param {number} tabId
|
|
169
|
+
* @param {Object} updateProperties
|
|
170
|
+
* @returns {Promise<browser.tabs.Tab & {tabId: number}>}
|
|
171
|
+
*/
|
|
172
|
+
export async function tabOpUpdate(
|
|
173
|
+
tabId,
|
|
174
|
+
updateProperties,
|
|
175
|
+
) {
|
|
176
|
+
const tab = await browser.tabs.update(tabId, updateProperties);
|
|
155
177
|
return tabOpEnhance(tab);
|
|
156
178
|
}
|
|
157
179
|
|
|
158
180
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* @
|
|
181
|
+
* Updates a tab to be inactive and muted.
|
|
182
|
+
*
|
|
183
|
+
* @param {number} tabId
|
|
184
|
+
* @returns {Promise<browser.tabs.Tab & {tabId: number}>}
|
|
162
185
|
*/
|
|
163
|
-
export
|
|
164
|
-
return
|
|
186
|
+
export function tabOpUpdateActiveFalse(tabId) {
|
|
187
|
+
return tabOpUpdate(tabId, {active: false, muted: true});
|
|
165
188
|
}
|
|
166
189
|
|
|
167
190
|
/**
|
|
168
|
-
*
|
|
191
|
+
* Focuses the window containing the tab, then highlights and activates the tab.
|
|
192
|
+
*
|
|
193
|
+
* @param {number} tabId
|
|
194
|
+
* @returns {Promise<browser.tabs.Tab & {tabId: number}>}
|
|
169
195
|
*/
|
|
170
196
|
export async function tabOpFocus(tabId) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
197
|
+
const tab = await tabOpGet(tabId);
|
|
198
|
+
if (!tab || !tab.windowId) throw new Error(`Tab ${tabId} not found.`);
|
|
199
|
+
|
|
200
|
+
await browser.windows.update(tab.windowId, {focused: true});
|
|
201
|
+
|
|
202
|
+
const tabUpdated = await browser.tabs.update(tabId, {
|
|
203
|
+
active: true,
|
|
204
|
+
highlighted: true,
|
|
205
|
+
});
|
|
174
206
|
|
|
175
|
-
let updateProperties = {active: true, highlighted: true};
|
|
176
|
-
let tabUpdated = await tabOpUpdate(tabId, updateProperties);
|
|
177
207
|
return tabOpEnhance(tabUpdated);
|
|
178
208
|
}
|
|
179
209
|
|
|
180
210
|
/**
|
|
211
|
+
* Injects CSS code into a page.
|
|
212
|
+
* Note: Check manifest version compatibility for 'insertCSS'.
|
|
181
213
|
*
|
|
182
|
-
* @param{number} tabId
|
|
183
|
-
* @param {string}code
|
|
214
|
+
* @param {number} tabId
|
|
215
|
+
* @param {string} code
|
|
184
216
|
* @returns {Promise<void>}
|
|
185
217
|
*/
|
|
186
|
-
export
|
|
187
|
-
|
|
218
|
+
export function tabOpInsertCssCode(
|
|
219
|
+
tabId,
|
|
220
|
+
code,
|
|
221
|
+
) {
|
|
222
|
+
return browser.tabs.insertCSS(tabId, {code});
|
|
188
223
|
}
|
|
189
224
|
|
|
190
225
|
/**
|
|
226
|
+
* Removes CSS code that was previously injected into a page.
|
|
191
227
|
*
|
|
192
|
-
* @param{number} tabId
|
|
193
|
-
* @param {string}code
|
|
228
|
+
* @param {number} tabId
|
|
229
|
+
* @param {string} code
|
|
194
230
|
* @returns {Promise<void>}
|
|
195
231
|
*/
|
|
196
|
-
export
|
|
197
|
-
|
|
232
|
+
export function tabOpRemoveCssCode(
|
|
233
|
+
tabId,
|
|
234
|
+
code,
|
|
235
|
+
) {
|
|
236
|
+
return browser.tabs.removeCSS(tabId, {code});
|
|
198
237
|
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import {stoOpGet, stoOpSet} from './opStorage.js';
|
|
2
|
-
|
|
3
|
-
export async function serviceInitUserSettings(userSettings) {
|
|
4
|
-
const initPromises = Object.entries(userSettings)
|
|
5
|
-
.map(async ([key, setting]) => {
|
|
6
|
-
const oldValue = await stoOpGet(key);
|
|
7
|
-
|
|
8
|
-
// FIX: Check strictly for null or undefined.
|
|
9
|
-
// This allows `false` and `0` to be recognized as valid saved values.
|
|
10
|
-
if (oldValue === null || oldValue === undefined) {
|
|
11
|
-
await stoOpSet(key, setting.selected);
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
await Promise.all(initPromises);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* // todo
|
|
20
|
-
* @param userSettings{{}}
|
|
21
|
-
* @returns {Promise<{Object}>}
|
|
22
|
-
*/
|
|
23
|
-
export async function serviceGetUserSettings(userSettings) {
|
|
24
|
-
const red = {};
|
|
25
|
-
for (let k of Object.keys(userSettings)) {
|
|
26
|
-
red[k] = await stoOpGet(k);
|
|
27
|
-
}
|
|
28
|
-
return red;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|