@@ -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 valueType ( val : unknown ) {
@@ -251,17 +251,25 @@ export function getBrowserVersion(): number {
251251
252252// 判断是否为Edge浏览器
253253export function isEdge ( ) : boolean {
254- return navigator . userAgent . includes ( "Edg/" ) ;
254+ return (
255+ // @ts -ignore; For Extension (Page/Worker), we can check UserSubscriptionState (hidden feature in Edge)
256+ typeof chrome . runtime . UserSubscriptionState === "object" ||
257+ // Fallback to userAgent check
258+ navigator . userAgent . includes ( "Edg/" )
259+ ) ;
255260}
256261
257- export enum BrowserType {
258- Edge = 2 ,
259- Chrome = 1 ,
260- chromeA = 4 , // ~ 120
261- chromeB = 8 , // 121 ~ 137
262- chromeC = 16 , // 138 ~
263- edgeA = 32 , // Edge 144~
264- }
262+ export const BrowserType = {
263+ Edge : 2 ,
264+ Chrome : 1 ,
265+ noUserScriptsAPI : 64 ,
266+ guardedByDeveloperMode : 128 ,
267+ guardedByAllowScript : 256 ,
268+ Mouse : 1 , // Desktop, Laptop. Tablet ??
269+ Touch : 2 , // Touchscreen Laptop, Mobile, Tablet
270+ } as const ;
271+
272+ export type BrowserType = ValueOf < typeof BrowserType > ;
265273
266274export function getBrowserType ( ) {
267275 const o = {
@@ -270,35 +278,77 @@ export function getBrowserType() {
270278 chrome : 0 , // Chrome, Chromium, Brave, Edge
271279 unknown : 0 ,
272280 chromeVersion : 0 ,
281+ device : 0 ,
273282 } ;
274283 if ( isFirefox ( ) ) {
284+ // Firefox, Zen
275285 o . firefox = 1 ;
276286 } else {
277287 //@ts -ignore
278288 const isWebkitBased = typeof webkitIndexedDB === "object" ;
279289 if ( isWebkitBased ) {
290+ // Safari, Orion
280291 o . webkit = 1 ;
281292 } else {
282- //@ts -ignore
283- const isChromeBased = typeof webkitRequestAnimationFrame === "function" ;
293+ const isChromeBased =
294+ typeof requestAnimationFrame === "function"
295+ ? // @ts -ignore. For Page only
296+ typeof webkitRequestAnimationFrame === "function"
297+ : // @ts -ignore. Available in Worker (Chrome 74+ Edge 79+)
298+ typeof BackgroundFetchRecord === "function" ;
284299 if ( isChromeBased ) {
285300 const isEdgeBrowser = isEdge ( ) ;
286301 const chromeVersion = getBrowserVersion ( ) ;
287302 o . chrome |= isEdgeBrowser ? BrowserType . Edge : BrowserType . Chrome ;
288- o . chrome |= chromeVersion < 120 ? BrowserType . chromeA : 0 ; // Chrome 120 以下
289- o . chrome |= chromeVersion < 138 ? BrowserType . chromeB : BrowserType . chromeC ; // Chrome 121 ~ 137 / 138 以上
290- if ( isEdgeBrowser ) {
291- o . chrome |= chromeVersion >= 144 ? BrowserType . edgeA : 0 ; // Edge 144 以上
303+ // 由小至大
304+ if ( chromeVersion < 120 ) {
305+ o . chrome |= BrowserType . noUserScriptsAPI ;
306+ } else {
307+ // 120+
308+ if ( isEdgeBrowser ? chromeVersion < 144 : chromeVersion < 138 ) {
309+ o . chrome |= BrowserType . guardedByDeveloperMode ;
310+ } else {
311+ // Edge 144+ / Chrome 138+
312+ o . chrome |= BrowserType . guardedByAllowScript ;
313+ // 如日后再变化,在这里再加条件式
314+ }
292315 }
293316 o . chromeVersion = chromeVersion ;
294317 } else {
295318 o . unknown = 1 ;
296319 }
297320 }
298321 }
322+ // BrowserType.Mouse 未能在 Worker 使用
323+ o . device |= typeof matchMedia === "function" && ! matchMedia ( "(hover: none)" ) . matches ? BrowserType . Mouse : 0 ;
324+ o . device |= navigator . maxTouchPoints > 0 ? BrowserType . Touch : 0 ;
299325 return o ;
300326}
301327
328+ export const isPermissionOk = async (
329+ manifestPermission : chrome . runtime . ManifestOptionalPermissions & chrome . runtime . ManifestPermissions
330+ ) : Promise < boolean | null > => {
331+ // 兼容 Firefox - 避免因为检查 permission 时,该permission不存在于 optional permission 而报错
332+ const manifest = chrome . runtime . getManifest ( ) ;
333+ if ( manifest . optional_permissions ?. includes ( manifestPermission ) ) {
334+ try {
335+ return await chrome . permissions . contains ( { permissions : [ manifestPermission ] } ) ;
336+ } catch {
337+ // ignored
338+ }
339+ } else if ( manifest . permissions ?. includes ( manifestPermission ) ) {
340+ // mainfest 而列明有该permission, 不用检查
341+ return true ;
342+ }
343+ return null ;
344+ } ;
345+
346+ export const getBrowserInstalledVersion = ( ) => {
347+ // unique for each browser update.
348+ // Usage: Detect whether the browser is upgraded.
349+ return btoa ( [ ...navigator . userAgent . matchAll ( / [ \d . _ ] + / g) ] . map ( ( e ) => e [ 0 ] ) . join ( ";" ) ) ;
350+ } ;
351+
302352export const makeBlobURL = < T extends { blob : Blob ; persistence : boolean } > (
303353 params : T ,
304354 fallbackFn ?: ( params : T ) => string | Promise < string >
@@ -474,7 +524,7 @@ export const normalizeResponseHeaders = (headersString: string) => {
474524// 遵循 ISO 8601, 一月四日为Week 1,星期一为新一周
475525// 能应对每年开始和结束(不会因为踏入新一年而重新计算)
476526// 见 https://wikipedia.org/wiki/ISO_week_date
477- // 中文說明 https://juejin.cn/post/6921245139855736846
527+ // 中文说明 https://juejin.cn/post/6921245139855736846
478528export const getISOWeek = ( date : Date ) : number => {
479529 // 使用传入日期的年月日创建 UTC 日期对象,忽略本地时间部分,避免时区影响
480530 const d = new Date ( Date . UTC ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) ) ) ;
0 commit comments