crx-rpc 1.0.7 → 2.0.0
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 +199 -37
- package/README.zh-CN.md +301 -45
- package/dist/adapter/background.d.ts +34 -0
- package/dist/adapter/background.js +323 -0
- package/dist/adapter/content.d.ts +21 -0
- package/dist/adapter/content.js +165 -0
- package/dist/adapter/index.d.ts +4 -0
- package/dist/adapter/index.js +4 -0
- package/dist/adapter/tab.d.ts +15 -0
- package/dist/adapter/tab.js +42 -0
- package/dist/adapter/web.d.ts +10 -0
- package/dist/adapter/web.js +25 -0
- package/dist/background.d.ts +6 -0
- package/dist/background.js +89 -63
- package/dist/content.d.ts +7 -0
- package/dist/content.js +121 -11
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -4
- package/dist/tab.d.ts +15 -0
- package/dist/tab.js +42 -0
- package/package.json +4 -1
- package/packages/webext-core-messaging/index.d.ts +16 -0
- package/packages/webext-core-messaging/index.js +180 -0
- package/packages/webext-core-messaging/package.json +7 -0
- package/src/{background.ts → adapter/background.ts} +58 -52
- package/src/adapter/content.ts +244 -0
- package/src/adapter/index.ts +4 -0
- package/src/adapter/tab.ts +49 -0
- package/src/{web.ts → adapter/web.ts} +3 -3
- package/src/index.ts +1 -4
- package/src/content.ts +0 -71
package/README.md
CHANGED
|
@@ -157,21 +157,86 @@ async function calculate() {
|
|
|
157
157
|
|
|
158
158
|
## Architecture
|
|
159
159
|
|
|
160
|
-
###
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
160
|
+
### Complete Communication Topology
|
|
161
|
+
|
|
162
|
+
```mermaid
|
|
163
|
+
graph TB
|
|
164
|
+
subgraph WebPage["Web Page Context"]
|
|
165
|
+
WC[WebRPCClient]
|
|
166
|
+
WO[WebObservable]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
subgraph ContentScript["Content Script Context"]
|
|
170
|
+
CR[ContentRPC<br/>Bridge Mode]
|
|
171
|
+
CC[ContentRPCClient<br/>Direct Mode]
|
|
172
|
+
CO[ContentObservable]
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
subgraph Background["Background Script Context"]
|
|
176
|
+
BR[BackgroundRPC]
|
|
177
|
+
MS[MathService]
|
|
178
|
+
US[UserService]
|
|
179
|
+
RS[RemoteSubject]
|
|
180
|
+
RSM[RemoteSubjectManager]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
subgraph ExtPage["Extension Page Context<br/>(Popup/Options/Sidepanel)"]
|
|
184
|
+
EC[ExtPageRPCClient]
|
|
185
|
+
EO[ExtPageObservable]
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
subgraph TabContext["Tab-specific Access"]
|
|
189
|
+
TC[TabRPCClient]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
%% RPC Calls
|
|
193
|
+
WC -->|"CustomEvent<br/>.add(1,2)"| CR
|
|
194
|
+
CR -->|"chrome.runtime<br/>Forward"| BR
|
|
195
|
+
CC -->|"chrome.runtime<br/>.multiply(2,3)"| BR
|
|
196
|
+
EC -->|"chrome.runtime<br/>.divide(10,2)"| BR
|
|
197
|
+
TC -->|"chrome.tabs<br/>Access Content Service"| CC
|
|
198
|
+
|
|
199
|
+
BR -->|Response| CR
|
|
200
|
+
CR -->|CustomEvent| WC
|
|
201
|
+
BR -->|Response| CC
|
|
202
|
+
BR -->|Response| EC
|
|
203
|
+
|
|
204
|
+
BR -.->|Manages| MS
|
|
205
|
+
BR -.->|Manages| US
|
|
206
|
+
|
|
207
|
+
%% Observable Streams
|
|
208
|
+
WO -.->|Subscribe| CR
|
|
209
|
+
CR -.->|Forward| RSM
|
|
210
|
+
CO -.->|Subscribe| RSM
|
|
211
|
+
EO -.->|Subscribe| RSM
|
|
212
|
+
RSM -.->|Broadcast| RS
|
|
213
|
+
RS -.->|Updates| CR
|
|
214
|
+
CR -.->|Updates| WO
|
|
215
|
+
RS -.->|Updates| CO
|
|
216
|
+
RS -.->|Updates| EO
|
|
217
|
+
|
|
218
|
+
style WC fill:#e1f5ff
|
|
219
|
+
style CC fill:#e1f5ff
|
|
220
|
+
style EC fill:#e1f5ff
|
|
221
|
+
style TC fill:#e1f5ff
|
|
222
|
+
style BR fill:#fff4e6
|
|
223
|
+
style MS fill:#f0f0f0
|
|
224
|
+
style US fill:#f0f0f0
|
|
225
|
+
style RS fill:#ffe6f0
|
|
226
|
+
style RSM fill:#ffe6f0
|
|
227
|
+
style CR fill:#e8f5e9
|
|
173
228
|
```
|
|
174
229
|
|
|
230
|
+
### Communication Paths
|
|
231
|
+
|
|
232
|
+
| Path | Method | Description |
|
|
233
|
+
|------|--------|-------------|
|
|
234
|
+
| **Web Page → Background** | CustomEvent + chrome.runtime | Through ContentRPC bridge |
|
|
235
|
+
| **Content Script → Background** | chrome.runtime | Direct communication |
|
|
236
|
+
| **Extension Page → Background** | chrome.runtime | Direct communication |
|
|
237
|
+
| **Extension Page → Content Script** | chrome.tabs + TabRPCClient | Tab-specific access |
|
|
238
|
+
| **Background → All Contexts** | RemoteSubject broadcast | Real-time data streaming |
|
|
239
|
+
|
|
175
240
|
### Key Components
|
|
176
241
|
|
|
177
242
|
- **WebRPCClient**: Client for web pages using window events
|
|
@@ -201,31 +266,12 @@ const rpc = new BackgroundRPC(true); // Enable logging
|
|
|
201
266
|
|
|
202
267
|
### Log Output
|
|
203
268
|
|
|
204
|
-
When logging is enabled, the following information is logged
|
|
205
|
-
|
|
206
|
-
- **Function Calls**: `[RPC] Call: Service.Method [ID]` with colored components
|
|
207
|
-
- **Success Responses**: `[RPC] Success: Service.Method [ID]` with green success indicator
|
|
208
|
-
- **Error Responses**: `[RPC] Error: Service.Method [ID]` with red error indicator
|
|
209
|
-
- **Unknown Services/Methods**: `[RPC] Unknown service/method: Service.Method [ID]` with orange warnings
|
|
210
|
-
|
|
211
|
-
### Color Scheme
|
|
212
|
-
|
|
213
|
-
- 🟣 **Purple**: `[RPC]` prefix and request IDs
|
|
214
|
-
- 🟢 **Green**: Service names and success indicators
|
|
215
|
-
- 🔴 **Red**: Method names and error indicators
|
|
216
|
-
- 🟠 **Orange**: Warning messages
|
|
217
|
-
- ⚫ **Gray**: Separators and structural elements
|
|
269
|
+
When logging is enabled, the following information is logged:
|
|
218
270
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
[RPC] Success: MathService.add [rpc-123]
|
|
224
|
-
[RPC] Error: MathService.divide [rpc-124]
|
|
225
|
-
[RPC] Unknown service: InvalidService [rpc-125]
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
*Note: Colors are visible in browser developer console, not in plain text.*
|
|
271
|
+
- **Function Calls**: Service name, method name, arguments, sender ID, and timestamp
|
|
272
|
+
- **Success Responses**: Service name, method name, result, and timestamp
|
|
273
|
+
- **Error Responses**: Service name, method name, error message, and timestamp
|
|
274
|
+
- **Unknown Services/Methods**: Warnings for invalid service or method calls
|
|
229
275
|
|
|
230
276
|
### Use Cases
|
|
231
277
|
|
|
@@ -361,6 +407,34 @@ const observable = new ContentObservable(
|
|
|
361
407
|
// observable.dispose();
|
|
362
408
|
```
|
|
363
409
|
|
|
410
|
+
### Subscribing from Extension Page
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
// popup.ts / options.ts
|
|
414
|
+
import { ExtPageObservable, createIdentifier } from 'crx-rpc';
|
|
415
|
+
|
|
416
|
+
interface ICounterObservable {
|
|
417
|
+
value: number;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const ICounterObservable = createIdentifier<ICounterObservable>('Counter');
|
|
421
|
+
|
|
422
|
+
// Extension page can subscribe to background observables
|
|
423
|
+
const observable = new ExtPageObservable(
|
|
424
|
+
ICounterObservable,
|
|
425
|
+
'main',
|
|
426
|
+
(value) => {
|
|
427
|
+
console.log('Counter from extension page:', value.value);
|
|
428
|
+
document.getElementById('counter').textContent = value.value.toString();
|
|
429
|
+
}
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// Cleanup when done
|
|
433
|
+
window.addEventListener('unload', () => {
|
|
434
|
+
observable.dispose();
|
|
435
|
+
});
|
|
436
|
+
```
|
|
437
|
+
|
|
364
438
|
### Observable Communication Patterns
|
|
365
439
|
|
|
366
440
|
The Observable system supports multiple communication patterns with centralized management:
|
|
@@ -413,6 +487,94 @@ if (!client.isDisposed()) {
|
|
|
413
487
|
}
|
|
414
488
|
```
|
|
415
489
|
|
|
490
|
+
### Extension Page Accessing Content Script Services
|
|
491
|
+
|
|
492
|
+
Extension pages can access content script services using `TabRPCClient` by specifying the target tab ID:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
// popup.ts
|
|
496
|
+
import { TabRPCClient } from 'crx-rpc';
|
|
497
|
+
import { IContentService } from './services';
|
|
498
|
+
|
|
499
|
+
// Get current active tab
|
|
500
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
501
|
+
|
|
502
|
+
if (tab.id) {
|
|
503
|
+
// Create RPC client for specific tab
|
|
504
|
+
const tabClient = new TabRPCClient(tab.id);
|
|
505
|
+
|
|
506
|
+
// Access content script services in that tab
|
|
507
|
+
const contentService = tabClient.createWebRPCService(IContentService);
|
|
508
|
+
|
|
509
|
+
// Call content script methods
|
|
510
|
+
const result = await contentService.getDOMInfo();
|
|
511
|
+
console.log('DOM info from content script:', result);
|
|
512
|
+
|
|
513
|
+
// Cleanup when done
|
|
514
|
+
window.addEventListener('unload', () => {
|
|
515
|
+
tabClient.dispose();
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
#### Use Cases for Extension Page → Content Script Communication:
|
|
521
|
+
|
|
522
|
+
1. **DOM Inspection**: Popup queries content script for page information
|
|
523
|
+
2. **User Actions**: Options page triggers content script actions on specific tabs
|
|
524
|
+
3. **Multi-tab Management**: Sidepanel coordinates actions across multiple tabs
|
|
525
|
+
4. **Live Preview**: Extension page gets real-time updates from content script
|
|
526
|
+
|
|
527
|
+
#### Complete Example: Popup with Tab-specific Services
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
// content.ts - Register services in content script
|
|
531
|
+
import { ContentRPCHost } from 'crx-rpc';
|
|
532
|
+
import { IPageService } from './services';
|
|
533
|
+
|
|
534
|
+
class PageService implements IPageService {
|
|
535
|
+
async getTitle(): Promise<string> {
|
|
536
|
+
return document.title;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async getSelection(): Promise<string> {
|
|
540
|
+
return window.getSelection()?.toString() || '';
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
async highlightText(text: string): Promise<void> {
|
|
544
|
+
// Highlight logic...
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const contentHost = new ContentRPCHost();
|
|
549
|
+
contentHost.register(IPageService, new PageService());
|
|
550
|
+
|
|
551
|
+
// popup.ts - Access content script from popup
|
|
552
|
+
import { TabRPCClient, ExtPageRPCClient } from 'crx-rpc';
|
|
553
|
+
import { IPageService, IMathService } from './services';
|
|
554
|
+
|
|
555
|
+
// Access background services
|
|
556
|
+
const bgClient = new ExtPageRPCClient();
|
|
557
|
+
const mathService = bgClient.createWebRPCService(IMathService);
|
|
558
|
+
|
|
559
|
+
// Access content script services in active tab
|
|
560
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
561
|
+
if (tab.id) {
|
|
562
|
+
const tabClient = new TabRPCClient(tab.id);
|
|
563
|
+
const pageService = tabClient.createWebRPCService(IPageService);
|
|
564
|
+
|
|
565
|
+
// Get page info from content script
|
|
566
|
+
const title = await pageService.getTitle();
|
|
567
|
+
const selection = await pageService.getSelection();
|
|
568
|
+
|
|
569
|
+
// Process with background service
|
|
570
|
+
const result = await mathService.calculate(selection.length);
|
|
571
|
+
|
|
572
|
+
// Update popup UI
|
|
573
|
+
document.getElementById('title').textContent = title;
|
|
574
|
+
document.getElementById('result').textContent = result.toString();
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
416
578
|
## Usage Scenarios
|
|
417
579
|
|
|
418
580
|
### Scenario 1: Web Page Only
|
package/README.zh-CN.md
CHANGED
|
@@ -155,25 +155,118 @@ async function calculate() {
|
|
|
155
155
|
}
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
### 5. 使用客户端(扩展页面)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// popup.ts / options.ts / sidepanel.ts
|
|
162
|
+
import { ExtPageRPCClient } from 'crx-rpc';
|
|
163
|
+
import { IMathService } from './services/math';
|
|
164
|
+
|
|
165
|
+
async function calculate() {
|
|
166
|
+
// 创建扩展页面RPC客户端
|
|
167
|
+
const client = new ExtPageRPCClient();
|
|
168
|
+
|
|
169
|
+
// 创建类型安全的服务代理
|
|
170
|
+
const mathService = client.createWebRPCService(IMathService);
|
|
171
|
+
|
|
172
|
+
// 直接调用background服务
|
|
173
|
+
const sum = await mathService.add(1, 2);
|
|
174
|
+
const difference = await mathService.subtract(10, 5);
|
|
175
|
+
const product = await mathService.multiply(3, 4);
|
|
176
|
+
const quotient = await mathService.divide(15, 3);
|
|
177
|
+
|
|
178
|
+
console.log('结果:', { sum, difference, product, quotient });
|
|
179
|
+
|
|
180
|
+
// 页面关闭时自动清理
|
|
181
|
+
window.addEventListener('unload', () => {
|
|
182
|
+
client.dispose();
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
158
187
|
## 架构
|
|
159
188
|
|
|
189
|
+
### 完整通信拓扑图
|
|
190
|
+
|
|
191
|
+
```mermaid
|
|
192
|
+
graph TB
|
|
193
|
+
subgraph WebPage["网页上下文"]
|
|
194
|
+
WC[WebRPCClient]
|
|
195
|
+
WO[WebObservable]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
subgraph ContentScript["内容脚本上下文"]
|
|
199
|
+
CR[ContentRPC<br/>桥接模式]
|
|
200
|
+
CC[ContentRPCClient<br/>直接模式]
|
|
201
|
+
CO[ContentObservable]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
subgraph Background["背景脚本上下文"]
|
|
205
|
+
BR[BackgroundRPC]
|
|
206
|
+
MS[MathService]
|
|
207
|
+
US[UserService]
|
|
208
|
+
RS[RemoteSubject]
|
|
209
|
+
RSM[RemoteSubjectManager]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
subgraph ExtPage["扩展页面上下文<br/>(Popup/Options/Sidepanel)"]
|
|
213
|
+
EC[ExtPageRPCClient]
|
|
214
|
+
EO[ExtPageObservable]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
subgraph TabContext["标签页特定访问"]
|
|
218
|
+
TC[TabRPCClient]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
%% RPC 调用
|
|
222
|
+
WC -->|"CustomEvent<br/>.add(1,2)"| CR
|
|
223
|
+
CR -->|"chrome.runtime<br/>转发"| BR
|
|
224
|
+
CC -->|"chrome.runtime<br/>.multiply(2,3)"| BR
|
|
225
|
+
EC -->|"chrome.runtime<br/>.divide(10,2)"| BR
|
|
226
|
+
TC -->|"chrome.tabs<br/>访问内容服务"| CC
|
|
227
|
+
|
|
228
|
+
BR -->|响应| CR
|
|
229
|
+
CR -->|CustomEvent| WC
|
|
230
|
+
BR -->|响应| CC
|
|
231
|
+
BR -->|响应| EC
|
|
232
|
+
|
|
233
|
+
BR -.->|管理| MS
|
|
234
|
+
BR -.->|管理| US
|
|
235
|
+
|
|
236
|
+
%% Observable 数据流
|
|
237
|
+
WO -.->|订阅| CR
|
|
238
|
+
CR -.->|转发| RSM
|
|
239
|
+
CO -.->|订阅| RSM
|
|
240
|
+
EO -.->|订阅| RSM
|
|
241
|
+
RSM -.->|广播| RS
|
|
242
|
+
RS -.->|更新| CR
|
|
243
|
+
CR -.->|更新| WO
|
|
244
|
+
RS -.->|更新| CO
|
|
245
|
+
RS -.->|更新| EO
|
|
246
|
+
|
|
247
|
+
style WC fill:#e1f5ff
|
|
248
|
+
style CC fill:#e1f5ff
|
|
249
|
+
style EC fill:#e1f5ff
|
|
250
|
+
style TC fill:#e1f5ff
|
|
251
|
+
style BR fill:#fff4e6
|
|
252
|
+
style MS fill:#f0f0f0
|
|
253
|
+
style US fill:#f0f0f0
|
|
254
|
+
style RS fill:#ffe6f0
|
|
255
|
+
style RSM fill:#ffe6f0
|
|
256
|
+
style CR fill:#e8f5e9
|
|
160
257
|
```
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
│
|
|
174
|
-
┌─────────────────┐
|
|
175
|
-
│ContentRPCClient │
|
|
176
|
-
│ (直接) │
|
|
258
|
+
|
|
259
|
+
### 通信路径
|
|
260
|
+
|
|
261
|
+
| 路径 | 方式 | 描述 |
|
|
262
|
+
|------|------|------|
|
|
263
|
+
| **网页 → 背景脚本** | CustomEvent + chrome.runtime | 通过 ContentRPC 桥接 |
|
|
264
|
+
| **内容脚本 → 背景脚本** | chrome.runtime | 直接通信 |
|
|
265
|
+
| **扩展页面 → 背景脚本** | chrome.runtime | 直接通信 |
|
|
266
|
+
| **扩展页面 → 内容脚本** | chrome.tabs + TabRPCClient | 标签页特定访问 |
|
|
267
|
+
| **背景脚本 → 所有上下文** | RemoteSubject 广播 | 实时数据流 |
|
|
268
|
+
|
|
269
|
+
### 核心组件
|
|
177
270
|
│ │
|
|
178
271
|
│ 代理服务 │
|
|
179
272
|
│ .subtract(5,2) │
|
|
@@ -187,12 +280,14 @@ async function calculate() {
|
|
|
187
280
|
3. **背景脚本 → 内容脚本**: 使用 `chrome.tabs.sendMessage`
|
|
188
281
|
4. **内容脚本 → 网页**: 使用 `window.dispatchEvent` 和 `CustomEvent`
|
|
189
282
|
5. **内容脚本直接**: 直接使用 `chrome.runtime.sendMessage` (ContentRPCClient)
|
|
283
|
+
6. **扩展页面 ↔ 背景脚本**: 直接使用 `chrome.runtime.sendMessage/onMessage` (ExtPageRPCClient)
|
|
190
284
|
|
|
191
285
|
### 核心组件
|
|
192
286
|
|
|
193
|
-
- **WebRPCClient**:
|
|
287
|
+
- **WebRPCClient**: 用于网页的客户端,使用window事件
|
|
194
288
|
- **ContentRPC**: 在网页和背景脚本间转发消息的桥接器
|
|
195
|
-
- **ContentRPCClient**: 内容脚本的直接RPC
|
|
289
|
+
- **ContentRPCClient**: 内容脚本的直接RPC客户端(绕过桥接器)
|
|
290
|
+
- **ExtPageRPCClient**: 用于扩展页面(popup/options/sidepanel)的直接RPC客户端
|
|
196
291
|
- **BackgroundRPC**: 背景脚本中的服务注册表和处理器
|
|
197
292
|
- **RPCClient**: 具有服务代理生成功能的基础客户端
|
|
198
293
|
|
|
@@ -217,31 +312,12 @@ const rpc = new BackgroundRPC(true); // 启用日志
|
|
|
217
312
|
|
|
218
313
|
### 日志输出
|
|
219
314
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
- **函数调用**: `[RPC] Call: Service.Method [ID]` 带有彩色组件
|
|
223
|
-
- **成功响应**: `[RPC] Success: Service.Method [ID]` 带有绿色成功指示器
|
|
224
|
-
- **错误响应**: `[RPC] Error: Service.Method [ID]` 带有红色错误指示器
|
|
225
|
-
- **未知服务/方法**: `[RPC] Unknown service/method: Service.Method [ID]` 带有橙色警告
|
|
226
|
-
|
|
227
|
-
### 颜色方案
|
|
228
|
-
|
|
229
|
-
- 🟣 **紫色**: `[RPC]` 前缀和请求ID
|
|
230
|
-
- 🟢 **绿色**: 服务名和成功指示器
|
|
231
|
-
- 🔴 **红色**: 方法名和错误指示器
|
|
232
|
-
- 🟠 **橙色**: 警告消息
|
|
233
|
-
- ⚫ **灰色**: 分隔符和结构元素
|
|
315
|
+
启用日志时,会记录以下信息:
|
|
234
316
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
[RPC] Success: MathService.add [rpc-123]
|
|
240
|
-
[RPC] Error: MathService.divide [rpc-124]
|
|
241
|
-
[RPC] Unknown service: InvalidService [rpc-125]
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
*注意:颜色在浏览器开发者控制台中可见,不在纯文本中显示。*
|
|
317
|
+
- **函数调用**: 服务名、方法名、参数、发送者ID和时间戳
|
|
318
|
+
- **成功响应**: 服务名、方法名、结果和时间戳
|
|
319
|
+
- **错误响应**: 服务名、方法名、错误消息和时间戳
|
|
320
|
+
- **未知服务/方法**: 无效服务或方法调用的警告
|
|
245
321
|
|
|
246
322
|
### 使用场景
|
|
247
323
|
|
|
@@ -377,6 +453,35 @@ const observable = new ContentObservable(
|
|
|
377
453
|
// observable.dispose();
|
|
378
454
|
```
|
|
379
455
|
|
|
456
|
+
### 从扩展页面订阅
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// popup.ts / options.ts / sidepanel.ts
|
|
460
|
+
import { ExtPageObservable, createIdentifier } from 'crx-rpc';
|
|
461
|
+
|
|
462
|
+
interface ICounterObservable {
|
|
463
|
+
value: number;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const ICounterObservable = createIdentifier<ICounterObservable>('Counter');
|
|
467
|
+
|
|
468
|
+
// 扩展页面可以订阅background的observables
|
|
469
|
+
const observable = new ExtPageObservable(
|
|
470
|
+
ICounterObservable,
|
|
471
|
+
'main',
|
|
472
|
+
(value) => {
|
|
473
|
+
console.log('Popup计数器更新:', value.value);
|
|
474
|
+
// 更新popup UI
|
|
475
|
+
document.getElementById('counter').textContent = value.value.toString();
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
// 页面关闭时自动清理
|
|
480
|
+
window.addEventListener('unload', () => {
|
|
481
|
+
observable.dispose();
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
380
485
|
### Observable通信模式
|
|
381
486
|
|
|
382
487
|
Observable系统支持多种具有集中式管理的通信模式:
|
|
@@ -477,6 +582,150 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
477
582
|
4. **桥接+客户端**: 既作为网页的桥接器又作为直接客户端
|
|
478
583
|
5. **DOM操作**: 使用RPC数据修改页面内容
|
|
479
584
|
|
|
585
|
+
### 扩展页面作为直接客户端
|
|
586
|
+
|
|
587
|
+
扩展页面(popup、options、sidepanel等)可以直接与background通信,无需content script:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
// popup.ts
|
|
591
|
+
import { ExtPageRPCClient, ExtPageObservable } from 'crx-rpc';
|
|
592
|
+
import { IMathService, IUserService, ICounterObservable } from './services';
|
|
593
|
+
|
|
594
|
+
const client = new ExtPageRPCClient();
|
|
595
|
+
|
|
596
|
+
// 创建服务代理
|
|
597
|
+
const mathService = client.createWebRPCService(IMathService);
|
|
598
|
+
const userService = client.createWebRPCService(IUserService);
|
|
599
|
+
|
|
600
|
+
// 直接调用背景服务
|
|
601
|
+
const result = await mathService.add(5, 3);
|
|
602
|
+
const user = await userService.getUser('123');
|
|
603
|
+
|
|
604
|
+
// 扩展页面也可以订阅observables
|
|
605
|
+
const counterObservable = new ExtPageObservable(
|
|
606
|
+
ICounterObservable,
|
|
607
|
+
'main',
|
|
608
|
+
(value) => {
|
|
609
|
+
// 实时更新popup UI
|
|
610
|
+
document.getElementById('counter').textContent = value.toString();
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
// 在popup中使用
|
|
615
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
616
|
+
const calculation = await mathService.multiply(2, 3);
|
|
617
|
+
document.getElementById('result').textContent = `结果: ${calculation}`;
|
|
618
|
+
|
|
619
|
+
// 更新用户信息
|
|
620
|
+
const currentUser = await userService.getUser('me');
|
|
621
|
+
document.getElementById('username').textContent = currentUser.name;
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// 页面关闭时清理
|
|
625
|
+
window.addEventListener('unload', () => {
|
|
626
|
+
client.dispose();
|
|
627
|
+
counterObservable.dispose();
|
|
628
|
+
});
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### 扩展页面使用场景
|
|
632
|
+
|
|
633
|
+
扩展页面在各种场景中使用RPC:
|
|
634
|
+
|
|
635
|
+
1. **直接通信**: 与background service直接通信,无需content script
|
|
636
|
+
2. **UI交互**: 根据background数据更新popup/options界面
|
|
637
|
+
3. **实时更新**: 订阅observables获取实时数据推送
|
|
638
|
+
4. **用户设置**: 在options页面中读取/保存配置
|
|
639
|
+
5. **状态同步**: 与background保持状态同步
|
|
640
|
+
|
|
641
|
+
### 扩展页面访问内容脚本服务
|
|
642
|
+
|
|
643
|
+
扩展页面可以使用 `TabRPCClient` 通过指定 tab ID 来访问内容脚本的服务:
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
// popup.ts
|
|
647
|
+
import { TabRPCClient } from 'crx-rpc';
|
|
648
|
+
import { IContentService } from './services';
|
|
649
|
+
|
|
650
|
+
// 获取当前活动标签页
|
|
651
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
652
|
+
|
|
653
|
+
if (tab.id) {
|
|
654
|
+
// 为特定 tab 创建 RPC 客户端
|
|
655
|
+
const tabClient = new TabRPCClient(tab.id);
|
|
656
|
+
|
|
657
|
+
// 访问该 tab 中的内容脚本服务
|
|
658
|
+
const contentService = tabClient.createWebRPCService(IContentService);
|
|
659
|
+
|
|
660
|
+
// 调用内容脚本方法
|
|
661
|
+
const result = await contentService.getDOMInfo();
|
|
662
|
+
console.log('来自内容脚本的 DOM 信息:', result);
|
|
663
|
+
|
|
664
|
+
// 完成时清理
|
|
665
|
+
window.addEventListener('unload', () => {
|
|
666
|
+
tabClient.dispose();
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
#### 扩展页面 → 内容脚本通信的使用场景:
|
|
672
|
+
|
|
673
|
+
1. **DOM 检查**: Popup 查询内容脚本获取页面信息
|
|
674
|
+
2. **用户操作**: Options 页面触发特定标签页的内容脚本操作
|
|
675
|
+
3. **多标签管理**: Sidepanel 协调多个标签页的操作
|
|
676
|
+
4. **实时预览**: 扩展页面从内容脚本获取实时更新
|
|
677
|
+
|
|
678
|
+
#### 完整示例: Popup 与标签页特定服务交互
|
|
679
|
+
|
|
680
|
+
```typescript
|
|
681
|
+
// content.ts - 在内容脚本中注册服务
|
|
682
|
+
import { ContentRPCHost } from 'crx-rpc';
|
|
683
|
+
import { IPageService } from './services';
|
|
684
|
+
|
|
685
|
+
class PageService implements IPageService {
|
|
686
|
+
async getTitle(): Promise<string> {
|
|
687
|
+
return document.title;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
async getSelection(): Promise<string> {
|
|
691
|
+
return window.getSelection()?.toString() || '';
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async highlightText(text: string): Promise<void> {
|
|
695
|
+
// 高亮逻辑...
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const contentHost = new ContentRPCHost();
|
|
700
|
+
contentHost.register(IPageService, new PageService());
|
|
701
|
+
|
|
702
|
+
// popup.ts - 从 popup 访问内容脚本
|
|
703
|
+
import { TabRPCClient, ExtPageRPCClient } from 'crx-rpc';
|
|
704
|
+
import { IPageService, IMathService } from './services';
|
|
705
|
+
|
|
706
|
+
// 访问背景服务
|
|
707
|
+
const bgClient = new ExtPageRPCClient();
|
|
708
|
+
const mathService = bgClient.createWebRPCService(IMathService);
|
|
709
|
+
|
|
710
|
+
// 访问活动标签页中的内容脚本服务
|
|
711
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
712
|
+
if (tab.id) {
|
|
713
|
+
const tabClient = new TabRPCClient(tab.id);
|
|
714
|
+
const pageService = tabClient.createWebRPCService(IPageService);
|
|
715
|
+
|
|
716
|
+
// 从内容脚本获取页面信息
|
|
717
|
+
const title = await pageService.getTitle();
|
|
718
|
+
const selection = await pageService.getSelection();
|
|
719
|
+
|
|
720
|
+
// 使用背景服务处理
|
|
721
|
+
const result = await mathService.calculate(selection.length);
|
|
722
|
+
|
|
723
|
+
// 更新 popup UI
|
|
724
|
+
document.getElementById('title').textContent = title;
|
|
725
|
+
document.getElementById('result').textContent = result.toString();
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
480
729
|
### 复杂数据类型
|
|
481
730
|
|
|
482
731
|
```typescript
|
|
@@ -537,15 +786,20 @@ const [sum, user, file] = await Promise.all([
|
|
|
537
786
|
|
|
538
787
|
### 场景2: 仅内容脚本
|
|
539
788
|
- 内容脚本需要直接访问背景服务
|
|
540
|
-
- 使用: 直接使用 `ContentRPCClient
|
|
789
|
+
- 使用: 直接使用 `ContentRPCClient`(无需桥接器)
|
|
541
790
|
|
|
542
791
|
### 场景3: 网页和内容脚本同时
|
|
543
792
|
- 两个上下文都需要RPC访问
|
|
544
793
|
- 使用: `ContentRPC` 桥接器 + `ContentRPCClient` 进行直接访问
|
|
545
794
|
|
|
546
|
-
### 场景4:
|
|
795
|
+
### 场景4: 扩展页面(Popup/Options/Sidepanel)
|
|
796
|
+
- 扩展内置页面需要访问背景服务
|
|
797
|
+
- 使用: 直接使用 `ExtPageRPCClient`
|
|
798
|
+
- 特点: 不需要content script,直接与background通信
|
|
799
|
+
|
|
800
|
+
### 场景5: 实时数据流
|
|
547
801
|
- 背景脚本需要向多个上下文推送更新
|
|
548
|
-
- 使用: `RemoteSubject` + `WebObservable`/`ContentObservable`
|
|
802
|
+
- 使用: `RemoteSubject` + `WebObservable`/`ContentObservable`/`ExtPageObservable`
|
|
549
803
|
|
|
550
804
|
## API参考
|
|
551
805
|
|
|
@@ -555,6 +809,7 @@ const [sum, user, file] = await Promise.all([
|
|
|
555
809
|
- **`ContentRPC`**: 网页和背景脚本间的消息桥接器
|
|
556
810
|
- **`WebRPCClient`**: 网页的RPC客户端
|
|
557
811
|
- **`ContentRPCClient`**: 内容脚本的直接RPC客户端
|
|
812
|
+
- **`ExtPageRPCClient`**: 扩展页面(popup/options/sidepanel)的直接RPC客户端
|
|
558
813
|
- **`RemoteSubjectManager`**: 集中式observable消息管理系统
|
|
559
814
|
|
|
560
815
|
### Observable类
|
|
@@ -563,6 +818,7 @@ const [sum, user, file] = await Promise.all([
|
|
|
563
818
|
- **`RemoteSubject<T>`**: 与管理器配合进行纯状态管理的Observable subject
|
|
564
819
|
- **`WebObservable<T>`**: 网页的Observable订阅者
|
|
565
820
|
- **`ContentObservable<T>`**: 内容脚本的Observable订阅者
|
|
821
|
+
- **`ExtPageObservable<T>`**: 扩展页面的Observable订阅者
|
|
566
822
|
|
|
567
823
|
### 工具函数
|
|
568
824
|
|