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