1 | /* |
2 | * Copyright (C) 2011 Google Inc. All rights reserved. |
3 | * Copyright (C) 2015-2017 Apple Inc. All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are |
7 | * met: |
8 | * |
9 | * * Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * * Redistributions in binary form must reproduce the above |
12 | * copyright notice, this list of conditions and the following disclaimer |
13 | * in the documentation and/or other materials provided with the |
14 | * distribution. |
15 | * * Neither the name of Google Inc. nor the names of its |
16 | * contributors may be used to endorse or promote products derived from |
17 | * this software without specific prior written permission. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include "config.h" |
33 | #include "InspectorPageAgent.h" |
34 | |
35 | #include "CachedResource.h" |
36 | #include "CachedResourceLoader.h" |
37 | #include "Cookie.h" |
38 | #include "CookieJar.h" |
39 | #include "CustomHeaderFields.h" |
40 | #include "Document.h" |
41 | #include "DocumentLoader.h" |
42 | #include "Frame.h" |
43 | #include "FrameLoadRequest.h" |
44 | #include "FrameLoader.h" |
45 | #include "FrameSnapshotting.h" |
46 | #include "FrameView.h" |
47 | #include "HTMLFrameOwnerElement.h" |
48 | #include "HTMLNames.h" |
49 | #include "ImageBuffer.h" |
50 | #include "InspectorClient.h" |
51 | #include "InspectorDOMAgent.h" |
52 | #include "InspectorNetworkAgent.h" |
53 | #include "InspectorOverlay.h" |
54 | #include "InstrumentingAgents.h" |
55 | #include "MIMETypeRegistry.h" |
56 | #include "MemoryCache.h" |
57 | #include "Page.h" |
58 | #include "RenderObject.h" |
59 | #include "RenderTheme.h" |
60 | #include "ScriptController.h" |
61 | #include "SecurityOrigin.h" |
62 | #include "Settings.h" |
63 | #include "StyleScope.h" |
64 | #include "TextEncoding.h" |
65 | #include "UserGestureIndicator.h" |
66 | #include <JavaScriptCore/ContentSearchUtilities.h> |
67 | #include <JavaScriptCore/IdentifiersFactory.h> |
68 | #include <JavaScriptCore/RegularExpression.h> |
69 | #include <wtf/ListHashSet.h> |
70 | #include <wtf/Stopwatch.h> |
71 | #include <wtf/text/Base64.h> |
72 | #include <wtf/text/StringBuilder.h> |
73 | |
74 | #if ENABLE(APPLICATION_MANIFEST) |
75 | #include "CachedApplicationManifest.h" |
76 | #endif |
77 | |
78 | #if ENABLE(WEB_ARCHIVE) && USE(CF) |
79 | #include "LegacyWebArchive.h" |
80 | #endif |
81 | |
82 | |
83 | namespace WebCore { |
84 | |
85 | using namespace Inspector; |
86 | |
87 | // Keep this in sync with Page.Setting |
88 | #define FOR_EACH_INSPECTOR_OVERRIDE_SETTING(macro) \ |
89 | macro(AuthorAndUserStylesEnabled) \ |
90 | macro(ICECandidateFilteringEnabled) \ |
91 | macro(ImagesEnabled) \ |
92 | macro(MediaCaptureRequiresSecureConnection) \ |
93 | macro(MockCaptureDevicesEnabled) \ |
94 | macro(NeedsSiteSpecificQuirks) \ |
95 | macro(ScriptEnabled) \ |
96 | macro(WebSecurityEnabled) |
97 | |
98 | static bool decodeBuffer(const char* buffer, unsigned size, const String& textEncodingName, String* result) |
99 | { |
100 | if (buffer) { |
101 | TextEncoding encoding(textEncodingName); |
102 | if (!encoding.isValid()) |
103 | encoding = WindowsLatin1Encoding(); |
104 | *result = encoding.decode(buffer, size); |
105 | return true; |
106 | } |
107 | return false; |
108 | } |
109 | |
110 | bool InspectorPageAgent::mainResourceContent(Frame* frame, bool withBase64Encode, String* result) |
111 | { |
112 | RefPtr<SharedBuffer> buffer = frame->loader().documentLoader()->mainResourceData(); |
113 | if (!buffer) |
114 | return false; |
115 | return InspectorPageAgent::dataContent(buffer->data(), buffer->size(), frame->document()->encoding(), withBase64Encode, result); |
116 | } |
117 | |
118 | bool InspectorPageAgent::sharedBufferContent(RefPtr<SharedBuffer>&& buffer, const String& textEncodingName, bool withBase64Encode, String* result) |
119 | { |
120 | return dataContent(buffer ? buffer->data() : nullptr, buffer ? buffer->size() : 0, textEncodingName, withBase64Encode, result); |
121 | } |
122 | |
123 | bool InspectorPageAgent::dataContent(const char* data, unsigned size, const String& textEncodingName, bool withBase64Encode, String* result) |
124 | { |
125 | if (withBase64Encode) { |
126 | *result = base64Encode(data, size); |
127 | return true; |
128 | } |
129 | |
130 | return decodeBuffer(data, size, textEncodingName, result); |
131 | } |
132 | |
133 | Vector<CachedResource*> InspectorPageAgent::cachedResourcesForFrame(Frame* frame) |
134 | { |
135 | Vector<CachedResource*> result; |
136 | |
137 | for (auto& cachedResourceHandle : frame->document()->cachedResourceLoader().allCachedResources().values()) { |
138 | auto* cachedResource = cachedResourceHandle.get(); |
139 | if (cachedResource->resourceRequest().hiddenFromInspector()) |
140 | continue; |
141 | |
142 | switch (cachedResource->type()) { |
143 | case CachedResource::Type::ImageResource: |
144 | // Skip images that were not auto loaded (images disabled in the user agent). |
145 | #if ENABLE(SVG_FONTS) |
146 | case CachedResource::Type::SVGFontResource: |
147 | #endif |
148 | case CachedResource::Type::FontResource: |
149 | // Skip fonts that were referenced in CSS but never used/downloaded. |
150 | if (cachedResource->stillNeedsLoad()) |
151 | continue; |
152 | break; |
153 | default: |
154 | // All other CachedResource types download immediately. |
155 | break; |
156 | } |
157 | |
158 | result.append(cachedResource); |
159 | } |
160 | |
161 | return result; |
162 | } |
163 | |
164 | void InspectorPageAgent::resourceContent(ErrorString& errorString, Frame* frame, const URL& url, String* result, bool* base64Encoded) |
165 | { |
166 | DocumentLoader* loader = assertDocumentLoader(errorString, frame); |
167 | if (!loader) |
168 | return; |
169 | |
170 | RefPtr<SharedBuffer> buffer; |
171 | bool success = false; |
172 | if (equalIgnoringFragmentIdentifier(url, loader->url())) { |
173 | *base64Encoded = false; |
174 | success = mainResourceContent(frame, *base64Encoded, result); |
175 | } |
176 | |
177 | if (!success) { |
178 | if (auto* resource = cachedResource(frame, url)) |
179 | success = InspectorNetworkAgent::cachedResourceContent(*resource, result, base64Encoded); |
180 | } |
181 | |
182 | if (!success) |
183 | errorString = "No resource with given URL found"_s ; |
184 | } |
185 | |
186 | String InspectorPageAgent::sourceMapURLForResource(CachedResource* cachedResource) |
187 | { |
188 | if (!cachedResource) |
189 | return String(); |
190 | |
191 | // Scripts are handled in a separate path. |
192 | if (cachedResource->type() != CachedResource::Type::CSSStyleSheet) |
193 | return String(); |
194 | |
195 | String = cachedResource->response().httpHeaderField(HTTPHeaderName::SourceMap); |
196 | if (!sourceMapHeader.isEmpty()) |
197 | return sourceMapHeader; |
198 | |
199 | sourceMapHeader = cachedResource->response().httpHeaderField(HTTPHeaderName::XSourceMap); |
200 | if (!sourceMapHeader.isEmpty()) |
201 | return sourceMapHeader; |
202 | |
203 | String content; |
204 | bool base64Encoded; |
205 | if (InspectorNetworkAgent::cachedResourceContent(*cachedResource, &content, &base64Encoded) && !base64Encoded) |
206 | return ContentSearchUtilities::findStylesheetSourceMapURL(content); |
207 | |
208 | return String(); |
209 | } |
210 | |
211 | CachedResource* InspectorPageAgent::cachedResource(Frame* frame, const URL& url) |
212 | { |
213 | if (url.isNull()) |
214 | return nullptr; |
215 | |
216 | CachedResource* cachedResource = frame->document()->cachedResourceLoader().cachedResource(MemoryCache::removeFragmentIdentifierIfNeeded(url)); |
217 | if (!cachedResource) { |
218 | ResourceRequest request(url); |
219 | request.setDomainForCachePartition(frame->document()->domainForCachePartition()); |
220 | cachedResource = MemoryCache::singleton().resourceForRequest(request, frame->page()->sessionID()); |
221 | } |
222 | |
223 | return cachedResource; |
224 | } |
225 | |
226 | Inspector::Protocol::Page::ResourceType InspectorPageAgent::resourceTypeJSON(InspectorPageAgent::ResourceType resourceType) |
227 | { |
228 | switch (resourceType) { |
229 | case DocumentResource: |
230 | return Inspector::Protocol::Page::ResourceType::Document; |
231 | case ImageResource: |
232 | return Inspector::Protocol::Page::ResourceType::Image; |
233 | case FontResource: |
234 | return Inspector::Protocol::Page::ResourceType::Font; |
235 | case StylesheetResource: |
236 | return Inspector::Protocol::Page::ResourceType::Stylesheet; |
237 | case ScriptResource: |
238 | return Inspector::Protocol::Page::ResourceType::Script; |
239 | case XHRResource: |
240 | return Inspector::Protocol::Page::ResourceType::XHR; |
241 | case FetchResource: |
242 | return Inspector::Protocol::Page::ResourceType::Fetch; |
243 | case PingResource: |
244 | return Inspector::Protocol::Page::ResourceType::Ping; |
245 | case BeaconResource: |
246 | return Inspector::Protocol::Page::ResourceType::Beacon; |
247 | case WebSocketResource: |
248 | return Inspector::Protocol::Page::ResourceType::WebSocket; |
249 | case OtherResource: |
250 | return Inspector::Protocol::Page::ResourceType::Other; |
251 | #if ENABLE(APPLICATION_MANIFEST) |
252 | case ApplicationManifestResource: |
253 | break; |
254 | #endif |
255 | } |
256 | return Inspector::Protocol::Page::ResourceType::Other; |
257 | } |
258 | |
259 | InspectorPageAgent::ResourceType InspectorPageAgent::inspectorResourceType(CachedResource::Type type) |
260 | { |
261 | switch (type) { |
262 | case CachedResource::Type::ImageResource: |
263 | return InspectorPageAgent::ImageResource; |
264 | #if ENABLE(SVG_FONTS) |
265 | case CachedResource::Type::SVGFontResource: |
266 | #endif |
267 | case CachedResource::Type::FontResource: |
268 | return InspectorPageAgent::FontResource; |
269 | #if ENABLE(XSLT) |
270 | case CachedResource::Type::XSLStyleSheet: |
271 | #endif |
272 | case CachedResource::Type::CSSStyleSheet: |
273 | return InspectorPageAgent::StylesheetResource; |
274 | case CachedResource::Type::Script: |
275 | return InspectorPageAgent::ScriptResource; |
276 | case CachedResource::Type::MainResource: |
277 | return InspectorPageAgent::DocumentResource; |
278 | case CachedResource::Type::Beacon: |
279 | return InspectorPageAgent::BeaconResource; |
280 | #if ENABLE(APPLICATION_MANIFEST) |
281 | case CachedResource::Type::ApplicationManifest: |
282 | return InspectorPageAgent::ApplicationManifestResource; |
283 | #endif |
284 | case CachedResource::Type::Ping: |
285 | return InspectorPageAgent::PingResource; |
286 | case CachedResource::Type::MediaResource: |
287 | case CachedResource::Type::Icon: |
288 | case CachedResource::Type::RawResource: |
289 | default: |
290 | return InspectorPageAgent::OtherResource; |
291 | } |
292 | } |
293 | |
294 | InspectorPageAgent::ResourceType InspectorPageAgent::inspectorResourceType(const CachedResource& cachedResource) |
295 | { |
296 | if (cachedResource.type() == CachedResource::Type::RawResource) { |
297 | switch (cachedResource.resourceRequest().requester()) { |
298 | case ResourceRequest::Requester::Fetch: |
299 | return InspectorPageAgent::FetchResource; |
300 | case ResourceRequest::Requester::Main: |
301 | return InspectorPageAgent::DocumentResource; |
302 | default: |
303 | return InspectorPageAgent::XHRResource; |
304 | } |
305 | } |
306 | |
307 | return inspectorResourceType(cachedResource.type()); |
308 | } |
309 | |
310 | Inspector::Protocol::Page::ResourceType InspectorPageAgent::cachedResourceTypeJSON(const CachedResource& cachedResource) |
311 | { |
312 | return resourceTypeJSON(inspectorResourceType(cachedResource)); |
313 | } |
314 | |
315 | Frame* InspectorPageAgent::findFrameWithSecurityOrigin(Page& page, const String& originRawString) |
316 | { |
317 | for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { |
318 | if (frame->document()->securityOrigin().toRawString() == originRawString) |
319 | return frame; |
320 | } |
321 | return nullptr; |
322 | } |
323 | |
324 | DocumentLoader* InspectorPageAgent::assertDocumentLoader(ErrorString& errorString, Frame* frame) |
325 | { |
326 | FrameLoader& frameLoader = frame->loader(); |
327 | DocumentLoader* documentLoader = frameLoader.documentLoader(); |
328 | if (!documentLoader) |
329 | errorString = "No documentLoader for given frame found"_s ; |
330 | return documentLoader; |
331 | } |
332 | |
333 | InspectorPageAgent::InspectorPageAgent(PageAgentContext& context, InspectorClient* client, InspectorOverlay* overlay) |
334 | : InspectorAgentBase("Page"_s , context) |
335 | , m_frontendDispatcher(std::make_unique<Inspector::PageFrontendDispatcher>(context.frontendRouter)) |
336 | , m_backendDispatcher(Inspector::PageBackendDispatcher::create(context.backendDispatcher, this)) |
337 | , m_inspectedPage(context.inspectedPage) |
338 | , m_client(client) |
339 | , m_overlay(overlay) |
340 | { |
341 | } |
342 | |
343 | void InspectorPageAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*) |
344 | { |
345 | } |
346 | |
347 | void InspectorPageAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason) |
348 | { |
349 | ErrorString unused; |
350 | disable(unused); |
351 | } |
352 | |
353 | double InspectorPageAgent::timestamp() |
354 | { |
355 | return m_environment.executionStopwatch()->elapsedTime().seconds(); |
356 | } |
357 | |
358 | void InspectorPageAgent::enable(ErrorString&) |
359 | { |
360 | if (m_instrumentingAgents.inspectorPageAgent() == this) |
361 | return; |
362 | |
363 | m_instrumentingAgents.setInspectorPageAgent(this); |
364 | |
365 | auto stopwatch = m_environment.executionStopwatch(); |
366 | stopwatch->reset(); |
367 | stopwatch->start(); |
368 | |
369 | #if HAVE(OS_DARK_MODE_SUPPORT) |
370 | defaultAppearanceDidChange(m_inspectedPage.defaultUseDarkAppearance()); |
371 | #endif |
372 | } |
373 | |
374 | void InspectorPageAgent::disable(ErrorString&) |
375 | { |
376 | ErrorString unused; |
377 | setShowPaintRects(unused, false); |
378 | setShowRulers(unused, false); |
379 | overrideUserAgent(unused, nullptr); |
380 | setEmulatedMedia(unused, emptyString()); |
381 | setForcedAppearance(unused, emptyString()); |
382 | |
383 | #define DISABLE_INSPECTOR_OVERRIDE_SETTING(name) \ |
384 | m_inspectedPage.settings().set##name##InspectorOverride(WTF::nullopt); |
385 | |
386 | FOR_EACH_INSPECTOR_OVERRIDE_SETTING(DISABLE_INSPECTOR_OVERRIDE_SETTING) |
387 | |
388 | #undef DISABLE_INSPECTOR_OVERRIDE_SETTING |
389 | |
390 | m_instrumentingAgents.setInspectorPageAgent(nullptr); |
391 | } |
392 | |
393 | void InspectorPageAgent::reload(ErrorString&, const bool* optionalReloadFromOrigin, const bool* optionalRevalidateAllResources) |
394 | { |
395 | bool reloadFromOrigin = optionalReloadFromOrigin && *optionalReloadFromOrigin; |
396 | bool revalidateAllResources = optionalRevalidateAllResources && *optionalRevalidateAllResources; |
397 | |
398 | OptionSet<ReloadOption> reloadOptions; |
399 | if (reloadFromOrigin) |
400 | reloadOptions.add(ReloadOption::FromOrigin); |
401 | if (!revalidateAllResources) |
402 | reloadOptions.add(ReloadOption::ExpiredOnly); |
403 | |
404 | m_inspectedPage.mainFrame().loader().reload(reloadOptions); |
405 | } |
406 | |
407 | void InspectorPageAgent::navigate(ErrorString&, const String& url) |
408 | { |
409 | UserGestureIndicator indicator { ProcessingUserGesture }; |
410 | Frame& frame = m_inspectedPage.mainFrame(); |
411 | |
412 | ResourceRequest resourceRequest { frame.document()->completeURL(url) }; |
413 | FrameLoadRequest frameLoadRequest { *frame.document(), frame.document()->securityOrigin(), resourceRequest, "_self"_s , LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::No, NewFrameOpenerPolicy::Allow, ShouldOpenExternalURLsPolicy::ShouldNotAllow, InitiatedByMainFrame::Unknown }; |
414 | frame.loader().changeLocation(WTFMove(frameLoadRequest)); |
415 | } |
416 | |
417 | void InspectorPageAgent::overrideUserAgent(ErrorString&, const String* value) |
418 | { |
419 | m_userAgentOverride = value ? *value : String(); |
420 | } |
421 | |
422 | void InspectorPageAgent::overrideSetting(ErrorString& errorString, const String& settingString, const bool* value) |
423 | { |
424 | if (settingString.isEmpty()) { |
425 | errorString = "Preference is empty"_s ; |
426 | return; |
427 | } |
428 | |
429 | auto setting = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::Page::Setting>(settingString); |
430 | if (!setting) { |
431 | errorString = makeString("Unknown setting: "_s , settingString); |
432 | return; |
433 | } |
434 | |
435 | switch (setting.value()) { |
436 | #define CASE_INSPECTOR_OVERRIDE_SETTING(name) \ |
437 | case Inspector::Protocol::Page::Setting::name: { \ |
438 | if (value) \ |
439 | m_inspectedPage.settings().set##name##InspectorOverride(*value); \ |
440 | else \ |
441 | m_inspectedPage.settings().set##name##InspectorOverride(WTF::nullopt); \ |
442 | return; \ |
443 | } \ |
444 | |
445 | FOR_EACH_INSPECTOR_OVERRIDE_SETTING(CASE_INSPECTOR_OVERRIDE_SETTING) |
446 | |
447 | #undef CASE_INSPECTOR_OVERRIDE_SETTING |
448 | } |
449 | |
450 | ASSERT_NOT_REACHED(); |
451 | } |
452 | |
453 | static Inspector::Protocol::Page::CookieSameSitePolicy cookieSameSitePolicyJSON(Cookie::SameSitePolicy policy) |
454 | { |
455 | switch (policy) { |
456 | case Cookie::SameSitePolicy::None: |
457 | return Inspector::Protocol::Page::CookieSameSitePolicy::None; |
458 | case Cookie::SameSitePolicy::Lax: |
459 | return Inspector::Protocol::Page::CookieSameSitePolicy::Lax; |
460 | case Cookie::SameSitePolicy::Strict: |
461 | return Inspector::Protocol::Page::CookieSameSitePolicy::Strict; |
462 | } |
463 | ASSERT_NOT_REACHED(); |
464 | return Inspector::Protocol::Page::CookieSameSitePolicy::None; |
465 | } |
466 | |
467 | static Ref<Inspector::Protocol::Page::Cookie> buildObjectForCookie(const Cookie& cookie) |
468 | { |
469 | return Inspector::Protocol::Page::Cookie::create() |
470 | .setName(cookie.name) |
471 | .setValue(cookie.value) |
472 | .setDomain(cookie.domain) |
473 | .setPath(cookie.path) |
474 | .setExpires(cookie.expires) |
475 | .setSize((cookie.name.length() + cookie.value.length())) |
476 | .setHttpOnly(cookie.httpOnly) |
477 | .setSecure(cookie.secure) |
478 | .setSession(cookie.session) |
479 | .setSameSite(cookieSameSitePolicyJSON(cookie.sameSite)) |
480 | .release(); |
481 | } |
482 | |
483 | static Ref<JSON::ArrayOf<Inspector::Protocol::Page::Cookie>> buildArrayForCookies(ListHashSet<Cookie>& cookiesList) |
484 | { |
485 | auto cookies = JSON::ArrayOf<Inspector::Protocol::Page::Cookie>::create(); |
486 | |
487 | for (const auto& cookie : cookiesList) |
488 | cookies->addItem(buildObjectForCookie(cookie)); |
489 | |
490 | return cookies; |
491 | } |
492 | |
493 | static Vector<URL> allResourcesURLsForFrame(Frame* frame) |
494 | { |
495 | Vector<URL> result; |
496 | |
497 | result.append(frame->loader().documentLoader()->url()); |
498 | |
499 | for (auto* cachedResource : InspectorPageAgent::cachedResourcesForFrame(frame)) |
500 | result.append(cachedResource->url()); |
501 | |
502 | return result; |
503 | } |
504 | |
505 | void InspectorPageAgent::getCookies(ErrorString&, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::Cookie>>& cookies) |
506 | { |
507 | // If we can get raw cookies. |
508 | ListHashSet<Cookie> rawCookiesList; |
509 | |
510 | // If we can't get raw cookies - fall back to String representation |
511 | StringBuilder stringCookiesList; |
512 | |
513 | // Return value to getRawCookies should be the same for every call because |
514 | // the return value is platform/network backend specific, and the call will |
515 | // always return the same true/false value. |
516 | bool rawCookiesImplemented = false; |
517 | |
518 | for (Frame* frame = &m_inspectedPage.mainFrame(); frame; frame = frame->tree().traverseNext()) { |
519 | Document* document = frame->document(); |
520 | if (!document || !document->page()) |
521 | continue; |
522 | |
523 | for (auto& url : allResourcesURLsForFrame(frame)) { |
524 | Vector<Cookie> docCookiesList; |
525 | rawCookiesImplemented = document->page()->cookieJar().getRawCookies(*document, URL({ }, url), docCookiesList); |
526 | |
527 | if (!rawCookiesImplemented) { |
528 | // FIXME: We need duplication checking for the String representation of cookies. |
529 | // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here |
530 | // because "document" is the document of the main frame of the page. |
531 | stringCookiesList.append(document->cookie().releaseReturnValue()); |
532 | } else { |
533 | for (auto& cookie : docCookiesList) |
534 | rawCookiesList.add(cookie); |
535 | } |
536 | } |
537 | } |
538 | |
539 | // FIXME: Do not return empty string/empty array. Make returns optional instead. https://bugs.webkit.org/show_bug.cgi?id=80855 |
540 | if (rawCookiesImplemented) |
541 | cookies = buildArrayForCookies(rawCookiesList); |
542 | else |
543 | cookies = JSON::ArrayOf<Inspector::Protocol::Page::Cookie>::create(); |
544 | } |
545 | |
546 | void InspectorPageAgent::deleteCookie(ErrorString&, const String& cookieName, const String& url) |
547 | { |
548 | URL parsedURL({ }, url); |
549 | for (Frame* frame = &m_inspectedPage.mainFrame(); frame; frame = frame->tree().traverseNext()) { |
550 | if (auto* document = frame->document()) { |
551 | if (auto* page = document->page()) |
552 | page->cookieJar().deleteCookie(*document, parsedURL, cookieName); |
553 | } |
554 | } |
555 | } |
556 | |
557 | void InspectorPageAgent::getResourceTree(ErrorString&, RefPtr<Inspector::Protocol::Page::FrameResourceTree>& object) |
558 | { |
559 | object = buildObjectForFrameTree(&m_inspectedPage.mainFrame()); |
560 | } |
561 | |
562 | void InspectorPageAgent::getResourceContent(ErrorString& errorString, const String& frameId, const String& url, String* content, bool* base64Encoded) |
563 | { |
564 | Frame* frame = assertFrame(errorString, frameId); |
565 | if (!frame) |
566 | return; |
567 | |
568 | resourceContent(errorString, frame, URL({ }, url), content, base64Encoded); |
569 | } |
570 | |
571 | void InspectorPageAgent::searchInResource(ErrorString& errorString, const String& frameId, const String& url, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, const String* optionalRequestId, RefPtr<JSON::ArrayOf<Inspector::Protocol::GenericTypes::SearchMatch>>& results) |
572 | { |
573 | results = JSON::ArrayOf<Inspector::Protocol::GenericTypes::SearchMatch>::create(); |
574 | |
575 | bool isRegex = optionalIsRegex ? *optionalIsRegex : false; |
576 | bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false; |
577 | |
578 | if (optionalRequestId) { |
579 | if (InspectorNetworkAgent* networkAgent = m_instrumentingAgents.inspectorNetworkAgent()) { |
580 | networkAgent->searchInRequest(errorString, *optionalRequestId, query, caseSensitive, isRegex, results); |
581 | return; |
582 | } |
583 | } |
584 | |
585 | Frame* frame = assertFrame(errorString, frameId); |
586 | if (!frame) |
587 | return; |
588 | |
589 | DocumentLoader* loader = assertDocumentLoader(errorString, frame); |
590 | if (!loader) |
591 | return; |
592 | |
593 | URL kurl({ }, url); |
594 | |
595 | String content; |
596 | bool success = false; |
597 | if (equalIgnoringFragmentIdentifier(kurl, loader->url())) |
598 | success = mainResourceContent(frame, false, &content); |
599 | |
600 | if (!success) { |
601 | if (auto* resource = cachedResource(frame, kurl)) { |
602 | if (auto textContent = InspectorNetworkAgent::textContentForCachedResource(*resource)) { |
603 | content = *textContent; |
604 | success = true; |
605 | } |
606 | } |
607 | } |
608 | |
609 | if (!success) |
610 | return; |
611 | |
612 | results = ContentSearchUtilities::searchInTextByLines(content, query, caseSensitive, isRegex); |
613 | } |
614 | |
615 | static Ref<Inspector::Protocol::Page::SearchResult> buildObjectForSearchResult(const String& frameId, const String& url, int matchesCount) |
616 | { |
617 | return Inspector::Protocol::Page::SearchResult::create() |
618 | .setUrl(url) |
619 | .setFrameId(frameId) |
620 | .setMatchesCount(matchesCount) |
621 | .release(); |
622 | } |
623 | |
624 | void InspectorPageAgent::searchInResources(ErrorString&, const String& text, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::SearchResult>>& result) |
625 | { |
626 | result = JSON::ArrayOf<Inspector::Protocol::Page::SearchResult>::create(); |
627 | |
628 | bool isRegex = optionalIsRegex ? *optionalIsRegex : false; |
629 | bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false; |
630 | JSC::Yarr::RegularExpression regex = ContentSearchUtilities::createSearchRegex(text, caseSensitive, isRegex); |
631 | |
632 | for (Frame* frame = &m_inspectedPage.mainFrame(); frame; frame = frame->tree().traverseNext()) { |
633 | for (auto* cachedResource : cachedResourcesForFrame(frame)) { |
634 | if (auto textContent = InspectorNetworkAgent::textContentForCachedResource(*cachedResource)) { |
635 | int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, *textContent); |
636 | if (matchesCount) |
637 | result->addItem(buildObjectForSearchResult(frameId(frame), cachedResource->url(), matchesCount)); |
638 | } |
639 | } |
640 | } |
641 | |
642 | if (InspectorNetworkAgent* networkAgent = m_instrumentingAgents.inspectorNetworkAgent()) |
643 | networkAgent->searchOtherRequests(regex, result); |
644 | } |
645 | |
646 | void InspectorPageAgent::setShowRulers(ErrorString&, bool showRulers) |
647 | { |
648 | m_overlay->setShowRulers(showRulers); |
649 | } |
650 | |
651 | void InspectorPageAgent::setShowPaintRects(ErrorString&, bool show) |
652 | { |
653 | m_showPaintRects = show; |
654 | m_client->setShowPaintRects(show); |
655 | |
656 | if (m_client->overridesShowPaintRects()) |
657 | return; |
658 | |
659 | m_overlay->setShowPaintRects(show); |
660 | } |
661 | |
662 | void InspectorPageAgent::domContentEventFired() |
663 | { |
664 | m_isFirstLayoutAfterOnLoad = true; |
665 | m_frontendDispatcher->domContentEventFired(timestamp()); |
666 | } |
667 | |
668 | void InspectorPageAgent::loadEventFired() |
669 | { |
670 | m_frontendDispatcher->loadEventFired(timestamp()); |
671 | } |
672 | |
673 | void InspectorPageAgent::frameNavigated(Frame& frame) |
674 | { |
675 | m_frontendDispatcher->frameNavigated(buildObjectForFrame(&frame)); |
676 | } |
677 | |
678 | void InspectorPageAgent::frameDetached(Frame& frame) |
679 | { |
680 | auto identifier = m_frameToIdentifier.take(&frame); |
681 | if (identifier.isNull()) |
682 | return; |
683 | m_frontendDispatcher->frameDetached(identifier); |
684 | m_identifierToFrame.remove(identifier); |
685 | } |
686 | |
687 | Frame* InspectorPageAgent::frameForId(const String& frameId) |
688 | { |
689 | return frameId.isEmpty() ? nullptr : m_identifierToFrame.get(frameId); |
690 | } |
691 | |
692 | String InspectorPageAgent::frameId(Frame* frame) |
693 | { |
694 | if (!frame) |
695 | return emptyString(); |
696 | return m_frameToIdentifier.ensure(frame, [this, frame] { |
697 | auto identifier = IdentifiersFactory::createIdentifier(); |
698 | m_identifierToFrame.set(identifier, frame); |
699 | return identifier; |
700 | }).iterator->value; |
701 | } |
702 | |
703 | String InspectorPageAgent::loaderId(DocumentLoader* loader) |
704 | { |
705 | if (!loader) |
706 | return emptyString(); |
707 | return m_loaderToIdentifier.ensure(loader, [] { |
708 | return IdentifiersFactory::createIdentifier(); |
709 | }).iterator->value; |
710 | } |
711 | |
712 | Frame* InspectorPageAgent::assertFrame(ErrorString& errorString, const String& frameId) |
713 | { |
714 | Frame* frame = frameForId(frameId); |
715 | if (!frame) |
716 | errorString = "No frame for given id found"_s ; |
717 | return frame; |
718 | } |
719 | |
720 | void InspectorPageAgent::loaderDetachedFromFrame(DocumentLoader& loader) |
721 | { |
722 | m_loaderToIdentifier.remove(&loader); |
723 | } |
724 | |
725 | void InspectorPageAgent::frameStartedLoading(Frame& frame) |
726 | { |
727 | m_frontendDispatcher->frameStartedLoading(frameId(&frame)); |
728 | } |
729 | |
730 | void InspectorPageAgent::frameStoppedLoading(Frame& frame) |
731 | { |
732 | m_frontendDispatcher->frameStoppedLoading(frameId(&frame)); |
733 | } |
734 | |
735 | void InspectorPageAgent::frameScheduledNavigation(Frame& frame, Seconds delay) |
736 | { |
737 | m_frontendDispatcher->frameScheduledNavigation(frameId(&frame), delay.value()); |
738 | } |
739 | |
740 | void InspectorPageAgent::frameClearedScheduledNavigation(Frame& frame) |
741 | { |
742 | m_frontendDispatcher->frameClearedScheduledNavigation(frameId(&frame)); |
743 | } |
744 | |
745 | void InspectorPageAgent::defaultAppearanceDidChange(bool useDarkAppearance) |
746 | { |
747 | m_frontendDispatcher->defaultAppearanceDidChange(useDarkAppearance ? Inspector::Protocol::Page::Appearance::Dark : Inspector::Protocol::Page::Appearance::Light); |
748 | } |
749 | |
750 | void InspectorPageAgent::didPaint(RenderObject& renderer, const LayoutRect& rect) |
751 | { |
752 | if (!m_showPaintRects) |
753 | return; |
754 | |
755 | LayoutRect absoluteRect = LayoutRect(renderer.localToAbsoluteQuad(FloatRect(rect)).boundingBox()); |
756 | FrameView* view = renderer.document().view(); |
757 | |
758 | LayoutRect rootRect = absoluteRect; |
759 | if (!view->frame().isMainFrame()) { |
760 | IntRect rootViewRect = view->contentsToRootView(snappedIntRect(absoluteRect)); |
761 | rootRect = view->frame().mainFrame().view()->rootViewToContents(rootViewRect); |
762 | } |
763 | |
764 | if (m_client->overridesShowPaintRects()) { |
765 | m_client->showPaintRect(rootRect); |
766 | return; |
767 | } |
768 | |
769 | m_overlay->showPaintRect(rootRect); |
770 | } |
771 | |
772 | void InspectorPageAgent::didLayout() |
773 | { |
774 | bool isFirstLayout = m_isFirstLayoutAfterOnLoad; |
775 | if (isFirstLayout) |
776 | m_isFirstLayoutAfterOnLoad = false; |
777 | |
778 | m_overlay->update(); |
779 | } |
780 | |
781 | void InspectorPageAgent::didScroll() |
782 | { |
783 | m_overlay->update(); |
784 | } |
785 | |
786 | void InspectorPageAgent::didRecalculateStyle() |
787 | { |
788 | m_overlay->update(); |
789 | } |
790 | |
791 | Ref<Inspector::Protocol::Page::Frame> InspectorPageAgent::buildObjectForFrame(Frame* frame) |
792 | { |
793 | ASSERT_ARG(frame, frame); |
794 | |
795 | auto frameObject = Inspector::Protocol::Page::Frame::create() |
796 | .setId(frameId(frame)) |
797 | .setLoaderId(loaderId(frame->loader().documentLoader())) |
798 | .setUrl(frame->document()->url().string()) |
799 | .setMimeType(frame->loader().documentLoader()->responseMIMEType()) |
800 | .setSecurityOrigin(frame->document()->securityOrigin().toRawString()) |
801 | .release(); |
802 | if (frame->tree().parent()) |
803 | frameObject->setParentId(frameId(frame->tree().parent())); |
804 | if (frame->ownerElement()) { |
805 | String name = frame->ownerElement()->getNameAttribute(); |
806 | if (name.isEmpty()) |
807 | name = frame->ownerElement()->attributeWithoutSynchronization(HTMLNames::idAttr); |
808 | frameObject->setName(name); |
809 | } |
810 | |
811 | return frameObject; |
812 | } |
813 | |
814 | Ref<Inspector::Protocol::Page::FrameResourceTree> InspectorPageAgent::buildObjectForFrameTree(Frame* frame) |
815 | { |
816 | ASSERT_ARG(frame, frame); |
817 | |
818 | Ref<Inspector::Protocol::Page::Frame> frameObject = buildObjectForFrame(frame); |
819 | auto subresources = JSON::ArrayOf<Inspector::Protocol::Page::FrameResource>::create(); |
820 | auto result = Inspector::Protocol::Page::FrameResourceTree::create() |
821 | .setFrame(WTFMove(frameObject)) |
822 | .setResources(subresources.copyRef()) |
823 | .release(); |
824 | |
825 | for (auto* cachedResource : cachedResourcesForFrame(frame)) { |
826 | auto resourceObject = Inspector::Protocol::Page::FrameResource::create() |
827 | .setUrl(cachedResource->url()) |
828 | .setType(cachedResourceTypeJSON(*cachedResource)) |
829 | .setMimeType(cachedResource->response().mimeType()) |
830 | .release(); |
831 | if (cachedResource->wasCanceled()) |
832 | resourceObject->setCanceled(true); |
833 | else if (cachedResource->status() == CachedResource::LoadError || cachedResource->status() == CachedResource::DecodeError) |
834 | resourceObject->setFailed(true); |
835 | String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource); |
836 | if (!sourceMappingURL.isEmpty()) |
837 | resourceObject->setSourceMapURL(sourceMappingURL); |
838 | String targetId = cachedResource->resourceRequest().initiatorIdentifier(); |
839 | if (!targetId.isEmpty()) |
840 | resourceObject->setTargetId(targetId); |
841 | subresources->addItem(WTFMove(resourceObject)); |
842 | } |
843 | |
844 | RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::FrameResourceTree>> childrenArray; |
845 | for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) { |
846 | if (!childrenArray) { |
847 | childrenArray = JSON::ArrayOf<Inspector::Protocol::Page::FrameResourceTree>::create(); |
848 | result->setChildFrames(childrenArray); |
849 | } |
850 | childrenArray->addItem(buildObjectForFrameTree(child)); |
851 | } |
852 | return result; |
853 | } |
854 | |
855 | void InspectorPageAgent::setEmulatedMedia(ErrorString&, const String& media) |
856 | { |
857 | if (media == m_emulatedMedia) |
858 | return; |
859 | |
860 | m_emulatedMedia = media; |
861 | |
862 | m_inspectedPage.updateStyleAfterChangeInEnvironment(); |
863 | |
864 | if (auto* document = m_inspectedPage.mainFrame().document()) |
865 | document->updateLayout(); |
866 | } |
867 | |
868 | void InspectorPageAgent::setForcedAppearance(ErrorString&, const String& appearance) |
869 | { |
870 | if (appearance == m_forcedAppearance) |
871 | return; |
872 | |
873 | m_forcedAppearance = appearance; |
874 | |
875 | if (appearance == "Light"_s ) |
876 | m_inspectedPage.setUseDarkAppearanceOverride(false); |
877 | else if (appearance == "Dark"_s ) |
878 | m_inspectedPage.setUseDarkAppearanceOverride(true); |
879 | else |
880 | m_inspectedPage.setUseDarkAppearanceOverride(WTF::nullopt); |
881 | } |
882 | |
883 | void InspectorPageAgent::applyUserAgentOverride(String& userAgent) |
884 | { |
885 | if (!m_userAgentOverride.isEmpty()) |
886 | userAgent = m_userAgentOverride; |
887 | } |
888 | |
889 | void InspectorPageAgent::applyEmulatedMedia(String& media) |
890 | { |
891 | if (!m_emulatedMedia.isEmpty()) |
892 | media = m_emulatedMedia; |
893 | } |
894 | |
895 | void InspectorPageAgent::getCompositingBordersVisible(ErrorString&, bool* outParam) |
896 | { |
897 | *outParam = m_inspectedPage.settings().showDebugBorders() || m_inspectedPage.settings().showRepaintCounter(); |
898 | } |
899 | |
900 | void InspectorPageAgent::setCompositingBordersVisible(ErrorString&, bool visible) |
901 | { |
902 | m_inspectedPage.settings().setShowDebugBorders(visible); |
903 | m_inspectedPage.settings().setShowRepaintCounter(visible); |
904 | } |
905 | |
906 | void InspectorPageAgent::snapshotNode(ErrorString& errorString, int nodeId, String* outDataURL) |
907 | { |
908 | InspectorDOMAgent* domAgent = m_instrumentingAgents.inspectorDOMAgent(); |
909 | ASSERT(domAgent); |
910 | Node* node = domAgent->assertNode(errorString, nodeId); |
911 | if (!node) |
912 | return; |
913 | |
914 | std::unique_ptr<ImageBuffer> snapshot = WebCore::snapshotNode(m_inspectedPage.mainFrame(), *node); |
915 | if (!snapshot) { |
916 | errorString = "Could not capture snapshot"_s ; |
917 | return; |
918 | } |
919 | |
920 | *outDataURL = snapshot->toDataURL("image/png"_s , WTF::nullopt, PreserveResolution::Yes); |
921 | } |
922 | |
923 | void InspectorPageAgent::snapshotRect(ErrorString& errorString, int x, int y, int width, int height, const String& coordinateSystem, String* outDataURL) |
924 | { |
925 | SnapshotOptions options = SnapshotOptionsNone; |
926 | if (coordinateSystem == "Viewport" ) |
927 | options |= SnapshotOptionsInViewCoordinates; |
928 | |
929 | IntRect rectangle(x, y, width, height); |
930 | std::unique_ptr<ImageBuffer> snapshot = snapshotFrameRect(m_inspectedPage.mainFrame(), rectangle, options); |
931 | |
932 | if (!snapshot) { |
933 | errorString = "Could not capture snapshot"_s ; |
934 | return; |
935 | } |
936 | |
937 | *outDataURL = snapshot->toDataURL("image/png"_s , WTF::nullopt, PreserveResolution::Yes); |
938 | } |
939 | |
940 | void InspectorPageAgent::archive(ErrorString& errorString, String* data) |
941 | { |
942 | #if ENABLE(WEB_ARCHIVE) && USE(CF) |
943 | auto archive = LegacyWebArchive::create(m_inspectedPage.mainFrame()); |
944 | if (!archive) { |
945 | errorString = "Could not create web archive for main frame"_s ; |
946 | return; |
947 | } |
948 | |
949 | RetainPtr<CFDataRef> buffer = archive->rawDataRepresentation(); |
950 | *data = base64Encode(CFDataGetBytePtr(buffer.get()), CFDataGetLength(buffer.get())); |
951 | #else |
952 | UNUSED_PARAM(data); |
953 | errorString = "No support for creating archives"_s ; |
954 | #endif |
955 | } |
956 | |
957 | } // namespace WebCore |
958 | |