dsh-mobile 0.6.5 → 0.6.7
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 +22 -0
- package/CONTRIBUTORS.md +15 -2
- package/README.en.md +6 -3
- package/README.md +6 -3
- package/apps/mobile/README.md +7 -1
- package/docs/CPOLAR_SUBDOMAIN.en.md +81 -0
- package/docs/CPOLAR_SUBDOMAIN.md +81 -0
- package/docs/README.md +1 -0
- package/docs/SKIN_COMPATIBILITY.en.md +1 -1
- package/docs/SKIN_COMPATIBILITY.md +1 -1
- package/lib/cli.js +2 -0
- package/lib/cli.js.map +1 -1
- package/lib/client.js +434 -159
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +13 -2
- package/lib/index.mjs +89 -59
- package/lib/index.mjs.map +1 -1
- package/package.json +7 -1
- package/packages/question-fixes/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3204,6 +3204,127 @@ window.__ModuleLoader__.load({
|
|
|
3204
3204
|
[data-dsh-mobile-composer-model-label] { flex:1 1 auto !important; max-width:none !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }
|
|
3205
3205
|
}
|
|
3206
3206
|
`;
|
|
3207
|
+
//#endregion
|
|
3208
|
+
//#region src/browser-file-picker.ts
|
|
3209
|
+
/** Browser chooser lifetime shared by camera controls and the extension SDK. */
|
|
3210
|
+
const BROWSER_FILE_PICKER_DEADLINE_MS = 3e5;
|
|
3211
|
+
const browserEnvironment = {
|
|
3212
|
+
createSource: () => {
|
|
3213
|
+
const input = document.createElement("input");
|
|
3214
|
+
return {
|
|
3215
|
+
input,
|
|
3216
|
+
open: () => {
|
|
3217
|
+
document.body.append(input);
|
|
3218
|
+
input.click();
|
|
3219
|
+
},
|
|
3220
|
+
remove: () => {
|
|
3221
|
+
input.remove();
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
},
|
|
3225
|
+
deadline: (callback, milliseconds) => {
|
|
3226
|
+
const timer = window.setTimeout(callback, milliseconds);
|
|
3227
|
+
return () => {
|
|
3228
|
+
window.clearTimeout(timer);
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3231
|
+
};
|
|
3232
|
+
/** Select files without treating focus/visibility restoration as cancellation.
|
|
3233
|
+
* @param options - MIME selection, lifetime and optional camera capture mode.
|
|
3234
|
+
* @param environment - Browser DOM and timer owner; tests may supply an owned source.
|
|
3235
|
+
* @returns Selected files, or undefined for explicit cancellation or deadline; abort rejects.
|
|
3236
|
+
*/
|
|
3237
|
+
function pickBrowserFiles(options, environment = browserEnvironment) {
|
|
3238
|
+
const abortReason = () => options.abortReason?.() ?? options.signal.reason ?? new DOMException("File selection aborted", "AbortError");
|
|
3239
|
+
if (options.signal.aborted) return Promise.reject(abortReason());
|
|
3240
|
+
return new Promise((resolve, reject) => {
|
|
3241
|
+
let source;
|
|
3242
|
+
let input;
|
|
3243
|
+
let settled = false;
|
|
3244
|
+
let cancelDeadline;
|
|
3245
|
+
const cleanup = () => {
|
|
3246
|
+
const errors = [];
|
|
3247
|
+
const attempt = (operation) => {
|
|
3248
|
+
try {
|
|
3249
|
+
operation();
|
|
3250
|
+
} catch (error) {
|
|
3251
|
+
errors.push(error);
|
|
3252
|
+
}
|
|
3253
|
+
};
|
|
3254
|
+
if (cancelDeadline !== void 0) attempt(cancelDeadline);
|
|
3255
|
+
cancelDeadline = void 0;
|
|
3256
|
+
attempt(() => {
|
|
3257
|
+
input?.removeEventListener("change", onChange);
|
|
3258
|
+
});
|
|
3259
|
+
attempt(() => {
|
|
3260
|
+
input?.removeEventListener("cancel", onCancel);
|
|
3261
|
+
});
|
|
3262
|
+
attempt(() => {
|
|
3263
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
3264
|
+
});
|
|
3265
|
+
attempt(() => {
|
|
3266
|
+
source?.remove();
|
|
3267
|
+
});
|
|
3268
|
+
return errors;
|
|
3269
|
+
};
|
|
3270
|
+
const finish = (outcome) => {
|
|
3271
|
+
if (settled) return;
|
|
3272
|
+
settled = true;
|
|
3273
|
+
const cleanupErrors = cleanup();
|
|
3274
|
+
if (outcome.kind === "failed") reject(outcome.error);
|
|
3275
|
+
else if (cleanupErrors.length > 0) reject(cleanupErrors[0]);
|
|
3276
|
+
else if (outcome.kind === "cancelled") resolve(void 0);
|
|
3277
|
+
else resolve(outcome.files);
|
|
3278
|
+
};
|
|
3279
|
+
const onChange = () => {
|
|
3280
|
+
if (settled || input === void 0) return;
|
|
3281
|
+
try {
|
|
3282
|
+
const files = input.files === null ? [] : [...input.files];
|
|
3283
|
+
finish(files.length === 0 ? { kind: "cancelled" } : {
|
|
3284
|
+
kind: "files",
|
|
3285
|
+
files: Object.freeze(files)
|
|
3286
|
+
});
|
|
3287
|
+
} catch (error) {
|
|
3288
|
+
finish({
|
|
3289
|
+
kind: "failed",
|
|
3290
|
+
error
|
|
3291
|
+
});
|
|
3292
|
+
}
|
|
3293
|
+
};
|
|
3294
|
+
const onCancel = () => {
|
|
3295
|
+
finish({ kind: "cancelled" });
|
|
3296
|
+
};
|
|
3297
|
+
const onAbort = () => {
|
|
3298
|
+
finish({
|
|
3299
|
+
kind: "failed",
|
|
3300
|
+
error: abortReason()
|
|
3301
|
+
});
|
|
3302
|
+
};
|
|
3303
|
+
try {
|
|
3304
|
+
source = environment.createSource();
|
|
3305
|
+
input = source.input;
|
|
3306
|
+
input.type = "file";
|
|
3307
|
+
input.accept = options.accept;
|
|
3308
|
+
if (options.capture !== void 0) input.capture = options.capture;
|
|
3309
|
+
input.multiple = options.multiple ?? false;
|
|
3310
|
+
input.hidden = true;
|
|
3311
|
+
input.addEventListener("change", onChange);
|
|
3312
|
+
input.addEventListener("cancel", onCancel);
|
|
3313
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
3314
|
+
cancelDeadline = environment.deadline(onCancel, BROWSER_FILE_PICKER_DEADLINE_MS);
|
|
3315
|
+
if (options.signal.aborted) {
|
|
3316
|
+
onAbort();
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
source.open();
|
|
3320
|
+
} catch (error) {
|
|
3321
|
+
finish({
|
|
3322
|
+
kind: "failed",
|
|
3323
|
+
error
|
|
3324
|
+
});
|
|
3325
|
+
}
|
|
3326
|
+
});
|
|
3327
|
+
}
|
|
3207
3328
|
//#endregion
|
|
3208
3329
|
//#region src/native-mobile.ts
|
|
3209
3330
|
/**
|
|
@@ -3276,6 +3397,13 @@ window.__ModuleLoader__.load({
|
|
|
3276
3397
|
html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*="_sessionRow"] [class*="_pinIndicator"] { display:none !important; }
|
|
3277
3398
|
html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*="_rowActions"] button { box-sizing:border-box !important; width:32px !important; min-width:32px !important; height:32px !important; min-height:32px !important; }
|
|
3278
3399
|
[data-dsh-mobile-frame] { grid-template-columns:0 minmax(0,1fr) 0 !important; width:100% !important; height:100dvh !important; overflow:hidden !important; }
|
|
3400
|
+
/* The stock frame owns layout, not a tap target. Restore only its content
|
|
3401
|
+
columns; the stock shell-overlay retains its own descendant hit policy. */
|
|
3402
|
+
html.dsh-native-mobile-active [data-dsh-mobile-frame] { pointer-events:none !important; }
|
|
3403
|
+
html.dsh-native-mobile-active [data-dsh-mobile-frame] > :is([data-dsh-mobile-sidebar],[data-dsh-mobile-center],[class*="_rightbarCol"]) { pointer-events:auto !important; }
|
|
3404
|
+
/* Floating slot surfaces paint above mobile drawers; their empty layer
|
|
3405
|
+
stays click-through and each stock occupant retains its own hit policy. */
|
|
3406
|
+
html.dsh-native-mobile-active [data-shell-overlay] { z-index:300 !important; pointer-events:none !important; }
|
|
3279
3407
|
[data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }
|
|
3280
3408
|
[data-dsh-mobile-center] > * { min-width:0 !important; }
|
|
3281
3409
|
[data-dsh-mobile-header] { box-sizing:border-box !important; width:calc(100% - 16px) !important; margin:0 8px !important; min-width:0; padding-top:max(4px,env(safe-area-inset-top)) !important; padding-right:8px !important; padding-left:42px !important; }
|
|
@@ -3297,6 +3425,10 @@ window.__ModuleLoader__.load({
|
|
|
3297
3425
|
[data-dsh-mobile-sidebar] { position:fixed !important; z-index:240 !important; inset:0 auto 0 0 !important; width:0 !important; overflow:visible !important; }
|
|
3298
3426
|
[data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:max(env(safe-area-inset-top),0px) auto 0 0 !important; height:auto !important; transition:width 180ms var(--dsh-mobile-motion-ease),box-shadow 180ms ease !important; }
|
|
3299
3427
|
[data-dsh-mobile-sidebar][data-open="true"] [data-dsh-mobile-sidebar-root] { width:min(88vw,340px) !important; padding-top:0 !important; box-shadow:18px 0 46px rgb(15 23 42 / 18%); }
|
|
3428
|
+
/* Extra panel/footer seats may exceed a short drawer. Keep a bounded
|
|
3429
|
+
workspace scrollport and let the drawer scroll to the remaining seats. */
|
|
3430
|
+
[data-dsh-mobile-sidebar][data-open="true"]:not(:has([data-dsh-mobile-settings])) [data-dsh-mobile-sidebar-root] { overflow-y:auto !important; overscroll-behavior-y:contain; }
|
|
3431
|
+
[data-dsh-mobile-sidebar][data-open="true"]:not(:has([data-dsh-mobile-settings])) [data-dsh-mobile-sidebar-root] > [class*="_regionArea"] { flex:1 0 50% !important; }
|
|
3300
3432
|
[data-dsh-mobile-sidebar][data-open="true"] [data-dsh-mobile-sidebar-root] [class*="_logoRow"] { height:52px !important; padding:4px 0 4px 4px !important; margin-bottom:4px !important; }
|
|
3301
3433
|
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }
|
|
3302
3434
|
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }
|
|
@@ -3624,19 +3756,66 @@ window.__ModuleLoader__.load({
|
|
|
3624
3756
|
}
|
|
3625
3757
|
};
|
|
3626
3758
|
}
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3759
|
+
/** A read-only, file-only transfer for engines without usable native constructors. */
|
|
3760
|
+
function controlledFileTransfer(files, initialDropEffect) {
|
|
3761
|
+
const snapshot = [...files];
|
|
3762
|
+
const fileList = Object.assign(snapshot, { item: (index) => snapshot[index] ?? null });
|
|
3763
|
+
const items = Object.assign(snapshot.map((file) => ({
|
|
3764
|
+
kind: "file",
|
|
3765
|
+
type: file.type,
|
|
3766
|
+
getAsFile: () => file,
|
|
3767
|
+
getAsString: () => {},
|
|
3768
|
+
webkitGetAsEntry: () => null
|
|
3769
|
+
})), {
|
|
3770
|
+
add: () => null,
|
|
3771
|
+
clear: () => {},
|
|
3772
|
+
remove: () => {}
|
|
3631
3773
|
});
|
|
3632
|
-
|
|
3774
|
+
Object.freeze(fileList);
|
|
3775
|
+
Object.freeze(items);
|
|
3776
|
+
return {
|
|
3633
3777
|
dropEffect: initialDropEffect,
|
|
3634
3778
|
effectAllowed: "copy",
|
|
3635
|
-
files:
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3779
|
+
files: fileList,
|
|
3780
|
+
items,
|
|
3781
|
+
types: Object.freeze(["Files"]),
|
|
3782
|
+
clearData: () => {},
|
|
3783
|
+
getData: () => "",
|
|
3784
|
+
setData: () => {},
|
|
3785
|
+
setDragImage: () => {}
|
|
3786
|
+
};
|
|
3787
|
+
}
|
|
3788
|
+
function nativeFileTransfer(files, initialDropEffect) {
|
|
3789
|
+
const transfer = new DataTransfer();
|
|
3790
|
+
for (const file of files) if (transfer.items.add(file) === null) throw new Error("file_transfer_unavailable");
|
|
3791
|
+
if (transfer.files.length !== files.length) throw new Error("file_transfer_unavailable");
|
|
3792
|
+
transfer.effectAllowed = "copy";
|
|
3793
|
+
transfer.dropEffect = "copy";
|
|
3794
|
+
if (transfer.effectAllowed !== "copy" || transfer.dropEffect !== "copy") throw new Error("file_transfer_unavailable");
|
|
3795
|
+
transfer.dropEffect = initialDropEffect;
|
|
3796
|
+
if (transfer.dropEffect !== initialDropEffect) throw new Error("file_transfer_unavailable");
|
|
3797
|
+
return transfer;
|
|
3798
|
+
}
|
|
3799
|
+
function controlledFileDragEvent(type, files, initialDropEffect) {
|
|
3800
|
+
let transfer;
|
|
3801
|
+
if (typeof DataTransfer === "function") try {
|
|
3802
|
+
transfer = nativeFileTransfer(files, initialDropEffect);
|
|
3803
|
+
} catch (_error) {
|
|
3804
|
+
transfer = controlledFileTransfer(files, initialDropEffect);
|
|
3805
|
+
}
|
|
3806
|
+
else transfer = controlledFileTransfer(files, initialDropEffect);
|
|
3807
|
+
if (typeof DragEvent === "function") try {
|
|
3808
|
+
const event = new DragEvent(type, {
|
|
3809
|
+
bubbles: true,
|
|
3810
|
+
cancelable: true,
|
|
3811
|
+
dataTransfer: transfer
|
|
3812
|
+
});
|
|
3813
|
+
if (event.dataTransfer !== null) return event;
|
|
3814
|
+
} catch (_error) {}
|
|
3815
|
+
return Object.assign(new Event(type, {
|
|
3816
|
+
bubbles: true,
|
|
3817
|
+
cancelable: true
|
|
3818
|
+
}), { dataTransfer: transfer });
|
|
3640
3819
|
}
|
|
3641
3820
|
/** Ask the official document dragover listener whether the current composer accepts files. */
|
|
3642
3821
|
function preflightComposerImageDrop(target, files) {
|
|
@@ -4086,7 +4265,6 @@ window.__ModuleLoader__.load({
|
|
|
4086
4265
|
const preflightFile = new File([], "dsh-mobile-preflight.png", { type: "image/png" });
|
|
4087
4266
|
const canAcceptComposerDrop = () => preflightComposerImageDrop(document, [preflightFile]);
|
|
4088
4267
|
const mediaPickerAbortController = new AbortController();
|
|
4089
|
-
const browserPickerCleanups = /* @__PURE__ */ new Set();
|
|
4090
4268
|
let boundEnterEditor = null;
|
|
4091
4269
|
let boundEnterCard = null;
|
|
4092
4270
|
let disposeEnterBinding;
|
|
@@ -4133,63 +4311,18 @@ window.__ModuleLoader__.load({
|
|
|
4133
4311
|
};
|
|
4134
4312
|
const deliverImages = (files, context) => {
|
|
4135
4313
|
if (files.length === 0 || !mediaRequestIsCurrent(context)) return;
|
|
4136
|
-
dispatchComposerImageDrop(document, files);
|
|
4314
|
+
if (!dispatchComposerImageDrop(document, files)) showMediaToast(label("Allegati immagine non disponibili", "Image attachments are unavailable", "图片附件不可用"));
|
|
4137
4315
|
};
|
|
4138
4316
|
const launchBrowserCamera = (context) => {
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
const scheduleCleanup = () => {
|
|
4149
|
-
if (!cleaned && cleanupTimer === 0) cleanupTimer = window.setTimeout(cleanup, 1e3);
|
|
4150
|
-
};
|
|
4151
|
-
const onVisibilityChange = () => {
|
|
4152
|
-
if (document.visibilityState === "visible") scheduleCleanup();
|
|
4153
|
-
};
|
|
4154
|
-
const onChange = () => {
|
|
4155
|
-
if (cleaned) return;
|
|
4156
|
-
const files = input.files === null ? [] : [...input.files];
|
|
4157
|
-
cleanup();
|
|
4158
|
-
deliverImages(files, context);
|
|
4159
|
-
};
|
|
4160
|
-
const cleanup = () => {
|
|
4161
|
-
if (cleaned) return;
|
|
4162
|
-
cleaned = true;
|
|
4163
|
-
if (cleanupTimer !== 0) window.clearTimeout(cleanupTimer);
|
|
4164
|
-
if (watchdogTimer !== 0) window.clearTimeout(watchdogTimer);
|
|
4165
|
-
cleanupTimer = 0;
|
|
4166
|
-
watchdogTimer = 0;
|
|
4167
|
-
input.removeEventListener("change", onChange);
|
|
4168
|
-
input.removeEventListener("cancel", cleanup);
|
|
4169
|
-
window.removeEventListener("focus", scheduleCleanup);
|
|
4170
|
-
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
4171
|
-
signal.removeEventListener("abort", cleanup);
|
|
4172
|
-
browserPickerCleanups.delete(cleanup);
|
|
4173
|
-
input.remove();
|
|
4174
|
-
};
|
|
4175
|
-
input.addEventListener("change", onChange);
|
|
4176
|
-
input.addEventListener("cancel", cleanup);
|
|
4177
|
-
window.addEventListener("focus", scheduleCleanup);
|
|
4178
|
-
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
4179
|
-
signal.addEventListener("abort", cleanup, { once: true });
|
|
4180
|
-
watchdogTimer = window.setTimeout(cleanup, 3e5);
|
|
4181
|
-
browserPickerCleanups.add(cleanup);
|
|
4182
|
-
if (signal.aborted) {
|
|
4183
|
-
cleanup();
|
|
4184
|
-
return;
|
|
4185
|
-
}
|
|
4186
|
-
try {
|
|
4187
|
-
document.body.append(input);
|
|
4188
|
-
input.click();
|
|
4189
|
-
} catch {
|
|
4190
|
-
cleanup();
|
|
4191
|
-
if (mediaRequestIsCurrent(context)) showMediaToast(label("Impossibile aprire la fotocamera", "Could not open the camera", "无法打开相机"));
|
|
4192
|
-
}
|
|
4317
|
+
pickBrowserFiles({
|
|
4318
|
+
accept: "image/*",
|
|
4319
|
+
capture: "environment",
|
|
4320
|
+
signal: mediaPickerAbortController.signal
|
|
4321
|
+
}).then((files) => {
|
|
4322
|
+
if (files !== void 0) deliverImages(files, context);
|
|
4323
|
+
}).catch((_error) => {
|
|
4324
|
+
if (mediaRequestIsCurrent(context)) showMediaToast(label("Impossibile aggiungere la foto", "Could not attach the photo", "无法附加照片"));
|
|
4325
|
+
});
|
|
4193
4326
|
};
|
|
4194
4327
|
const dismissCommandMenu = () => {
|
|
4195
4328
|
(boundComposer?.querySelector("button[aria-haspopup=\"listbox\"][aria-expanded=\"true\"]"))?.click();
|
|
@@ -4647,7 +4780,6 @@ window.__ModuleLoader__.load({
|
|
|
4647
4780
|
mediaRequestGeneration += 1;
|
|
4648
4781
|
mediaPickerAbortController.abort();
|
|
4649
4782
|
restoreLanguageMarker();
|
|
4650
|
-
for (const cleanup of [...browserPickerCleanups]) cleanup();
|
|
4651
4783
|
disposeEnterBinding?.();
|
|
4652
4784
|
disposeBrowserComposerSoftEnter();
|
|
4653
4785
|
observer.disconnect();
|
|
@@ -6686,6 +6818,122 @@ window.__ModuleLoader__.load({
|
|
|
6686
6818
|
});
|
|
6687
6819
|
}
|
|
6688
6820
|
//#endregion
|
|
6821
|
+
//#region src/mobile-custom-assets.ts
|
|
6822
|
+
const COPY = {
|
|
6823
|
+
en: {
|
|
6824
|
+
gateway_required: "Custom mobile CSS, scripts and extensions require a paired DSH Mobile access entry. This page is not using the Mobile gateway.",
|
|
6825
|
+
authentication_required: "Mobile customization was not refreshed because device authentication is unavailable. Reconnect through the current Mobile access entry.",
|
|
6826
|
+
manifest_unavailable: "The mobile extension manifest is unavailable. Check the plugin version and proxy routing; legacy custom files can still load when authorized.",
|
|
6827
|
+
manifest_invalid: "The mobile extension manifest is invalid. Existing customization is retained; check the plugin version and proxy routing.",
|
|
6828
|
+
resource_unavailable: "A mobile customization resource could not be loaded or activated. Check the custom files and current Mobile access entry.",
|
|
6829
|
+
refresh_failed: "Mobile customization could not be refreshed. Check the connection and current Mobile access entry.",
|
|
6830
|
+
recovered: "Mobile customization is available again."
|
|
6831
|
+
},
|
|
6832
|
+
it: {
|
|
6833
|
+
gateway_required: "CSS, script ed estensioni mobili personalizzati richiedono un accesso DSH Mobile abbinato. Questa pagina non usa il gateway Mobile.",
|
|
6834
|
+
authentication_required: "La personalizzazione mobile non è stata aggiornata perché l’autenticazione del dispositivo non è disponibile. Riconnettiti tramite l’accesso Mobile attuale.",
|
|
6835
|
+
manifest_unavailable: "Il manifest delle estensioni mobili non è disponibile. Controlla la versione del plugin e le route del proxy; i file personalizzati legacy possono essere caricati se autorizzati.",
|
|
6836
|
+
manifest_invalid: "Il manifest delle estensioni mobili non è valido. La personalizzazione esistente viene mantenuta; controlla la versione del plugin e le route del proxy.",
|
|
6837
|
+
resource_unavailable: "Una risorsa mobile personalizzata non è stata caricata o attivata. Controlla i file personalizzati e l’accesso Mobile attuale.",
|
|
6838
|
+
refresh_failed: "Impossibile aggiornare la personalizzazione mobile. Controlla la connessione e l’accesso Mobile attuale.",
|
|
6839
|
+
recovered: "La personalizzazione mobile è di nuovo disponibile."
|
|
6840
|
+
},
|
|
6841
|
+
zh: {
|
|
6842
|
+
gateway_required: "自定义移动样式、脚本与扩展需要通过已配对的 DSH Mobile 入口访问;当前页面不是移动网关页面。",
|
|
6843
|
+
authentication_required: "设备认证不可用,移动定制暂未刷新;请通过当前移动访问入口重新连接。",
|
|
6844
|
+
manifest_unavailable: "移动扩展清单不可用;请检查插件版本及反向代理路由。通过认证时,旧版自定义文件仍可加载。",
|
|
6845
|
+
manifest_invalid: "移动扩展清单无效,保留现有定制;请检查插件版本及反向代理路由。",
|
|
6846
|
+
resource_unavailable: "移动定制资源加载或激活失败;请检查定制文件及当前移动访问入口。",
|
|
6847
|
+
refresh_failed: "移动定制暂未刷新;请检查网络及当前移动访问入口。",
|
|
6848
|
+
recovered: "移动定制已恢复加载。"
|
|
6849
|
+
}
|
|
6850
|
+
};
|
|
6851
|
+
/** Only the authenticated Gateway owns customization APIs; the older dedicated marker remains supported. */
|
|
6852
|
+
function ownsMobileCustomization(gateway, frontend) {
|
|
6853
|
+
return gateway === true || frontend === "dedicated";
|
|
6854
|
+
}
|
|
6855
|
+
/** Emit each safe failure once until recovery, and ignore work after cancellation or disposal. */
|
|
6856
|
+
function createMobileAssetLoadReporter(locale, output = console) {
|
|
6857
|
+
const reported = /* @__PURE__ */ new Set();
|
|
6858
|
+
let disposed = false;
|
|
6859
|
+
return {
|
|
6860
|
+
fail(reason, signal) {
|
|
6861
|
+
if (disposed || signal?.aborted === true || reported.has(reason)) return;
|
|
6862
|
+
if (reason === "resource_unavailable" && reported.has("authentication_required")) return;
|
|
6863
|
+
reported.add(reason);
|
|
6864
|
+
output.warn(`[DSH Mobile] ${COPY[locale()][reason]}`);
|
|
6865
|
+
},
|
|
6866
|
+
recover(signal) {
|
|
6867
|
+
if (disposed || signal.aborted || reported.size === 0) return;
|
|
6868
|
+
reported.clear();
|
|
6869
|
+
output.info(`[DSH Mobile] ${COPY[locale()].recovered}`);
|
|
6870
|
+
},
|
|
6871
|
+
dispose() {
|
|
6872
|
+
disposed = true;
|
|
6873
|
+
reported.clear();
|
|
6874
|
+
}
|
|
6875
|
+
};
|
|
6876
|
+
}
|
|
6877
|
+
/** Read customization text within its file budget; the aggregate manifest has no existing byte cap. */
|
|
6878
|
+
async function readMobileAssetText(response, signal, maxBytes) {
|
|
6879
|
+
const assertActive = () => {
|
|
6880
|
+
if (signal.aborted) throw signal.reason ?? new DOMException("mobile customization cancelled", "AbortError");
|
|
6881
|
+
};
|
|
6882
|
+
if (response.body === null) {
|
|
6883
|
+
assertActive();
|
|
6884
|
+
return "";
|
|
6885
|
+
}
|
|
6886
|
+
const reader = response.body.getReader();
|
|
6887
|
+
let cancelled;
|
|
6888
|
+
const cancel = () => cancelled ??= reader.cancel().catch((error) => {});
|
|
6889
|
+
const aborted = () => {
|
|
6890
|
+
cancel();
|
|
6891
|
+
};
|
|
6892
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
6893
|
+
let completed = false;
|
|
6894
|
+
try {
|
|
6895
|
+
assertActive();
|
|
6896
|
+
const chunks = [];
|
|
6897
|
+
let bytes = 0;
|
|
6898
|
+
while (true) {
|
|
6899
|
+
const next = await reader.read();
|
|
6900
|
+
assertActive();
|
|
6901
|
+
if (next.done) {
|
|
6902
|
+
completed = true;
|
|
6903
|
+
break;
|
|
6904
|
+
}
|
|
6905
|
+
bytes += next.value.byteLength;
|
|
6906
|
+
if (maxBytes !== void 0 && bytes > maxBytes) throw new Error("mobile customization resource is too large");
|
|
6907
|
+
chunks.push(next.value);
|
|
6908
|
+
}
|
|
6909
|
+
const body = new Uint8Array(bytes);
|
|
6910
|
+
let offset = 0;
|
|
6911
|
+
for (const chunk of chunks) {
|
|
6912
|
+
body.set(chunk, offset);
|
|
6913
|
+
offset += chunk.byteLength;
|
|
6914
|
+
}
|
|
6915
|
+
return new TextDecoder().decode(body);
|
|
6916
|
+
} finally {
|
|
6917
|
+
signal.removeEventListener("abort", aborted);
|
|
6918
|
+
if (!completed) await cancel();
|
|
6919
|
+
reader.releaseLock();
|
|
6920
|
+
}
|
|
6921
|
+
}
|
|
6922
|
+
//#endregion
|
|
6923
|
+
//#region src/extension-limits.ts
|
|
6924
|
+
/** Maximum sizes accepted for legacy mobile customization resources. */
|
|
6925
|
+
const LEGACY_CUSTOM_ASSET_LIMIT = 262144;
|
|
6926
|
+
/** Maximum sizes enforced when reading local extension files. */
|
|
6927
|
+
const EXTENSION_LIMITS = Object.freeze({
|
|
6928
|
+
manifest: 65536,
|
|
6929
|
+
script: 1048576,
|
|
6930
|
+
css: 524288,
|
|
6931
|
+
asset: 8388608,
|
|
6932
|
+
assetFiles: 256,
|
|
6933
|
+
assetBytes: 33554432,
|
|
6934
|
+
assetDepth: 8
|
|
6935
|
+
});
|
|
6936
|
+
//#endregion
|
|
6689
6937
|
//#region src/client.ts
|
|
6690
6938
|
/** Map a host-reported provider onto the UI union, defaulting to the built-in one. */
|
|
6691
6939
|
function parseMobileRemoteProvider(value) {
|
|
@@ -10966,6 +11214,7 @@ window.__ModuleLoader__.load({
|
|
|
10966
11214
|
}
|
|
10967
11215
|
/** A missing manifest is authoritative before optional legacy resources finish refreshing. */
|
|
10968
11216
|
async function handleMissingExtensionManifest(clearManifestResources, refreshLegacyResources, signal) {
|
|
11217
|
+
if (signal.aborted) return false;
|
|
10969
11218
|
clearManifestResources();
|
|
10970
11219
|
const results = await refreshLegacyResources();
|
|
10971
11220
|
return !signal.aborted && results.every(Boolean);
|
|
@@ -11058,10 +11307,11 @@ window.__ModuleLoader__.load({
|
|
|
11058
11307
|
function startExtensionChangeStream(changed, runtime = {
|
|
11059
11308
|
window,
|
|
11060
11309
|
create: (url) => new EventSource(url, { withCredentials: true })
|
|
11061
|
-
}, onTaskEvent, onDeviceRevoked) {
|
|
11310
|
+
}, onTaskEvent, onDeviceRevoked, initiallyEnabled = true) {
|
|
11062
11311
|
let source;
|
|
11063
11312
|
let timer;
|
|
11064
11313
|
let disposed = false;
|
|
11314
|
+
let enabled = initiallyEnabled;
|
|
11065
11315
|
let retryMs = 1e3;
|
|
11066
11316
|
const clearTimer = () => {
|
|
11067
11317
|
if (timer === void 0) return;
|
|
@@ -11069,19 +11319,24 @@ window.__ModuleLoader__.load({
|
|
|
11069
11319
|
timer = void 0;
|
|
11070
11320
|
};
|
|
11071
11321
|
const connect = () => {
|
|
11072
|
-
if (disposed || source !== void 0) return;
|
|
11322
|
+
if (disposed || !enabled || source !== void 0) return;
|
|
11073
11323
|
clearTimer();
|
|
11074
11324
|
const next = runtime.create("/mobile-access/extensions/events");
|
|
11075
11325
|
source = next;
|
|
11326
|
+
const current = () => !disposed && enabled && source === next;
|
|
11076
11327
|
next.onopen = () => {
|
|
11077
|
-
retryMs = 1e3;
|
|
11328
|
+
if (current()) retryMs = 1e3;
|
|
11078
11329
|
};
|
|
11079
|
-
next.addEventListener("extensions-changed",
|
|
11330
|
+
next.addEventListener("extensions-changed", () => {
|
|
11331
|
+
if (current()) changed();
|
|
11332
|
+
});
|
|
11080
11333
|
if (onTaskEvent !== void 0) next.addEventListener("task-notify", (message) => {
|
|
11334
|
+
if (!current()) return;
|
|
11081
11335
|
const data = message.data;
|
|
11082
11336
|
onTaskEvent(data);
|
|
11083
11337
|
});
|
|
11084
11338
|
if (onDeviceRevoked !== void 0) next.addEventListener("device-revoked", (message) => {
|
|
11339
|
+
if (!current()) return;
|
|
11085
11340
|
const data = message.data;
|
|
11086
11341
|
if (isDeviceRevokedPayload(data)) onDeviceRevoked();
|
|
11087
11342
|
});
|
|
@@ -11089,14 +11344,14 @@ window.__ModuleLoader__.load({
|
|
|
11089
11344
|
if (source !== next) return;
|
|
11090
11345
|
next.close();
|
|
11091
11346
|
source = void 0;
|
|
11092
|
-
if (disposed) return;
|
|
11347
|
+
if (disposed || !enabled) return;
|
|
11093
11348
|
const delay = retryMs;
|
|
11094
11349
|
retryMs = Math.min(3e4, retryMs * 2);
|
|
11095
11350
|
timer = runtime.window.setTimeout(connect, delay);
|
|
11096
11351
|
};
|
|
11097
11352
|
};
|
|
11098
11353
|
const reconnectNow = () => {
|
|
11099
|
-
if (disposed) return;
|
|
11354
|
+
if (disposed || !enabled) return;
|
|
11100
11355
|
source?.close();
|
|
11101
11356
|
source = void 0;
|
|
11102
11357
|
retryMs = 1e3;
|
|
@@ -11104,13 +11359,23 @@ window.__ModuleLoader__.load({
|
|
|
11104
11359
|
};
|
|
11105
11360
|
runtime.window.addEventListener("online", reconnectNow);
|
|
11106
11361
|
connect();
|
|
11107
|
-
|
|
11362
|
+
const stop = () => {
|
|
11108
11363
|
disposed = true;
|
|
11109
11364
|
clearTimer();
|
|
11110
11365
|
source?.close();
|
|
11111
11366
|
source = void 0;
|
|
11112
11367
|
runtime.window.removeEventListener("online", reconnectNow);
|
|
11113
11368
|
};
|
|
11369
|
+
return Object.assign(stop, { setEnabled(value) {
|
|
11370
|
+
if (disposed || value === enabled) return;
|
|
11371
|
+
enabled = value;
|
|
11372
|
+
if (value) connect();
|
|
11373
|
+
else {
|
|
11374
|
+
clearTimer();
|
|
11375
|
+
source?.close();
|
|
11376
|
+
source = void 0;
|
|
11377
|
+
}
|
|
11378
|
+
} });
|
|
11114
11379
|
}
|
|
11115
11380
|
/** Keep at most one activation in flight per id and commit only its latest generation. */
|
|
11116
11381
|
var PerIdActivationLifecycle = class {
|
|
@@ -11238,7 +11503,10 @@ window.__ModuleLoader__.load({
|
|
|
11238
11503
|
});
|
|
11239
11504
|
}
|
|
11240
11505
|
};
|
|
11506
|
+
/** Install authenticated mobile customization refresh, retaining loaded legacy assets through temporary failures. */
|
|
11241
11507
|
function installCustomAssets() {
|
|
11508
|
+
const reporter = createMobileAssetLoadReporter(selectedMobileControlLocale);
|
|
11509
|
+
const customizationOwner = ownsMobileCustomization(document.querySelector("meta[name=\"dsh-mobile-gateway\"][content=\"authenticated\"]") !== null, window.__DSH_MOBILE_FRONTEND__);
|
|
11242
11510
|
const legacyStyle = element("style");
|
|
11243
11511
|
legacyStyle.dataset.plugin = "dsh-mobile-custom";
|
|
11244
11512
|
document.head.append(legacyStyle);
|
|
@@ -11263,6 +11531,16 @@ window.__ModuleLoader__.load({
|
|
|
11263
11531
|
const managedDefinitionIds = /* @__PURE__ */ new Set();
|
|
11264
11532
|
let manifestEtag = "";
|
|
11265
11533
|
let disposed = false;
|
|
11534
|
+
let events;
|
|
11535
|
+
const failed = (reason, signal) => {
|
|
11536
|
+
if (disposed || signal.aborted) return;
|
|
11537
|
+
reporter.fail(reason, signal);
|
|
11538
|
+
if (reason === "authentication_required") events?.setEnabled(false);
|
|
11539
|
+
};
|
|
11540
|
+
const unavailable = (response, signal, fallback) => {
|
|
11541
|
+
failed(response.status === 401 || response.status === 403 ? "authentication_required" : fallback, signal);
|
|
11542
|
+
response.body?.cancel().catch((error) => {});
|
|
11543
|
+
};
|
|
11266
11544
|
let expectedDefinitionId;
|
|
11267
11545
|
const SURFACE_HOST_STYLES = {
|
|
11268
11546
|
"sidebar-action": "position:fixed;z-index:1100;top:calc(env(safe-area-inset-top) + 8px);left:8px;display:flex;flex-direction:column;gap:6px;pointer-events:none",
|
|
@@ -11300,6 +11578,16 @@ window.__ModuleLoader__.load({
|
|
|
11300
11578
|
const stopNativeBack = installMobileNativeBack(window, document, () => {
|
|
11301
11579
|
toast(mobileBackBlockedMessage(selectedMobileControlLocale()));
|
|
11302
11580
|
});
|
|
11581
|
+
if (!customizationOwner) {
|
|
11582
|
+
reporter.fail("gateway_required");
|
|
11583
|
+
return () => {
|
|
11584
|
+
disposed = true;
|
|
11585
|
+
reporter.dispose();
|
|
11586
|
+
stopNativeBack();
|
|
11587
|
+
legacyStyle.remove();
|
|
11588
|
+
document.querySelector("[data-dsh-mobile-extension-layer]")?.remove();
|
|
11589
|
+
};
|
|
11590
|
+
}
|
|
11303
11591
|
const materializeNativeFile = (value) => {
|
|
11304
11592
|
if (typeof value !== "object" || value === null || !("base64" in value) || !("name" in value)) return value;
|
|
11305
11593
|
const candidate = value;
|
|
@@ -11326,73 +11614,12 @@ window.__ModuleLoader__.load({
|
|
|
11326
11614
|
await navigator.clipboard.writeText(typeof input === "object" && input !== null && "text" in input ? String(input.text) : "");
|
|
11327
11615
|
return { ok: true };
|
|
11328
11616
|
}
|
|
11329
|
-
if (action === "files.pick" || action === "camera.capture") {
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
return new Promise((resolve, reject) => {
|
|
11336
|
-
let settled = false;
|
|
11337
|
-
let cleaned = false;
|
|
11338
|
-
let cleanupTimer = 0;
|
|
11339
|
-
let watchdogTimer = 0;
|
|
11340
|
-
const cleanup = () => {
|
|
11341
|
-
if (cleaned) return;
|
|
11342
|
-
cleaned = true;
|
|
11343
|
-
if (cleanupTimer !== 0) window.clearTimeout(cleanupTimer);
|
|
11344
|
-
if (watchdogTimer !== 0) window.clearTimeout(watchdogTimer);
|
|
11345
|
-
window.removeEventListener("focus", scheduleCancel);
|
|
11346
|
-
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
11347
|
-
signal.removeEventListener("abort", onAbort);
|
|
11348
|
-
inputElement.removeEventListener("change", onChange);
|
|
11349
|
-
inputElement.removeEventListener("cancel", onCancel);
|
|
11350
|
-
inputElement.remove();
|
|
11351
|
-
};
|
|
11352
|
-
const finish = (callback) => {
|
|
11353
|
-
if (settled) return;
|
|
11354
|
-
settled = true;
|
|
11355
|
-
cleanup();
|
|
11356
|
-
callback();
|
|
11357
|
-
};
|
|
11358
|
-
const onChange = () => {
|
|
11359
|
-
const file = inputElement.files?.[0];
|
|
11360
|
-
finish(() => {
|
|
11361
|
-
resolve(file);
|
|
11362
|
-
});
|
|
11363
|
-
};
|
|
11364
|
-
const onCancel = () => {
|
|
11365
|
-
finish(() => {
|
|
11366
|
-
resolve(void 0);
|
|
11367
|
-
});
|
|
11368
|
-
};
|
|
11369
|
-
const onAbort = () => {
|
|
11370
|
-
finish(() => {
|
|
11371
|
-
reject(abortReason());
|
|
11372
|
-
});
|
|
11373
|
-
};
|
|
11374
|
-
const scheduleCancel = () => {
|
|
11375
|
-
if (!settled && cleanupTimer === 0) cleanupTimer = window.setTimeout(onCancel, 1e3);
|
|
11376
|
-
};
|
|
11377
|
-
const onVisibilityChange = () => {
|
|
11378
|
-
if (document.visibilityState === "visible") scheduleCancel();
|
|
11379
|
-
};
|
|
11380
|
-
inputElement.addEventListener("change", onChange, { once: true });
|
|
11381
|
-
inputElement.addEventListener("cancel", onCancel, { once: true });
|
|
11382
|
-
window.addEventListener("focus", scheduleCancel);
|
|
11383
|
-
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
11384
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
11385
|
-
watchdogTimer = window.setTimeout(onCancel, 3e5);
|
|
11386
|
-
try {
|
|
11387
|
-
document.body.append(inputElement);
|
|
11388
|
-
inputElement.click();
|
|
11389
|
-
} catch (error) {
|
|
11390
|
-
finish(() => {
|
|
11391
|
-
reject(error);
|
|
11392
|
-
});
|
|
11393
|
-
}
|
|
11394
|
-
});
|
|
11395
|
-
}
|
|
11617
|
+
if (action === "files.pick" || action === "camera.capture") return pickBrowserFiles({
|
|
11618
|
+
signal,
|
|
11619
|
+
accept: action === "camera.capture" ? "image/*" : "*/*",
|
|
11620
|
+
...action === "camera.capture" ? { capture: "environment" } : {},
|
|
11621
|
+
abortReason
|
|
11622
|
+
}).then((files) => files?.[0]);
|
|
11396
11623
|
throw new Error("native capability is unavailable");
|
|
11397
11624
|
};
|
|
11398
11625
|
const makeApi = (id, hostGeneration, controller, surfaces, surfaceIds) => {
|
|
@@ -11567,10 +11794,13 @@ window.__ModuleLoader__.load({
|
|
|
11567
11794
|
signal
|
|
11568
11795
|
});
|
|
11569
11796
|
if (response.status === 304) return true;
|
|
11570
|
-
if (!response.ok)
|
|
11797
|
+
if (!response.ok) {
|
|
11798
|
+
unavailable(response, signal, "resource_unavailable");
|
|
11799
|
+
return false;
|
|
11800
|
+
}
|
|
11571
11801
|
const nextEtag = response.headers.get("etag") ?? "";
|
|
11572
11802
|
const nextModified = response.headers.get("last-modified") ?? "";
|
|
11573
|
-
const next = await response
|
|
11803
|
+
const next = await readMobileAssetText(response, signal, LEGACY_CUSTOM_ASSET_LIMIT);
|
|
11574
11804
|
if (refreshAborted(signal)) return false;
|
|
11575
11805
|
if (next === legacySource) {
|
|
11576
11806
|
legacyJsEtag = nextEtag;
|
|
@@ -11626,6 +11856,7 @@ window.__ModuleLoader__.load({
|
|
|
11626
11856
|
} catch {
|
|
11627
11857
|
pendingRoot?.remove();
|
|
11628
11858
|
legacyMount = previousMount;
|
|
11859
|
+
failed("resource_unavailable", signal);
|
|
11629
11860
|
return false;
|
|
11630
11861
|
}
|
|
11631
11862
|
};
|
|
@@ -11663,16 +11894,40 @@ window.__ModuleLoader__.load({
|
|
|
11663
11894
|
headers,
|
|
11664
11895
|
signal
|
|
11665
11896
|
});
|
|
11897
|
+
if (disposed || signal.aborted) {
|
|
11898
|
+
response.body?.cancel().catch((error) => {});
|
|
11899
|
+
return false;
|
|
11900
|
+
}
|
|
11666
11901
|
if (response.status === 404) {
|
|
11902
|
+
unavailable(response, signal, "manifest_unavailable");
|
|
11667
11903
|
legacyScriptRevision = "";
|
|
11668
11904
|
legacyStyleRevision = "";
|
|
11669
|
-
|
|
11905
|
+
if (!await handleMissingExtensionManifest(clearManifestExtensions, () => Promise.all([refreshLegacy(signal), refreshCssLegacy(legacyStyle, signal, legacyCssState, unavailable)]), signal)) failed("resource_unavailable", signal);
|
|
11906
|
+
return false;
|
|
11907
|
+
}
|
|
11908
|
+
if (response.status === 304) {
|
|
11909
|
+
if (!signal.aborted) events?.setEnabled(true);
|
|
11910
|
+
return true;
|
|
11911
|
+
}
|
|
11912
|
+
if (!response.ok) {
|
|
11913
|
+
unavailable(response, signal, "manifest_unavailable");
|
|
11914
|
+
return false;
|
|
11670
11915
|
}
|
|
11671
|
-
if (response.status === 304) return true;
|
|
11672
|
-
if (!response.ok) return false;
|
|
11673
11916
|
const nextManifestEtag = response.headers.get("etag") ?? "";
|
|
11674
|
-
const
|
|
11675
|
-
|
|
11917
|
+
const manifestSource = await readMobileAssetText(response, signal, void 0);
|
|
11918
|
+
let payload;
|
|
11919
|
+
try {
|
|
11920
|
+
payload = parseMobileExtensionManifest(JSON.parse(manifestSource));
|
|
11921
|
+
} catch {
|
|
11922
|
+
failed("manifest_invalid", signal);
|
|
11923
|
+
return false;
|
|
11924
|
+
}
|
|
11925
|
+
if (refreshAborted(signal)) return false;
|
|
11926
|
+
if (payload === void 0) {
|
|
11927
|
+
failed("manifest_invalid", signal);
|
|
11928
|
+
return false;
|
|
11929
|
+
}
|
|
11930
|
+
events?.setEnabled(true);
|
|
11676
11931
|
const entries = payload.extensions;
|
|
11677
11932
|
const seen = new Set(entries.map((entry) => entry.id));
|
|
11678
11933
|
const scriptRevision = payload.legacy.scriptRevision;
|
|
@@ -11683,8 +11938,11 @@ window.__ModuleLoader__.load({
|
|
|
11683
11938
|
else refreshComplete = false;
|
|
11684
11939
|
}
|
|
11685
11940
|
if (styleRevision === "" || styleRevision !== legacyStyleRevision) {
|
|
11686
|
-
if (await refreshCssLegacy(legacyStyle, signal, legacyCssState)) legacyStyleRevision = styleRevision;
|
|
11687
|
-
else
|
|
11941
|
+
if (await refreshCssLegacy(legacyStyle, signal, legacyCssState, unavailable)) legacyStyleRevision = styleRevision;
|
|
11942
|
+
else {
|
|
11943
|
+
failed("resource_unavailable", signal);
|
|
11944
|
+
refreshComplete = false;
|
|
11945
|
+
}
|
|
11688
11946
|
}
|
|
11689
11947
|
const commitStyle = (id, change, css, etag) => {
|
|
11690
11948
|
if (change === "retain") return;
|
|
@@ -11726,9 +11984,12 @@ window.__ModuleLoader__.load({
|
|
|
11726
11984
|
signal
|
|
11727
11985
|
});
|
|
11728
11986
|
if (cssResponse.status !== 304) {
|
|
11729
|
-
if (!cssResponse.ok)
|
|
11987
|
+
if (!cssResponse.ok) {
|
|
11988
|
+
unavailable(cssResponse, signal, "resource_unavailable");
|
|
11989
|
+
throw new Error("mobile extension style failed to load");
|
|
11990
|
+
}
|
|
11730
11991
|
pendingStyleEtag = cssResponse.headers.get("etag") ?? void 0;
|
|
11731
|
-
pendingCss = await cssResponse.
|
|
11992
|
+
pendingCss = await readMobileAssetText(cssResponse, signal, EXTENSION_LIMITS.css);
|
|
11732
11993
|
styleChange = "replace";
|
|
11733
11994
|
if (refreshAborted(signal)) return false;
|
|
11734
11995
|
}
|
|
@@ -11755,9 +12016,12 @@ window.__ModuleLoader__.load({
|
|
|
11755
12016
|
let nextDigest;
|
|
11756
12017
|
let nextEtag;
|
|
11757
12018
|
if (scriptResponse.status !== 304) {
|
|
11758
|
-
if (!scriptResponse.ok)
|
|
12019
|
+
if (!scriptResponse.ok) {
|
|
12020
|
+
unavailable(scriptResponse, signal, "resource_unavailable");
|
|
12021
|
+
throw new Error("mobile extension script failed to load");
|
|
12022
|
+
}
|
|
11759
12023
|
nextEtag = scriptResponse.headers.get("etag") ?? void 0;
|
|
11760
|
-
const source = await scriptResponse.
|
|
12024
|
+
const source = await readMobileAssetText(scriptResponse, signal, EXTENSION_LIMITS.script);
|
|
11761
12025
|
if (refreshAborted(signal)) return false;
|
|
11762
12026
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(source));
|
|
11763
12027
|
if (refreshAborted(signal)) return false;
|
|
@@ -11811,6 +12075,7 @@ window.__ModuleLoader__.load({
|
|
|
11811
12075
|
failClosedExtensionGenerationReplacement(hadActiveGeneration, previousHostGeneration, entry.generation, () => {
|
|
11812
12076
|
disposeManifestExtension(entry.id);
|
|
11813
12077
|
});
|
|
12078
|
+
failed("resource_unavailable", signal);
|
|
11814
12079
|
refreshComplete = false;
|
|
11815
12080
|
}
|
|
11816
12081
|
}
|
|
@@ -11821,13 +12086,17 @@ window.__ModuleLoader__.load({
|
|
|
11821
12086
|
} else manifestEtag = "";
|
|
11822
12087
|
return refreshComplete;
|
|
11823
12088
|
} catch {
|
|
12089
|
+
failed("refresh_failed", signal);
|
|
11824
12090
|
return false;
|
|
11825
12091
|
}
|
|
11826
12092
|
};
|
|
11827
12093
|
started = true;
|
|
11828
12094
|
for (const definition of definitions.values()) activateDefinition(definition);
|
|
11829
12095
|
const stopRefresh = startLifecycleRefreshScheduler(async (signal) => {
|
|
11830
|
-
await refreshExtensions(signal)
|
|
12096
|
+
if (await refreshExtensions(signal) && !disposed && !signal.aborted) {
|
|
12097
|
+
reporter.recover(signal);
|
|
12098
|
+
events?.setEnabled(true);
|
|
12099
|
+
}
|
|
11831
12100
|
});
|
|
11832
12101
|
const stopEvents = startExtensionChangeStream(() => {
|
|
11833
12102
|
stopRefresh.refresh();
|
|
@@ -11845,9 +12114,11 @@ window.__ModuleLoader__.load({
|
|
|
11845
12114
|
});
|
|
11846
12115
|
}, () => {
|
|
11847
12116
|
fireDeviceRevoked();
|
|
11848
|
-
});
|
|
12117
|
+
}, false);
|
|
12118
|
+
events = stopEvents;
|
|
11849
12119
|
return () => {
|
|
11850
12120
|
disposed = true;
|
|
12121
|
+
reporter.dispose();
|
|
11851
12122
|
stopNativeBack();
|
|
11852
12123
|
stopEvents();
|
|
11853
12124
|
stopRefresh();
|
|
@@ -11866,7 +12137,7 @@ window.__ModuleLoader__.load({
|
|
|
11866
12137
|
else window.dshMobile = previous;
|
|
11867
12138
|
};
|
|
11868
12139
|
}
|
|
11869
|
-
async function refreshCssLegacy(style, signal, state) {
|
|
12140
|
+
async function refreshCssLegacy(style, signal, state, unavailable) {
|
|
11870
12141
|
try {
|
|
11871
12142
|
const headers = {};
|
|
11872
12143
|
if (state.etag !== "") headers["if-none-match"] = state.etag;
|
|
@@ -11890,13 +12161,14 @@ window.__ModuleLoader__.load({
|
|
|
11890
12161
|
if (response.ok) {
|
|
11891
12162
|
const nextEtag = response.headers.get("etag") ?? "";
|
|
11892
12163
|
const nextModified = response.headers.get("last-modified") ?? "";
|
|
11893
|
-
const css = await response
|
|
12164
|
+
const css = await readMobileAssetText(response, signal, LEGACY_CUSTOM_ASSET_LIMIT);
|
|
11894
12165
|
if (refreshAborted(signal)) return false;
|
|
11895
12166
|
style.textContent = css;
|
|
11896
12167
|
state.etag = nextEtag;
|
|
11897
12168
|
state.modified = nextModified;
|
|
11898
12169
|
return true;
|
|
11899
12170
|
}
|
|
12171
|
+
unavailable(response, signal, "resource_unavailable");
|
|
11900
12172
|
return false;
|
|
11901
12173
|
} catch {
|
|
11902
12174
|
return false;
|
|
@@ -11950,11 +12222,13 @@ window.__ModuleLoader__.load({
|
|
|
11950
12222
|
}, "dsh-mobile: authenticated gateway client trust");
|
|
11951
12223
|
ctx.effect(() => {
|
|
11952
12224
|
const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__, location.protocol);
|
|
12225
|
+
const gatewayCustomization = ownsMobileCustomization(document.querySelector("meta[name=\"dsh-mobile-gateway\"][content=\"authenticated\"]") !== null, window.__DSH_MOBILE_FRONTEND__);
|
|
11953
12226
|
const style = element("style");
|
|
11954
12227
|
style.dataset.plugin = "dsh-mobile";
|
|
11955
12228
|
style.textContent = desktopAdmin ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
|
|
11956
12229
|
style.textContent += CLIENT_MODULE_STYLES;
|
|
11957
12230
|
document.head.append(style);
|
|
12231
|
+
const removeCustom = !desktopAdmin || gatewayCustomization ? installCustomAssets() : () => void 0;
|
|
11958
12232
|
const removeVoiceSession = installVoiceSession(!desktopAdmin);
|
|
11959
12233
|
const removeModuleSettings = ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
11960
12234
|
name: "settings.general.item",
|
|
@@ -11998,7 +12272,6 @@ window.__ModuleLoader__.load({
|
|
|
11998
12272
|
removeFontSize();
|
|
11999
12273
|
};
|
|
12000
12274
|
});
|
|
12001
|
-
const removeCustom = installCustomAssets();
|
|
12002
12275
|
const removeSurface = installDshLanguageBoundSurface(() => installNativeMobileSurface({
|
|
12003
12276
|
getLayout: () => ctx.get("layout"),
|
|
12004
12277
|
getSidebarRight: () => ctx.get("sidebarRight")
|
|
@@ -12058,6 +12331,7 @@ window.__ModuleLoader__.load({
|
|
|
12058
12331
|
disposeClientModuleDialogs();
|
|
12059
12332
|
removeModuleSettings();
|
|
12060
12333
|
removeControl();
|
|
12334
|
+
removeCustom();
|
|
12061
12335
|
removeVoiceSession();
|
|
12062
12336
|
style.remove();
|
|
12063
12337
|
};
|
|
@@ -12091,6 +12365,7 @@ window.__ModuleLoader__.load({
|
|
|
12091
12365
|
exports.frpAttachFormErrorCode = frpAttachFormErrorCode;
|
|
12092
12366
|
exports.handleMissingExtensionManifest = handleMissingExtensionManifest;
|
|
12093
12367
|
exports.inject = inject;
|
|
12368
|
+
exports.installCustomAssets = installCustomAssets;
|
|
12094
12369
|
exports.installDshLanguageBoundSurface = installDshLanguageBoundSurface;
|
|
12095
12370
|
exports.installMobileNativeBack = installMobileNativeBack;
|
|
12096
12371
|
exports.mobileBackBlockedMessage = mobileBackBlockedMessage;
|