chrome-devtools-frontend 1.0.1661063 → 1.0.1662965
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/.agents/skills/devtools-unicode-escaping/SKILL.md +64 -0
- package/.agents/skills/evaluate-ai-css-completion/SKILL.md +19 -9
- package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +44 -16
- package/.agents/skills/migrate-chromium-test/SKILL.md +32 -2
- package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +7 -1
- package/docs/ui_engineering.md +75 -42
- package/front_end/core/common/SettingRegistration.ts +0 -10
- package/front_end/core/common/Settings.ts +69 -3
- package/front_end/core/platform/ArrayUtilities.ts +10 -0
- package/front_end/core/platform/StringUtilities.ts +38 -6
- package/front_end/core/sdk/ServiceWorkerCacheModel.ts +0 -10
- package/front_end/core/sdk/StorageBucketsModel.ts +0 -10
- package/front_end/core/sdk/sdk-meta.ts +0 -1
- package/front_end/entrypoints/inspector_main/OutermostTargetSelector.ts +0 -13
- package/front_end/entrypoints/inspector_main/RenderingOptions.ts +8 -3
- package/front_end/entrypoints/inspector_main/inspector_main-meta.ts +3 -3
- package/front_end/entrypoints/main/MainImpl.ts +7 -2
- package/front_end/foundation/README.md +86 -0
- package/front_end/foundation/Universe.ts +16 -0
- package/front_end/generated/InspectorBackendCommands.ts +6 -19
- package/front_end/generated/protocol-mapping.d.ts +0 -53
- package/front_end/generated/protocol-proxy-api.d.ts +0 -46
- package/front_end/generated/protocol.ts +20 -186
- package/front_end/models/ai_assistance/AiAgent2.ts +3 -3
- package/front_end/models/ai_assistance/AiConversation.ts +21 -10
- package/front_end/models/ai_assistance/AiHistoryStorage.ts +21 -10
- package/front_end/models/ai_assistance/ChangeManager.ts +2 -2
- package/front_end/models/ai_assistance/ExtensionScope.ts +6 -2
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
- package/front_end/models/badges/UserBadges.ts +31 -17
- package/front_end/models/bindings/CompilerScriptMapping.ts +5 -4
- package/front_end/models/bindings/ContentProviderBasedProject.ts +2 -3
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +4 -3
- package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
- package/front_end/models/bindings/NetworkProject.ts +22 -1
- package/front_end/models/bindings/ResourceMapping.ts +4 -3
- package/front_end/models/bindings/ResourceScriptMapping.ts +2 -1
- package/front_end/models/bindings/SASSSourceMapping.ts +4 -3
- package/front_end/models/bindings/StylesSourceMapping.ts +3 -3
- package/front_end/models/emulation/DeviceModeModel.ts +67 -5
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +7 -5
- package/front_end/models/extensions/RecorderPluginManager.ts +10 -6
- package/front_end/models/har/Log.snapshot.txt +193 -0
- package/front_end/models/har/Log.ts +3 -2
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +14 -5
- package/front_end/models/issues_manager/CheckFormsIssuesTrigger.ts +8 -9
- package/front_end/models/issues_manager/RelatedIssue.ts +10 -8
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
- package/front_end/models/logs/RequestResolver.ts +2 -2
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +0 -5
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +8 -6
- package/front_end/models/source_map_scopes/NamesResolver.ts +36 -30
- package/front_end/models/stack_trace/StackTrace.ts +17 -0
- package/front_end/models/trace/handlers/FramesHandler.ts +4 -7
- package/front_end/models/trace/handlers/ModelHandlers.ts +0 -1
- package/front_end/models/trace/handlers/RendererHandler.ts +2 -15
- package/front_end/models/trace/handlers/Threads.ts +4 -11
- package/front_end/models/trace/insights/DOMSize.ts +1 -1
- package/front_end/models/workspace/WorkspaceImpl.ts +1 -10
- package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -44
- package/front_end/panels/application/CookieItemsView.ts +1 -1
- package/front_end/panels/application/FrameDetailsView.ts +1 -1
- package/front_end/panels/application/IndexedDBModel.ts +0 -10
- package/front_end/panels/application/IndexedDBViews.ts +240 -164
- package/front_end/panels/application/ServiceWorkersView.ts +494 -411
- package/front_end/panels/application/SharedStorageModel.ts +0 -10
- package/front_end/panels/application/application.ts +0 -6
- package/front_end/panels/application/components/AdsView.ts +23 -0
- package/front_end/panels/application/components/SharedStorageAccessGrid.ts +1 -1
- package/front_end/panels/application/components/adsView.css +21 -1
- package/front_end/panels/application/components/components.ts +0 -2
- package/front_end/panels/application/indexedDBViews.css +1 -1
- package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +1 -1
- package/front_end/panels/application/serviceWorkersView.css +40 -4
- package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +147 -123
- package/front_end/panels/common/ExtensionServer.ts +3 -3
- package/front_end/panels/console/ConsoleView.ts +1 -1
- package/front_end/panels/emulation/DeviceModeView.ts +139 -70
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +53 -12
- package/front_end/panels/network/NetworkDataGridNode.ts +43 -1
- package/front_end/panels/network/RequestInitiatorView.ts +24 -2
- package/front_end/panels/network/RequestPayloadView.ts +77 -39
- package/front_end/panels/network/components/ResponseHeaderSection.ts +5 -3
- package/front_end/panels/network/networkLogView.css +5 -0
- package/front_end/panels/network/requestInitiatorViewTree.css +5 -0
- package/front_end/panels/profiler/HeapProfileView.ts +198 -79
- package/front_end/panels/profiler/HeapSnapshotView.ts +2 -2
- package/front_end/panels/recorder/README.md +1 -2
- package/front_end/panels/recorder/RecorderPanel.ts +1749 -17
- package/front_end/panels/recorder/recorder.ts +0 -2
- package/front_end/panels/settings/AISettingsTab.ts +13 -0
- package/front_end/panels/sources/SourcesNavigator.ts +3 -2
- package/front_end/panels/sources/SourcesView.ts +0 -17
- package/front_end/panels/sources/UISourceCodeFrame.ts +42 -0
- package/front_end/panels/sources/sources.ts +0 -2
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -17
- package/front_end/panels/timeline/ThreadAppender.ts +0 -106
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/report_view/ReportView.ts +11 -5
- package/front_end/ui/components/report_view/report.css +4 -1
- package/front_end/ui/components/spinners/Spinner.ts +29 -32
- package/front_end/ui/components/spinners/spinner.css +32 -84
- package/front_end/ui/legacy/TextPrompt.ts +49 -1
- package/front_end/ui/legacy/Treeoutline.ts +14 -6
- package/front_end/ui/legacy/UIUtils.ts +36 -11
- package/front_end/ui/legacy/Widget.ts +17 -10
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +2 -2
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +36 -7
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +63 -68
- package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +1 -1
- package/front_end/ui/legacy/components/object_ui/objectValue.css +2 -1
- package/front_end/ui/legacy/inspectorCommon.css +4 -4
- package/front_end/ui/legacy/textPrompt.css +10 -0
- package/front_end/ui/lit/lit.ts +1 -0
- package/front_end/ui/lit/render.ts +44 -10
- package/front_end/ui/lit/strip-whitespace.ts +23 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
- package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +0 -183
- package/front_end/panels/application/InterestGroupStorageModel.ts +0 -81
- package/front_end/panels/application/InterestGroupStorageView.ts +0 -151
- package/front_end/panels/application/InterestGroupTreeElement.ts +0 -65
- package/front_end/panels/application/components/InterestGroupAccessGrid.ts +0 -135
- package/front_end/panels/application/components/interestGroupAccessGrid.css +0 -27
- package/front_end/panels/application/interestGroupStorageView.css +0 -9
- package/front_end/panels/recorder/RecorderController.ts +0 -1595
- package/front_end/panels/sources/InplaceFormatterEditorAction.ts +0 -149
- /package/front_end/panels/recorder/{recorderController.css → recorderPanel.css} +0 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
6
|
import * as Host from '../../core/host/host.js';
|
|
7
|
+
import * as Root from '../../core/root/root.js';
|
|
7
8
|
|
|
8
9
|
import {AiExplorerBadge} from './AiExplorerBadge.js';
|
|
9
10
|
import type {Badge, BadgeAction, BadgeActionEvents, BadgeContext, TriggerOptions} from './Badge.js';
|
|
@@ -32,7 +33,6 @@ const SNOOZE_TIME_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
|
32
33
|
const MAX_SNOOZE_COUNT = 3;
|
|
33
34
|
const DELAY_BEFORE_TRIGGER = 1500;
|
|
34
35
|
|
|
35
|
-
let userBadgesInstance: UserBadges|undefined = undefined;
|
|
36
36
|
export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
37
37
|
readonly #badgeActionEventTarget = new Common.ObjectWrapper.ObjectWrapper<BadgeActionEvents>();
|
|
38
38
|
|
|
@@ -43,6 +43,9 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
|
43
43
|
#starterBadgeLastSnoozedTimestamp: Common.Settings.Setting<number>;
|
|
44
44
|
#starterBadgeDismissed: Common.Settings.Setting<boolean>;
|
|
45
45
|
|
|
46
|
+
readonly #settings: Common.Settings.Settings;
|
|
47
|
+
readonly #gdpClient: Host.GdpClient.GdpClient;
|
|
48
|
+
|
|
46
49
|
static readonly BADGE_REGISTRY: BadgeClass[] = [
|
|
47
50
|
StarterBadge,
|
|
48
51
|
SpeedsterBadge,
|
|
@@ -51,21 +54,27 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
|
51
54
|
AiExplorerBadge,
|
|
52
55
|
];
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
constructor(
|
|
58
|
+
settings: Common.Settings.Settings,
|
|
59
|
+
gdpClient: Host.GdpClient.GdpClient,
|
|
60
|
+
) {
|
|
55
61
|
super();
|
|
56
62
|
|
|
57
|
-
this.#
|
|
63
|
+
this.#settings = settings;
|
|
64
|
+
this.#gdpClient = gdpClient;
|
|
65
|
+
|
|
66
|
+
this.#receiveBadgesSetting = this.#settings.moduleSetting('receive-gdp-badges');
|
|
58
67
|
if (!Host.GdpClient.isBadgesEnabled()) {
|
|
59
68
|
this.#receiveBadgesSetting.set(false);
|
|
60
69
|
}
|
|
61
70
|
this.#receiveBadgesSetting.addChangeListener(this.#reconcileBadges, this);
|
|
62
71
|
|
|
63
|
-
this.#starterBadgeSnoozeCount =
|
|
64
|
-
'starter-badge-snooze-count', 0, Common.Settings.SettingStorageType.SYNCED);
|
|
65
|
-
this.#starterBadgeLastSnoozedTimestamp =
|
|
66
|
-
|
|
67
|
-
this.#starterBadgeDismissed =
|
|
68
|
-
'starter-badge-dismissed', false, Common.Settings.SettingStorageType.SYNCED);
|
|
72
|
+
this.#starterBadgeSnoozeCount =
|
|
73
|
+
this.#settings.createSetting('starter-badge-snooze-count', 0, Common.Settings.SettingStorageType.SYNCED);
|
|
74
|
+
this.#starterBadgeLastSnoozedTimestamp = this.#settings.createSetting('starter-badge-last-snoozed-timestamp', 0,
|
|
75
|
+
Common.Settings.SettingStorageType.SYNCED);
|
|
76
|
+
this.#starterBadgeDismissed =
|
|
77
|
+
this.#settings.createSetting('starter-badge-dismissed', false, Common.Settings.SettingStorageType.SYNCED);
|
|
69
78
|
|
|
70
79
|
this.#allBadges = UserBadges.BADGE_REGISTRY.map(badgeCtor => new badgeCtor({
|
|
71
80
|
onTriggerBadge: this.#onTriggerBadge.bind(this),
|
|
@@ -74,10 +83,16 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
static instance({forceNew}: {forceNew: boolean} = {forceNew: false}): UserBadges {
|
|
77
|
-
if (!
|
|
78
|
-
|
|
86
|
+
if (!Root.DevToolsContext.globalInstance().has(UserBadges) || forceNew) {
|
|
87
|
+
Root.DevToolsContext.globalInstance().set(
|
|
88
|
+
UserBadges,
|
|
89
|
+
new UserBadges(
|
|
90
|
+
Common.Settings.Settings.instance(),
|
|
91
|
+
Host.GdpClient.GdpClient.instance(),
|
|
92
|
+
),
|
|
93
|
+
);
|
|
79
94
|
}
|
|
80
|
-
return
|
|
95
|
+
return Root.DevToolsContext.globalInstance().get(UserBadges);
|
|
81
96
|
}
|
|
82
97
|
|
|
83
98
|
async initialize(): Promise<void> {
|
|
@@ -108,7 +123,7 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
|
108
123
|
return BadgeTriggerReason.AWARD;
|
|
109
124
|
}
|
|
110
125
|
|
|
111
|
-
const getProfileResponse = await
|
|
126
|
+
const getProfileResponse = await this.#gdpClient.getProfile();
|
|
112
127
|
// The `getProfile` call failed and returned a `null`.
|
|
113
128
|
// For that case, we don't show anything.
|
|
114
129
|
if (!getProfileResponse) {
|
|
@@ -147,7 +162,7 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
|
147
162
|
}
|
|
148
163
|
|
|
149
164
|
if (reason === BadgeTriggerReason.AWARD) {
|
|
150
|
-
const result = await
|
|
165
|
+
const result = await this.#gdpClient.createAward({name: badge.name});
|
|
151
166
|
if (!result) {
|
|
152
167
|
return;
|
|
153
168
|
}
|
|
@@ -192,7 +207,7 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
|
192
207
|
return;
|
|
193
208
|
}
|
|
194
209
|
|
|
195
|
-
const getProfileResponse = await
|
|
210
|
+
const getProfileResponse = await this.#gdpClient.getProfile();
|
|
196
211
|
if (!getProfileResponse) {
|
|
197
212
|
this.#deactivateAllBadges();
|
|
198
213
|
return;
|
|
@@ -209,8 +224,7 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
|
209
224
|
|
|
210
225
|
let awardedBadgeNames: Set<string>|null = null;
|
|
211
226
|
if (hasGdpProfile) {
|
|
212
|
-
awardedBadgeNames = await
|
|
213
|
-
{names: this.#allBadges.map(badge => badge.name)});
|
|
227
|
+
awardedBadgeNames = await this.#gdpClient.getAwardedBadgeNames({names: this.#allBadges.map(badge => badge.name)});
|
|
214
228
|
// This is a conservative approach. We bail out if `awardedBadgeNames` is null
|
|
215
229
|
// when there is a profile to prevent a negative user experience.
|
|
216
230
|
//
|
|
@@ -55,9 +55,9 @@ export class CompilerScriptMapping implements DebuggerSourceMapping {
|
|
|
55
55
|
this.#debuggerModel = debuggerModel;
|
|
56
56
|
this.#ignoreListManager = debuggerWorkspaceBinding.ignoreListManager;
|
|
57
57
|
|
|
58
|
-
this.#stubProject =
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
this.#stubProject =
|
|
59
|
+
new ContentProviderBasedProject(workspace, 'jsSourceMaps:stub:' + debuggerModel.target().id(),
|
|
60
|
+
Workspace.Workspace.projectTypes.Service, '', true /* isServiceProject */);
|
|
61
61
|
this.#eventListeners = [
|
|
62
62
|
this.#sourceMapManager.addEventListener(
|
|
63
63
|
SDK.SourceMapManager.Events.SourceMapWillAttach, this.sourceMapWillAttach, this),
|
|
@@ -464,7 +464,8 @@ export class CompilerScriptMapping implements DebuggerSourceMapping {
|
|
|
464
464
|
const projectType = script.isContentScript() ? Workspace.Workspace.projectTypes.ContentScripts :
|
|
465
465
|
Workspace.Workspace.projectTypes.Network;
|
|
466
466
|
project = new ContentProviderBasedProject(this.#stubProject.workspace(), projectId, projectType,
|
|
467
|
-
/* displayName */ '', /* isServiceProject */ false
|
|
467
|
+
/* displayName */ '', /* isServiceProject */ false);
|
|
468
|
+
NetworkProject.setTargetForProject(project, target);
|
|
468
469
|
this.#projects.set(projectId, project);
|
|
469
470
|
}
|
|
470
471
|
this.#sourceMapToProject.set(sourceMap, project);
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import type * as Common from '../../core/common/common.js';
|
|
6
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
7
7
|
import * as Platform from '../../core/platform/platform.js';
|
|
8
|
-
import type * as SDK from '../../core/sdk/sdk.js';
|
|
9
8
|
import * as TextUtils from '../text_utils/text_utils.js';
|
|
10
9
|
import * as Workspace from '../workspace/workspace.js';
|
|
11
10
|
|
|
@@ -28,8 +27,8 @@ export class ContentProviderBasedProject extends Workspace.Workspace.ProjectStor
|
|
|
28
27
|
readonly #isServiceProject: boolean;
|
|
29
28
|
readonly #uiSourceCodeToData = new WeakMap<Workspace.UISourceCode.UISourceCode, UISourceCodeData>();
|
|
30
29
|
constructor(workspace: Workspace.Workspace.WorkspaceImpl, id: string, type: Workspace.Workspace.projectTypes,
|
|
31
|
-
displayName: string, isServiceProject: boolean
|
|
32
|
-
super(workspace, id, type, displayName
|
|
30
|
+
displayName: string, isServiceProject: boolean) {
|
|
31
|
+
super(workspace, id, type, displayName);
|
|
33
32
|
this.#isServiceProject = isServiceProject;
|
|
34
33
|
workspace.addProject(this);
|
|
35
34
|
}
|
|
@@ -1100,9 +1100,10 @@ class ModelData {
|
|
|
1100
1100
|
project: ContentProviderBasedProject;
|
|
1101
1101
|
readonly uiSourceCodeToScripts: Map<Workspace.UISourceCode.UISourceCode, SDK.Script.Script[]>;
|
|
1102
1102
|
constructor(debuggerModel: SDK.DebuggerModel.DebuggerModel, workspace: Workspace.Workspace.WorkspaceImpl) {
|
|
1103
|
-
this.project =
|
|
1104
|
-
|
|
1105
|
-
|
|
1103
|
+
this.project =
|
|
1104
|
+
new ContentProviderBasedProject(workspace, 'language_plugins::' + debuggerModel.target().id(),
|
|
1105
|
+
Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
|
|
1106
|
+
NetworkProject.setTargetForProject(this.project, debuggerModel.target());
|
|
1106
1107
|
|
|
1107
1108
|
this.uiSourceCodeToScripts = new Map();
|
|
1108
1109
|
}
|
|
@@ -23,9 +23,9 @@ export class DefaultScriptMapping implements DebuggerSourceMapping {
|
|
|
23
23
|
debuggerWorkspaceBinding: DebuggerWorkspaceBinding) {
|
|
24
24
|
defaultScriptMappings.add(this);
|
|
25
25
|
this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
|
|
26
|
-
this.#project =
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
this.#project =
|
|
27
|
+
new ContentProviderBasedProject(workspace, 'debugger:' + debuggerModel.target().id(),
|
|
28
|
+
Workspace.Workspace.projectTypes.Debugger, '', true /* isServiceProject */);
|
|
29
29
|
this.#eventListeners = [
|
|
30
30
|
debuggerModel.addEventListener(SDK.DebuggerModel.Events.GlobalObjectCleared, this.globalObjectCleared, this),
|
|
31
31
|
debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, this.parsedScriptSource, this),
|
|
@@ -14,6 +14,7 @@ const uiSourceCodeToAttributionMap = new WeakMap<Workspace.UISourceCode.UISource
|
|
|
14
14
|
}>>();
|
|
15
15
|
|
|
16
16
|
export class NetworkProjectManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
17
|
+
readonly #projectToTargetMap = new WeakMap<Workspace.Workspace.Project, SDK.Target.Target>();
|
|
17
18
|
|
|
18
19
|
static instance({forceNew}: {
|
|
19
20
|
forceNew: boolean,
|
|
@@ -28,6 +29,18 @@ export class NetworkProjectManager extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
|
28
29
|
static removeInstance(): void {
|
|
29
30
|
Root.DevToolsContext.globalInstance().delete(NetworkProjectManager);
|
|
30
31
|
}
|
|
32
|
+
|
|
33
|
+
setTargetForProject(project: Workspace.Workspace.Project, target: SDK.Target.Target): void {
|
|
34
|
+
this.#projectToTargetMap.set(project, target);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getTargetForProject(project: Workspace.Workspace.Project): SDK.Target.Target|null {
|
|
38
|
+
return this.#projectToTargetMap.get(project) ?? null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getTargetForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): SDK.Target.Target|null {
|
|
42
|
+
return this.#projectToTargetMap.get(uiSourceCode.project()) ?? null;
|
|
43
|
+
}
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
export const enum Events {
|
|
@@ -131,7 +144,15 @@ export class NetworkProject {
|
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
static targetForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): SDK.Target.Target|null {
|
|
134
|
-
return
|
|
147
|
+
return NetworkProjectManager.instance().getTargetForUISourceCode(uiSourceCode);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static setTargetForProject(project: Workspace.Workspace.Project, target: SDK.Target.Target): void {
|
|
151
|
+
NetworkProjectManager.instance().setTargetForProject(project, target);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static getTargetForProject(project: Workspace.Workspace.Project): SDK.Target.Target|null {
|
|
155
|
+
return NetworkProjectManager.instance().getTargetForProject(project);
|
|
135
156
|
}
|
|
136
157
|
|
|
137
158
|
static framesForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode):
|
|
@@ -361,9 +361,10 @@ class ModelInfo {
|
|
|
361
361
|
constructor(resourceMapping: ResourceMapping, resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel) {
|
|
362
362
|
const target = resourceTreeModel.target();
|
|
363
363
|
this.resourceMapping = resourceMapping;
|
|
364
|
-
this.project =
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
this.project =
|
|
365
|
+
new ContentProviderBasedProject(resourceMapping.workspace, 'resources:' + target.id(),
|
|
366
|
+
Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
|
|
367
|
+
NetworkProject.setTargetForProject(this.project, target);
|
|
367
368
|
|
|
368
369
|
const cssModel = target.model(SDK.CSSModel.CSSModel);
|
|
369
370
|
console.assert(Boolean(cssModel));
|
|
@@ -74,7 +74,8 @@ export class ResourceScriptMapping implements DebuggerSourceMapping {
|
|
|
74
74
|
const projectType = script.isContentScript() ? Workspace.Workspace.projectTypes.ContentScripts :
|
|
75
75
|
Workspace.Workspace.projectTypes.Network;
|
|
76
76
|
project = new ContentProviderBasedProject(this.#workspace, projectId, projectType, '' /* displayName */,
|
|
77
|
-
false /* isServiceProject
|
|
77
|
+
false /* isServiceProject */);
|
|
78
|
+
NetworkProject.setTargetForProject(project, this.debuggerModel.target());
|
|
78
79
|
this.#projects.set(projectId, project);
|
|
79
80
|
}
|
|
80
81
|
return project;
|
|
@@ -26,9 +26,10 @@ export class SASSSourceMapping implements SourceMapping {
|
|
|
26
26
|
workspace: Workspace.Workspace.WorkspaceImpl, cssWorkspaceBinding: CSSWorkspaceBinding) {
|
|
27
27
|
this.#sourceMapManager = sourceMapManager;
|
|
28
28
|
this.#cssWorkspaceBinding = cssWorkspaceBinding;
|
|
29
|
-
this.#project =
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
this.#project =
|
|
30
|
+
new ContentProviderBasedProject(workspace, 'cssSourceMaps:' + target.id(),
|
|
31
|
+
Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
|
|
32
|
+
NetworkProject.setTargetForProject(this.#project, target);
|
|
32
33
|
|
|
33
34
|
this.#bindings = new Map();
|
|
34
35
|
|
|
@@ -24,9 +24,9 @@ export class StylesSourceMapping implements SourceMapping {
|
|
|
24
24
|
constructor(cssModel: SDK.CSSModel.CSSModel, workspace: Workspace.Workspace.WorkspaceImpl) {
|
|
25
25
|
this.#cssModel = cssModel;
|
|
26
26
|
const target = this.#cssModel.target();
|
|
27
|
-
this.#project =
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
this.#project = new ContentProviderBasedProject(
|
|
28
|
+
workspace, 'css:' + target.id(), Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
|
|
29
|
+
NetworkProject.setTargetForProject(this.#project, target);
|
|
30
30
|
|
|
31
31
|
this.#eventListeners = [
|
|
32
32
|
this.#cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, this.styleSheetAdded, this),
|
|
@@ -11,6 +11,8 @@ import * as Protocol from '../../generated/protocol.js';
|
|
|
11
11
|
import * as Geometry from '../geometry/geometry.js';
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
+
type Cutout,
|
|
15
|
+
CutoutShape,
|
|
14
16
|
type EmulatedDevice,
|
|
15
17
|
Horizontal,
|
|
16
18
|
HorizontalSpanned,
|
|
@@ -85,6 +87,13 @@ const UIStrings = {
|
|
|
85
87
|
const str_ = i18n.i18n.registerUIStrings('models/emulation/DeviceModeModel.ts', UIStrings);
|
|
86
88
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
87
89
|
|
|
90
|
+
const CUTOUT_SHAPE_TO_PROTOCOL: Record<CutoutShape, Protocol.Overlay.DisplayCutoutShape> = {
|
|
91
|
+
[CutoutShape.PILL]: Protocol.Overlay.DisplayCutoutShape.Pill,
|
|
92
|
+
[CutoutShape.NOTCH]: Protocol.Overlay.DisplayCutoutShape.Notch,
|
|
93
|
+
[CutoutShape.CIRCLE]: Protocol.Overlay.DisplayCutoutShape.Circle,
|
|
94
|
+
[CutoutShape.RECTANGLE]: Protocol.Overlay.DisplayCutoutShape.Rectangle,
|
|
95
|
+
};
|
|
96
|
+
|
|
88
97
|
export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
|
|
89
98
|
SDK.TargetManager.SDKModelObserver<SDK.EmulationModel.EmulationModel> {
|
|
90
99
|
#screenRect: Rect;
|
|
@@ -506,7 +515,7 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
506
515
|
return;
|
|
507
516
|
}
|
|
508
517
|
|
|
509
|
-
this.
|
|
518
|
+
this.showDeviceOverlaysIfApplicable(overlayModel);
|
|
510
519
|
}
|
|
511
520
|
|
|
512
521
|
private onScreenOrientationLockChanged(
|
|
@@ -638,7 +647,7 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
638
647
|
const mobile = this.isMobile();
|
|
639
648
|
const overlayModel = this.#emulationModel ? this.#emulationModel.overlayModel() : null;
|
|
640
649
|
if (overlayModel) {
|
|
641
|
-
this.
|
|
650
|
+
this.showDeviceOverlaysIfApplicable(overlayModel);
|
|
642
651
|
}
|
|
643
652
|
if (this.#type === Type.Device && this.#device && this.#mode) {
|
|
644
653
|
const orientation = this.#device.orientationByName(this.#mode.orientation);
|
|
@@ -878,14 +887,67 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
878
887
|
}
|
|
879
888
|
}
|
|
880
889
|
|
|
881
|
-
private
|
|
890
|
+
private showDeviceOverlaysIfApplicable(overlayModel: SDK.OverlayModel.OverlayModel): void {
|
|
882
891
|
const orientation = (this.#device && this.#mode) ? this.#device.orientationByName(this.#mode.orientation) : null;
|
|
883
892
|
if (orientation?.hinge) {
|
|
884
893
|
overlayModel.showHingeForDualScreen(orientation.hinge);
|
|
885
|
-
|
|
894
|
+
} else {
|
|
895
|
+
overlayModel.showHingeForDualScreen(null);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
overlayModel.showDisplayCutout(this.currentDisplayCutout());
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
private currentDisplayCutout(): SDK.OverlayModel.DisplayCutout|null {
|
|
902
|
+
const device = this.#device;
|
|
903
|
+
const mode = this.#mode;
|
|
904
|
+
if (!device || !mode || !device.modes.includes(mode)) {
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const cutout = mode.cutout;
|
|
909
|
+
if (cutout) {
|
|
910
|
+
return this.toDisplayCutout(cutout);
|
|
886
911
|
}
|
|
887
912
|
|
|
888
|
-
|
|
913
|
+
if (mode.orientation !== Horizontal) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const rotationPartner = device.getRotationPartner(mode);
|
|
918
|
+
const rotatedCutout = rotationPartner?.cutout;
|
|
919
|
+
if (rotationPartner?.orientation !== Vertical || !rotatedCutout) {
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
const orientation = device.orientationByName(mode.orientation);
|
|
924
|
+
if (rotatedCutout.shape === CutoutShape.CIRCLE) {
|
|
925
|
+
return this.toDisplayCutout({
|
|
926
|
+
...rotatedCutout,
|
|
927
|
+
x: orientation.width - rotatedCutout.y - rotatedCutout.height,
|
|
928
|
+
y: rotatedCutout.x,
|
|
929
|
+
width: rotatedCutout.height,
|
|
930
|
+
height: rotatedCutout.width,
|
|
931
|
+
cx: orientation.width - rotatedCutout.cy,
|
|
932
|
+
cy: rotatedCutout.cx,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
return this.toDisplayCutout({
|
|
936
|
+
...rotatedCutout,
|
|
937
|
+
x: orientation.width - rotatedCutout.y - rotatedCutout.height,
|
|
938
|
+
y: rotatedCutout.x,
|
|
939
|
+
width: rotatedCutout.height,
|
|
940
|
+
height: rotatedCutout.width,
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
private toDisplayCutout(cutout: Cutout): SDK.OverlayModel.DisplayCutout {
|
|
945
|
+
const {shape, ...rest} = cutout;
|
|
946
|
+
return {
|
|
947
|
+
...rest,
|
|
948
|
+
shape: CUTOUT_SHAPE_TO_PROTOCOL[shape],
|
|
949
|
+
contentColor: {r: 0, g: 0, b: 0, a: 1},
|
|
950
|
+
} as SDK.OverlayModel.DisplayCutout;
|
|
889
951
|
}
|
|
890
952
|
|
|
891
953
|
private getDisplayFeatureOrientation(): Protocol.Emulation.DisplayFeatureOrientation {
|
|
@@ -6,22 +6,24 @@ import type * as Platform from '../../core/platform/platform.js';
|
|
|
6
6
|
|
|
7
7
|
import {PrivateAPI} from './ExtensionAPI.js';
|
|
8
8
|
import {ExtensionEndpoint} from './ExtensionEndpoint.js';
|
|
9
|
-
import {RecorderPluginManager} from './RecorderPluginManager.js';
|
|
9
|
+
import type {RecorderPluginManager} from './RecorderPluginManager.js';
|
|
10
10
|
|
|
11
11
|
export class RecorderExtensionEndpoint extends ExtensionEndpoint {
|
|
12
12
|
private readonly name: string;
|
|
13
13
|
private readonly mediaType?: string;
|
|
14
14
|
private readonly capabilities: PrivateAPI.RecordingExtensionPluginCapability[];
|
|
15
15
|
readonly #extensionOrigin: Platform.DevToolsPath.UrlString;
|
|
16
|
+
readonly #recorderPluginManager: RecorderPluginManager;
|
|
16
17
|
|
|
17
|
-
constructor(
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
constructor(name: string, port: MessagePort, capabilities: PrivateAPI.RecordingExtensionPluginCapability[],
|
|
19
|
+
extensionOrigin: Platform.DevToolsPath.UrlString, recorderPluginManager: RecorderPluginManager,
|
|
20
|
+
mediaType?: string) {
|
|
20
21
|
super(port);
|
|
21
22
|
this.name = name;
|
|
22
23
|
this.mediaType = mediaType;
|
|
23
24
|
this.capabilities = capabilities;
|
|
24
25
|
this.#extensionOrigin = extensionOrigin;
|
|
26
|
+
this.#recorderPluginManager = recorderPluginManager;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
getName(): string {
|
|
@@ -44,7 +46,7 @@ export class RecorderExtensionEndpoint extends ExtensionEndpoint {
|
|
|
44
46
|
switch (event) {
|
|
45
47
|
case PrivateAPI.RecorderExtensionPluginEvents.UnregisteredRecorderExtensionPlugin: {
|
|
46
48
|
this.disconnect();
|
|
47
|
-
|
|
49
|
+
this.#recorderPluginManager.removePlugin(this);
|
|
48
50
|
break;
|
|
49
51
|
}
|
|
50
52
|
default:
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
|
+
import * as Root from '../../core/root/root.js';
|
|
6
7
|
|
|
7
8
|
import type {RecorderExtensionEndpoint} from './RecorderExtensionEndpoint.js';
|
|
8
9
|
|
|
9
|
-
let instance: RecorderPluginManager|null = null;
|
|
10
|
-
|
|
11
10
|
export interface ViewDescriptor {
|
|
12
11
|
id: string;
|
|
13
12
|
title: string;
|
|
@@ -21,11 +20,16 @@ export class RecorderPluginManager extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
|
21
20
|
#plugins = new Set<RecorderExtensionEndpoint>();
|
|
22
21
|
#views = new Map<string, ViewDescriptor>();
|
|
23
22
|
|
|
24
|
-
static instance(): RecorderPluginManager {
|
|
25
|
-
if (!
|
|
26
|
-
|
|
23
|
+
static instance(opts?: {forceNew: boolean}): RecorderPluginManager {
|
|
24
|
+
if (!Root.DevToolsContext.globalInstance().has(RecorderPluginManager) || opts?.forceNew) {
|
|
25
|
+
Root.DevToolsContext.globalInstance().set(RecorderPluginManager, new RecorderPluginManager());
|
|
27
26
|
}
|
|
28
|
-
|
|
27
|
+
|
|
28
|
+
return Root.DevToolsContext.globalInstance().get(RecorderPluginManager);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static removeInstance(): void {
|
|
32
|
+
Root.DevToolsContext.globalInstance().delete(RecorderPluginManager);
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
addPlugin(plugin: RecorderExtensionEndpoint): void {
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Title: HAR Log build converts network requests into a complete HAR log with pages and entries
|
|
2
|
+
Content:
|
|
3
|
+
{
|
|
4
|
+
"version": "1.2",
|
|
5
|
+
"creator": {
|
|
6
|
+
"name": "WebInspector",
|
|
7
|
+
"version": "VERSION"
|
|
8
|
+
},
|
|
9
|
+
"pages": [
|
|
10
|
+
{
|
|
11
|
+
"startedDateTime": "1970-01-01T00:00:01.000Z",
|
|
12
|
+
"id": "page_1",
|
|
13
|
+
"title": "http://127.0.0.1:8000/devtools/resources/inspected-page.html",
|
|
14
|
+
"pageTimings": {
|
|
15
|
+
"onContentLoad": 9000,
|
|
16
|
+
"onLoad": 14000
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"entries": [
|
|
21
|
+
{
|
|
22
|
+
"_initiator": null,
|
|
23
|
+
"_priority": null,
|
|
24
|
+
"_resourceType": "document",
|
|
25
|
+
"cache": {},
|
|
26
|
+
"request": {
|
|
27
|
+
"method": "GET",
|
|
28
|
+
"url": "http://127.0.0.1:8000/devtools/resources/inspected-page.html",
|
|
29
|
+
"httpVersion": "HTTP/1.1",
|
|
30
|
+
"headers": [
|
|
31
|
+
{
|
|
32
|
+
"name": "version",
|
|
33
|
+
"value": "HTTP/1.1"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"queryString": [],
|
|
37
|
+
"cookies": [
|
|
38
|
+
{
|
|
39
|
+
"name": "a",
|
|
40
|
+
"value": "b",
|
|
41
|
+
"path": "/path",
|
|
42
|
+
"domain": "example.com",
|
|
43
|
+
"expires": null,
|
|
44
|
+
"httpOnly": false,
|
|
45
|
+
"secure": false
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "a1",
|
|
49
|
+
"value": "b1",
|
|
50
|
+
"expires": null,
|
|
51
|
+
"httpOnly": false,
|
|
52
|
+
"secure": false
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "c1",
|
|
56
|
+
"value": "d1",
|
|
57
|
+
"expires": null,
|
|
58
|
+
"httpOnly": false,
|
|
59
|
+
"secure": false
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"headersSize": -1,
|
|
63
|
+
"bodySize": 0
|
|
64
|
+
},
|
|
65
|
+
"response": {
|
|
66
|
+
"status": 0,
|
|
67
|
+
"statusText": "",
|
|
68
|
+
"httpVersion": "",
|
|
69
|
+
"headers": [
|
|
70
|
+
{
|
|
71
|
+
"name": "Set-Cookie",
|
|
72
|
+
"value": "x=y; Path=/path; Domain=example.com; httpOnly; Secure\nx1=y1; SameSite=Strict\nz2=y2; SameSite=Lax"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"cookies": [
|
|
76
|
+
{
|
|
77
|
+
"name": "x",
|
|
78
|
+
"value": "y",
|
|
79
|
+
"path": "/path",
|
|
80
|
+
"domain": "example.com",
|
|
81
|
+
"expires": null,
|
|
82
|
+
"httpOnly": true,
|
|
83
|
+
"secure": true
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "x1",
|
|
87
|
+
"value": "y1",
|
|
88
|
+
"domain": "127.0.0.1",
|
|
89
|
+
"expires": null,
|
|
90
|
+
"httpOnly": false,
|
|
91
|
+
"secure": false,
|
|
92
|
+
"sameSite": "Strict"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "z2",
|
|
96
|
+
"value": "y2",
|
|
97
|
+
"domain": "127.0.0.1",
|
|
98
|
+
"expires": null,
|
|
99
|
+
"httpOnly": false,
|
|
100
|
+
"secure": false,
|
|
101
|
+
"sameSite": "Lax"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"content": {
|
|
105
|
+
"size": 0,
|
|
106
|
+
"mimeType": "x-unknown"
|
|
107
|
+
},
|
|
108
|
+
"redirectURL": "",
|
|
109
|
+
"headersSize": -1,
|
|
110
|
+
"bodySize": -1,
|
|
111
|
+
"_transferSize": 0,
|
|
112
|
+
"_error": null,
|
|
113
|
+
"_fetchedViaServiceWorker": true,
|
|
114
|
+
"_responseCacheStorageCacheName": "v1",
|
|
115
|
+
"_serviceWorkerResponseSource": "cache-storage",
|
|
116
|
+
"_serviceWorkerRouterRuleIdMatched": 3,
|
|
117
|
+
"_serviceWorkerRouterMatchedSourceType": "cache",
|
|
118
|
+
"_serviceWorkerRouterActualSourceType": "cache"
|
|
119
|
+
},
|
|
120
|
+
"serverIPAddress": "",
|
|
121
|
+
"startedDateTime": "1969-12-31T23:59:59.000Z",
|
|
122
|
+
"time": 0,
|
|
123
|
+
"timings": {
|
|
124
|
+
"blocked": 0,
|
|
125
|
+
"dns": -1,
|
|
126
|
+
"ssl": -1,
|
|
127
|
+
"connect": -1,
|
|
128
|
+
"send": 0,
|
|
129
|
+
"wait": 0,
|
|
130
|
+
"receive": 0,
|
|
131
|
+
"_blocked_queueing": 2000
|
|
132
|
+
},
|
|
133
|
+
"pageref": "page_1"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"_initiator": null,
|
|
137
|
+
"_priority": null,
|
|
138
|
+
"_resourceType": "xhr",
|
|
139
|
+
"cache": {},
|
|
140
|
+
"request": {
|
|
141
|
+
"method": "POST",
|
|
142
|
+
"url": "http://127.0.0.1:8000/devtools/resources/post-target.cgi",
|
|
143
|
+
"httpVersion": "",
|
|
144
|
+
"headers": [
|
|
145
|
+
{
|
|
146
|
+
"name": "Content-Type",
|
|
147
|
+
"value": "text/xml"
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"queryString": [],
|
|
151
|
+
"cookies": [],
|
|
152
|
+
"headersSize": -1,
|
|
153
|
+
"bodySize": 11,
|
|
154
|
+
"postData": {
|
|
155
|
+
"mimeType": "text/xml",
|
|
156
|
+
"text": "<xml></xml>"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"response": {
|
|
160
|
+
"status": 0,
|
|
161
|
+
"statusText": "",
|
|
162
|
+
"httpVersion": "",
|
|
163
|
+
"headers": [],
|
|
164
|
+
"cookies": [],
|
|
165
|
+
"content": {
|
|
166
|
+
"size": 0,
|
|
167
|
+
"mimeType": "application/xml"
|
|
168
|
+
},
|
|
169
|
+
"redirectURL": "",
|
|
170
|
+
"headersSize": -1,
|
|
171
|
+
"bodySize": -1,
|
|
172
|
+
"_transferSize": 0,
|
|
173
|
+
"_error": null,
|
|
174
|
+
"_fetchedViaServiceWorker": false
|
|
175
|
+
},
|
|
176
|
+
"serverIPAddress": "",
|
|
177
|
+
"startedDateTime": "1969-12-31T23:59:59.000Z",
|
|
178
|
+
"time": 0,
|
|
179
|
+
"timings": {
|
|
180
|
+
"blocked": 0,
|
|
181
|
+
"dns": -1,
|
|
182
|
+
"ssl": -1,
|
|
183
|
+
"connect": -1,
|
|
184
|
+
"send": 0,
|
|
185
|
+
"wait": 0,
|
|
186
|
+
"receive": 0,
|
|
187
|
+
"_blocked_queueing": -1
|
|
188
|
+
},
|
|
189
|
+
"pageref": "page_1"
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
=== end content
|