@@ -40,8 +40,8 @@ export const deferred = <T = void>(): Deferred<T> => {
4040} ;
4141
4242export function isFirefox ( ) {
43- //@ts -ignore
44- return typeof mozInnerScreenX !== "undefined" ;
43+ // @ts -ignore. For both Page & Worker
44+ return typeof mozInnerScreenX !== "undefined" || typeof navigator . mozGetUserMedia === "function" ;
4545}
4646
4747export function InfoNotification ( title : string , msg : string ) {
@@ -256,17 +256,25 @@ export function getBrowserVersion(): number {
256256
257257// 判断是否为Edge浏览器
258258export function isEdge ( ) : boolean {
259- return navigator . userAgent . includes ( "Edg/" ) ;
259+ return (
260+ // @ts -ignore; For Extension (Page/Worker), we can check UserSubscriptionState (hidden feature in Edge)
261+ typeof chrome . runtime . UserSubscriptionState === "object" ||
262+ // Fallback to userAgent check
263+ navigator . userAgent . includes ( "Edg/" )
264+ ) ;
260265}
261266
262- export enum BrowserType {
263- Edge = 2 ,
264- Chrome = 1 ,
265- chromeA = 4 , // ~ 120
266- chromeB = 8 , // 121 ~ 137
267- chromeC = 16 , // 138 ~
268- edgeA = 32 , // Edge 144~
269- }
267+ export const BrowserType = {
268+ Edge : 2 ,
269+ Chrome : 1 ,
270+ noUserScriptsAPI : 64 ,
271+ guardedByDeveloperMode : 128 ,
272+ guardedByAllowScript : 256 ,
273+ Mouse : 1 , // Desktop, Laptop. Tablet ??
274+ Touch : 2 , // Touchscreen Laptop, Mobile, Tablet
275+ } as const ;
276+
277+ export type BrowserType = ValueOf < typeof BrowserType > ;
270278
271279export function getBrowserType ( ) {
272280 const o = {
@@ -275,35 +283,77 @@ export function getBrowserType() {
275283 chrome : 0 , // Chrome, Chromium, Brave, Edge
276284 unknown : 0 ,
277285 chromeVersion : 0 ,
286+ device : 0 ,
278287 } ;
279288 if ( isFirefox ( ) ) {
289+ // Firefox, Zen
280290 o . firefox = 1 ;
281291 } else {
282292 //@ts -ignore
283293 const isWebkitBased = typeof webkitIndexedDB === "object" ;
284294 if ( isWebkitBased ) {
295+ // Safari, Orion
285296 o . webkit = 1 ;
286297 } else {
287- //@ts -ignore
288- const isChromeBased = typeof webkitRequestAnimationFrame === "function" ;
298+ const isChromeBased =
299+ typeof requestAnimationFrame === "function"
300+ ? // @ts -ignore. For Page only
301+ typeof webkitRequestAnimationFrame === "function"
302+ : // @ts -ignore. Available in Worker (Chrome 74+ Edge 79+)
303+ typeof BackgroundFetchRecord === "function" ;
289304 if ( isChromeBased ) {
290305 const isEdgeBrowser = isEdge ( ) ;
291306 const chromeVersion = getBrowserVersion ( ) ;
292307 o . chrome |= isEdgeBrowser ? BrowserType . Edge : BrowserType . Chrome ;
293- o . chrome |= chromeVersion < 120 ? BrowserType . chromeA : 0 ; // Chrome 120 以下
294- o . chrome |= chromeVersion < 138 ? BrowserType . chromeB : BrowserType . chromeC ; // Chrome 121 ~ 137 / 138 以上
295- if ( isEdgeBrowser ) {
296- o . chrome |= chromeVersion >= 144 ? BrowserType . edgeA : 0 ; // Edge 144 以上
308+ // 由小至大
309+ if ( chromeVersion < 120 ) {
310+ o . chrome |= BrowserType . noUserScriptsAPI ;
311+ } else {
312+ // 120+
313+ if ( isEdgeBrowser ? chromeVersion < 144 : chromeVersion < 138 ) {
314+ o . chrome |= BrowserType . guardedByDeveloperMode ;
315+ } else {
316+ // Edge 144+ / Chrome 138+
317+ o . chrome |= BrowserType . guardedByAllowScript ;
318+ // 如日后再变化,在这里再加条件式
319+ }
297320 }
298321 o . chromeVersion = chromeVersion ;
299322 } else {
300323 o . unknown = 1 ;
301324 }
302325 }
303326 }
327+ // BrowserType.Mouse 未能在 Worker 使用
328+ o . device |= typeof matchMedia === "function" && ! matchMedia ( "(hover: none)" ) . matches ? BrowserType . Mouse : 0 ;
329+ o . device |= navigator . maxTouchPoints > 0 ? BrowserType . Touch : 0 ;
304330 return o ;
305331}
306332
333+ export const isPermissionOk = async (
334+ manifestPermission : chrome . runtime . ManifestOptionalPermissions & chrome . runtime . ManifestPermissions
335+ ) : Promise < boolean | null > => {
336+ // 兼容 Firefox - 避免因为检查 permission 时,该permission不存在于 optional permission 而报错
337+ const manifest = chrome . runtime . getManifest ( ) ;
338+ if ( manifest . optional_permissions ?. includes ( manifestPermission ) ) {
339+ try {
340+ return await chrome . permissions . contains ( { permissions : [ manifestPermission ] } ) ;
341+ } catch {
342+ // ignored
343+ }
344+ } else if ( manifest . permissions ?. includes ( manifestPermission ) ) {
345+ // mainfest 而列明有该permission, 不用检查
346+ return true ;
347+ }
348+ return null ;
349+ } ;
350+
351+ export const getBrowserInstalledVersion = ( ) => {
352+ // unique for each browser update.
353+ // Usage: Detect whether the browser is upgraded.
354+ return btoa ( [ ...navigator . userAgent . matchAll ( / [ \d . _ ] + / g) ] . map ( ( e ) => e [ 0 ] ) . join ( ";" ) ) ;
355+ } ;
356+
307357export const makeBlobURL = < T extends { blob : Blob ; persistence : boolean } > (
308358 params : T ,
309359 fallbackFn ?: ( params : T ) => string | Promise < string >
@@ -513,7 +563,7 @@ export const normalizeResponseHeaders = (headersString: string) => {
513563// 遵循 ISO 8601, 一月四日为Week 1,星期一为新一周
514564// 能应对每年开始和结束(不会因为踏入新一年而重新计算)
515565// 见 https://wikipedia.org/wiki/ISO_week_date
516- // 中文說明 https://juejin.cn/post/6921245139855736846
566+ // 中文说明 https://juejin.cn/post/6921245139855736846
517567export const getISOWeek = ( date : Date ) : number => {
518568 // 使用传入日期的年月日创建 UTC 日期对象,忽略本地时间部分,避免时区影响
519569 const d = new Date ( Date . UTC ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) ) ) ;
0 commit comments