FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
FluentRead/userscript/browser.ts at main · FluentRead/FluentRead · GitHub
FluentRead
/
FluentRead
Public
Notifications
You must be signed in to change notification settings
Fork
423
Star
8.2k
Code
Issues
11
Pull requests
2
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
FluentRead
/
userscript
/
browser.ts
Copy path
More file actions
More file actions
Latest commit
History
History
History
150 lines (137 loc) · 6.07 KB
Breadcrumbs
FluentRead
/
userscript
/
browser.ts
Copy path
File metadata and controls
150 lines (137 loc) · 6.07 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import
{
getStoredValue
,
listStoredKeys
,
setStoredValue
,
storage
as
userscriptStorage
}
from
'./storage'
;
type
RuntimeListener
=
(
message
:
any
,
sender
:
any
,
sendResponse
:
(
response
?:
any
)
=>
void
,
)
=>
unknown
;
type
PlatformMessageHandler
=
(
message
:
any
)
=>
Promise
<
any
>
;
export
const
UNHANDLED_RUNTIME_MESSAGE
=
Symbol
(
'unhandled-runtime-message'
)
;
const
runtimeListeners
=
new
Set
<
RuntimeListener
>
(
)
;
const
defaultPlatformMessageHandler
:
PlatformMessageHandler
=
async
(
)
=>
UNHANDLED_RUNTIME_MESSAGE
;
let
platformMessageHandler
:
PlatformMessageHandler
=
defaultPlatformMessageHandler
;
type
StorageChange
=
{
oldValue
?:
unknown
;
newValue
?:
unknown
}
;
type
StorageChangedListener
=
(
changes
:
Record
<
string
,
StorageChange
>
,
areaName
:
string
)
=>
void
;
const
storageChangedListeners
=
new
Set
<
StorageChangedListener
>
(
)
;
const
storageKeyWatchers
=
new
Set
<
string
>
(
)
;
function
watchStorageKey
(
key
:
string
)
:
void
{
if
(
storageKeyWatchers
.
has
(
key
)
)
return
;
storageKeyWatchers
.
add
(
key
)
;
userscriptStorage
.
watch
(
key
,
(
nextValue
,
previousValue
)
=>
{
const
changes
=
{
[
key
]
:
{
oldValue
:
previousValue
,
newValue
:
nextValue
}
}
;
storageChangedListeners
.
forEach
(
(
listener
)
=>
listener
(
changes
,
'local'
)
)
;
}
)
;
}
const
storageOnChanged
=
{
addListener
(
listener
:
StorageChangedListener
)
:
void
{
storageChangedListeners
.
add
(
listener
)
;
}
,
removeListener
(
listener
:
StorageChangedListener
)
:
void
{
storageChangedListeners
.
delete
(
listener
)
;
}
,
hasListener
(
listener
:
StorageChangedListener
)
:
boolean
{
return
storageChangedListeners
.
has
(
listener
)
;
}
,
}
;
export
function
setPlatformMessageHandler
(
handler
:
PlatformMessageHandler
)
:
void
{
platformMessageHandler
=
handler
;
}
/** 初始化失败或页面离开时恢复空适配器,防止后续重注入继续调用旧页面闭包。 */
export
function
resetPlatformMessageHandler
(
)
:
void
{
platformMessageHandler
=
defaultPlatformMessageHandler
;
}
/**
* 在同一页面内模拟 webextension runtime 消息分派,兼容同步 sendResponse、Promise
* 返回值以及返回 true 后异步应答三种监听器形式。
*/
export
async
function
dispatchContentMessage
(
message
:
any
)
:
Promise
<
any
>
{
for
(
const
listener
of
runtimeListeners
)
{
let
didRespond
=
false
;
let
responseValue
:
any
;
const
sendResponse
=
(
value
?:
any
)
=>
{
didRespond
=
true
;
responseValue
=
value
;
}
;
const
returned
=
listener
(
message
,
{
tab
:
{
id
:
1
,
windowId
:
1
}
,
frameId
:
0
}
,
sendResponse
)
;
if
(
returned
&&
typeof
(
returned
as
PromiseLike
<
unknown
>
)
.
then
===
'function'
)
{
const
value
=
await
returned
;
if
(
value
!==
undefined
)
return
value
;
}
if
(
didRespond
)
return
responseValue
;
if
(
returned
===
true
)
{
await
new
Promise
(
(
resolve
)
=>
setTimeout
(
resolve
,
0
)
)
;
if
(
didRespond
)
return
responseValue
;
}
}
return
undefined
;
}
function
runtimeAssetUrl
(
path
:
string
)
:
string
{
if
(
/
i
c
o
n
\/
(?:
3
2
|
4
8
|
6
4
|
1
2
8
|
2
5
6
|
5
1
2
)
\.
p
n
g
$
/
u
.
test
(
path
)
)
{
return
globalThis
.
__FLUENTREAD_ICON_DATA__
||
''
;
}
return
''
;
}
const
runtime
=
{
async
sendMessage
(
message
:
any
)
:
Promise
<
any
>
{
// 先交给 userscript 的“后台”适配器;未处理消息再回送内容侧监听器。
const
result
=
await
platformMessageHandler
(
message
)
;
if
(
result
!==
UNHANDLED_RUNTIME_MESSAGE
)
return
result
;
return
dispatchContentMessage
(
message
)
;
}
,
getURL
:
runtimeAssetUrl
,
async
openOptionsPage
(
)
:
Promise
<
void
>
{
window
.
dispatchEvent
(
new
CustomEvent
(
'fluentread-userscript-open-settings'
)
)
;
}
,
onMessage
:
{
addListener
(
listener
:
RuntimeListener
)
:
void
{
runtimeListeners
.
add
(
listener
)
;
}
,
removeListener
(
listener
:
RuntimeListener
)
:
void
{
runtimeListeners
.
delete
(
listener
)
;
}
,
hasListener
(
listener
:
RuntimeListener
)
:
boolean
{
return
runtimeListeners
.
has
(
listener
)
;
}
,
}
,
}
;
const
browser
=
{
runtime
,
tabs
:
{
async
query
(
)
:
Promise
<
Array
<
{
id
:
number
;
windowId
:
number
;
active
:
boolean
}
>
>
{
return
[
{
id
:
1
,
windowId
:
1
,
active
:
true
}
]
;
}
,
async
sendMessage
(
_tabId
:
number
,
message
:
any
)
:
Promise
<
any
>
{
return
dispatchContentMessage
(
message
)
;
}
,
async
create
(
{
url
}
:
{
url
?:
string
}
)
:
Promise
<
void
>
{
if
(
url
)
window
.
open
(
url
,
'_blank'
,
'noopener,noreferrer'
)
;
}
,
}
,
storage
:
{
local
:
{
// webextension storage.local 在 userscript 中由 GM 存储承接,并维持相同的批量键形态。
async
get
(
keys
?:
string
|
string
[
]
|
Record
<
string
,
unknown
>
)
:
Promise
<
Record
<
string
,
unknown
>
>
{
if
(
typeof
keys
===
'string'
)
{
watchStorageKey
(
keys
)
;
return
{
[
keys
]
:
await
getStoredValue
(
keys
)
}
;
}
if
(
Array
.
isArray
(
keys
)
)
{
keys
.
forEach
(
watchStorageKey
)
;
return
Object
.
fromEntries
(
await
Promise
.
all
(
keys
.
map
(
async
(
key
)
=>
[
key
,
await
getStoredValue
(
key
)
]
)
)
)
;
}
if
(
keys
&&
typeof
keys
===
'object'
)
{
Object
.
keys
(
keys
)
.
forEach
(
watchStorageKey
)
;
return
Object
.
fromEntries
(
await
Promise
.
all
(
Object
.
entries
(
keys
)
.
map
(
async
(
[
key
,
fallback
]
)
=>
{
const
value
=
await
getStoredValue
(
key
)
;
return
[
key
,
value
??
fallback
]
;
}
)
)
)
;
}
const
names
=
await
listStoredKeys
(
)
;
return
Object
.
fromEntries
(
await
Promise
.
all
(
names
.
map
(
async
(
key
)
=>
[
key
,
await
getStoredValue
(
key
)
]
)
)
)
;
}
,
async
set
(
values
:
Record
<
string
,
unknown
>
)
:
Promise
<
void
>
{
Object
.
keys
(
values
)
.
forEach
(
watchStorageKey
)
;
await
Promise
.
all
(
Object
.
entries
(
values
)
.
map
(
(
[
key
,
value
]
)
=>
setStoredValue
(
key
,
value
)
)
)
;
}
,
}
,
onChanged
:
storageOnChanged
,
}
,
}
;
export
const
chrome
=
{
runtime
,
storage
:
browser
.
storage
,
}
;
export
default
browser
;
Back
|
FazBrowse Home
|
New Git URL