目的
puppeteer
でブラウザアクセスを自動化した上で、Content-Security-Policy-Report-Only レスポンスヘッダが付与された際に report-to
先に正しくレポート内容が送付されたかを確認したいことがありました。
その際に、Developer Tools では以下の画面のように開かせたい、という動きを実装します。
Chrome で Developer Tools を開き、表示したい内容に遷移する
例えば、以下のように Developer Tools タブやメニューを選択し、表示サイズ変更などを行います。
Preferences
ファイルを確認する
以下のファイルに先ほど指定した内容が反映されています。
cat ~/Library/Application\ Support/Google/Chrome/Default/Preferences | jq '.devtools'
preferences.json
{
"adb_key": "...",
"port_forwarding_config": {
"8080": "localhost:8080"
},
"port_forwarding_default_set": true,
"preferences": {
"Inspector.drawerSplitViewState": "{\"horizontal\":{\"size\":423.5145129011871,\"showMode\":\"OnlyMain\"}}",
"InspectorView.screencastSplitViewState": "{\"vertical\":{\"size\":0}}",
"InspectorView.splitViewState": "{\"vertical\":{\"size\":1041.4018410336284}}",
"Styles-pane-sidebar-tabOrder": "{\"Styles\":10,\"Computed\":20}",
"closeableTabs": "{\"security\":true,\"chrome_recorder\":true,\"performance_insights\":true,\"issues-pane\":true,\"release-note\":true}",
"console.sidebar.width": "{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}",
"console.sidebarSelectedFilter": "\"message\"",
"consoleShowSettingsToolbar": "false",
"currentDockState": "\"right\"",
"dataGrid-networkLog-columnWeights": "{\"name\":17.25083329421154,\"path\":6,\"url\":10.391061452513966,\"method\":6,\"status\":6,\"protocol\":6,\"scheme\":6,\"domain\":6,\"remoteaddress\":10,\"remoteaddress-space\":10,\"type\":6,\"initiator\":10,\"initiator-address-space\":10,\"cookies\":6,\"setcookies\":6,\"size\":6,\"time\":4.358105253274494,\"priority\":6,\"connectionid\":6,\"cache-control\":6,\"connection\":6,\"content-encoding\":6,\"content-length\":6,\"etag\":6,\"keep-alive\":6,\"last-modified\":6,\"server\":6,\"vary\":6,\"waterfall\":6}",
"drawer-view-selectedTab": "\"console-view\"",
"drawer-view-tabOrder": "{\"console-view\":10,\"issues-pane\":20,\"release-note\":30}",
"elements.styles.sidebar.width": "{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}",
"emulation.deviceHeight": "0",
"emulation.deviceModeValue": "{\"device\":\"\",\"orientation\":\"\",\"mode\":\"\"}",
"emulation.deviceScale": "1",
"emulation.deviceScaleFactor": "0",
"emulation.deviceUA": "\"Mobile\"",
"emulation.deviceWidth": "400",
"emulation.showDeviceMode": "false",
"emulation.showDeviceOutline": "false",
"emulation.showDeviceScaleFactor": "false",
"emulation.showRulers": "false",
"emulation.showUserAgentType": "false",
"inspectorVersion": "35",
"messageLevelFilters": "{\"verbose\":true,\"info\":true,\"warning\":true,\"error\":true}",
"navigator-view-selectedTab": "\"navigator-network\"",
"networkLogColumns": "{\"name\":{\"visible\":true},\"path\":{\"visible\":false},\"url\":{\"visible\":true},\"method\":{\"visible\":false},\"status\":{\"visible\":true},\"protocol\":{\"visible\":true},\"scheme\":{\"visible\":false},\"domain\":{\"visible\":false},\"remoteaddress\":{\"visible\":false},\"remoteaddress-space\":{\"visible\":false},\"type\":{\"visible\":true},\"initiator\":{\"visible\":true},\"initiator-address-space\":{\"visible\":false},\"cookies\":{\"visible\":false},\"setcookies\":{\"visible\":false},\"size\":{\"visible\":true},\"time\":{\"visible\":true},\"priority\":{\"visible\":false},\"connectionid\":{\"visible\":false},\"cache-control\":{\"visible\":false},\"connection\":{\"visible\":false},\"content-encoding\":{\"visible\":false},\"content-length\":{\"visible\":false},\"etag\":{\"visible\":false},\"keep-alive\":{\"visible\":false},\"last-modified\":{\"visible\":false},\"server\":{\"visible\":false},\"vary\":{\"visible\":false},\"waterfall\":{\"visible\":false}}",
"networkPanelSidebarState": "{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}",
"networkPanelSplitViewState": "{\"vertical\":{\"size\":165}}",
"networkPanelSplitViewWaterfall": "{\"vertical\":{\"size\":0}}",
"networkResourceTypeFilters": "{\"Other\":true}",
"networkTextFilter": "\"\"",
"panel-selectedTab": "\"resources\"",
"panel-tabOrder": "{\"resources\":10,\"console\":20,\"network\":30,\"elements\":40,\"chrome_recorder\":50,\"performance_insights\":60,\"sources\":70,\"timeline\":80,\"heap_profiler\":90,\"security\":100,\"lighthouse\":110,\"chrome-extension://pibckhncbjabhlomohgcdedacopeoomlDr.FLARE\":111}",
"releaseNoteVersionSeen": "56",
"request-info-formData-category-expanded": "true",
"request-info-general-category-expanded": "true",
"request-info-queryString-category-expanded": "true",
"request-info-requestHeaders-category-expanded": "true",
"request-info-requestPayload-category-expanded": "true",
"request-info-responseHeaders-category-expanded": "true",
"resourceViewTab": "\"response\"",
"resourcesLastSelectedElementPath": "[\"reportingApi://\"]",
"selectedProfileType": "\"HEAP\"",
"showMediaQueryInspector": "false",
"sourcesPanelNavigatorSplitViewState": "{\"vertical\":{\"size\":0,\"showMode\":\"Both\"}}",
"sourcesPanelSplitViewState": "{\"vertical\":{\"size\":0,\"showMode\":\"Both\"},\"horizontal\":{\"size\":0,\"showMode\":\"Both\"}}",
"standardEmulatedDeviceList": "[{...}]",
"timelineCountersSplitViewState": "{\"horizontal\":{\"size\":0}}",
"timelineFlamechartMainAndVitalsView": "{\"horizontal\":{\"size\":0}}",
"timelineStartCoverage": "false",
"timelineTreeViewDetailsSplitWidget": "{\"vertical\":{\"size\":0}}",
"undefined-tabOrder": "{\"Styles\":10,\"Computed\":20,\"sources.scopeChain\":21,\"sources.watch\":22}"
},
"synced_preferences_sync_disabled": {
"adornerSettings": "[{...}]",
"colorFormat": "\"original\"",
"emulation.locations": "[{...}]",
"syncedInspectorVersion": "35"
}
}
puppeteer-extra
のプラグインを使う
puppeteer-extra
の user-preferences
プラグインを活用します。
- berstend/puppeteer-extra: 💯 Teach puppeteer new tricks through plugins.
- puppeteer-extra/packages/puppeteer-extra-plugin-user-preferences at master · berstend/puppeteer-extra
先ほどの preferences.json
から使いたい設定値をとってきます。
以下の指定により、目的のの画面内容を指定できます。
-
panel-selectedTab
-->resources
-
resourcesLastSelectedElementPath
-->reportingApi://
以下のような puppeteer.js
スクリプトを実行すると
puppeteer.js
const puppeteer = require('puppeteer-extra');
const ppUserPrefs = require('puppeteer-extra-plugin-user-preferences');
puppeteer.use(ppUserPrefs({
userPrefs: {
devtools: {
preferences: {
'InspectorView.splitViewState': JSON.stringify({
vertical: { size: 1500 },
horizontal: { size: 0 },
}),
uiTheme: '"dark"',
'panel-selectedTab': '"resources"',
'resourcesLastSelectedElementPath': JSON.stringify([
"reportingApi://"
])
},
},
},
}));
puppeteer.launch({
headless: false,
devtools: true,
product: 'chrome',
args: [
"--short-reporting-delay",
"--auto-open-devtools-for-tabs"
]
}).then(async browser => {
//const page = (await browser.pages())[0];
const page = await browser.newPage();
const response = await page.goto('https://try.cloudflare.com/', {
waitUntil: "load",
});
const headers = response.headers();
await page.waitForTimeout(5000);
await page.close();
await browser.close();
});
PUPPETEER_PRODUCT=chrome \
PUPPETEER_EXECUTABLE_PATH=/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
node puppeteer.js
意図した挙動にできたことが確認できます。
これで CSP レポーティングのテストがかなりわかりやすくなりました。