Web Inspector: Audit: provide a way to get the contents of resources
[WebKit-https.git] / Source / WebCore / inspector / agents / InspectorPageAgent.cpp
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 sourceMapHeader = 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::MediaResource:
284     case CachedResource::Type::Icon:
285     case CachedResource::Type::RawResource:
286     default:
287         return InspectorPageAgent::OtherResource;
288     }
289 }
290
291 InspectorPageAgent::ResourceType InspectorPageAgent::inspectorResourceType(const CachedResource& cachedResource)
292 {
293     if (cachedResource.type() == CachedResource::Type::RawResource) {
294         switch (cachedResource.resourceRequest().requester()) {
295         case ResourceRequest::Requester::Fetch:
296             return InspectorPageAgent::FetchResource;
297         case ResourceRequest::Requester::Main:
298             return InspectorPageAgent::DocumentResource;
299         default:
300             return InspectorPageAgent::XHRResource;
301         }
302     }
303
304     return inspectorResourceType(cachedResource.type());
305 }
306
307 Inspector::Protocol::Page::ResourceType InspectorPageAgent::cachedResourceTypeJSON(const CachedResource& cachedResource)
308 {
309     return resourceTypeJSON(inspectorResourceType(cachedResource));
310 }
311
312 InspectorPageAgent::InspectorPageAgent(PageAgentContext& context, InspectorClient* client, InspectorOverlay* overlay)
313     : InspectorAgentBase("Page"_s, context)
314     , m_frontendDispatcher(std::make_unique<Inspector::PageFrontendDispatcher>(context.frontendRouter))
315     , m_backendDispatcher(Inspector::PageBackendDispatcher::create(context.backendDispatcher, this))
316     , m_page(context.inspectedPage)
317     , m_client(client)
318     , m_overlay(overlay)
319 {
320 }
321
322 void InspectorPageAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
323 {
324 }
325
326 void InspectorPageAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
327 {
328     ErrorString unused;
329     disable(unused);
330 }
331
332 double InspectorPageAgent::timestamp()
333 {
334     return m_environment.executionStopwatch()->elapsedTime().seconds();
335 }
336
337 void InspectorPageAgent::enable(ErrorString&)
338 {
339     m_enabled = true;
340     m_instrumentingAgents.setInspectorPageAgent(this);
341
342     auto stopwatch = m_environment.executionStopwatch();
343     stopwatch->reset();
344     stopwatch->start();
345
346 #if HAVE(OS_DARK_MODE_SUPPORT)
347     defaultAppearanceDidChange(m_page.defaultUseDarkAppearance());
348 #endif
349 }
350
351 void InspectorPageAgent::disable(ErrorString&)
352 {
353     m_enabled = false;
354     m_instrumentingAgents.setInspectorPageAgent(nullptr);
355
356     ErrorString unused;
357     setShowPaintRects(unused, false);
358     setShowRulers(unused, false);
359     overrideUserAgent(unused, nullptr);
360     setEmulatedMedia(unused, emptyString());
361     setForcedAppearance(unused, emptyString());
362
363 #define DISABLE_INSPECTOR_OVERRIDE_SETTING(name) \
364     m_page.settings().set##name##InspectorOverride(WTF::nullopt);
365
366     FOR_EACH_INSPECTOR_OVERRIDE_SETTING(DISABLE_INSPECTOR_OVERRIDE_SETTING)
367
368 #undef DISABLE_INSPECTOR_OVERRIDE_SETTING
369 }
370
371 void InspectorPageAgent::reload(ErrorString&, const bool* optionalReloadFromOrigin, const bool* optionalRevalidateAllResources)
372 {
373     bool reloadFromOrigin = optionalReloadFromOrigin && *optionalReloadFromOrigin;
374     bool revalidateAllResources = optionalRevalidateAllResources && *optionalRevalidateAllResources;
375
376     OptionSet<ReloadOption> reloadOptions;
377     if (reloadFromOrigin)
378         reloadOptions.add(ReloadOption::FromOrigin);
379     if (!revalidateAllResources)
380         reloadOptions.add(ReloadOption::ExpiredOnly);
381
382     m_page.mainFrame().loader().reload(reloadOptions);
383 }
384
385 void InspectorPageAgent::navigate(ErrorString&, const String& url)
386 {
387     UserGestureIndicator indicator { ProcessingUserGesture };
388     Frame& frame = m_page.mainFrame();
389
390     ResourceRequest resourceRequest { frame.document()->completeURL(url) };
391     FrameLoadRequest frameLoadRequest { *frame.document(), frame.document()->securityOrigin(), resourceRequest, "_self"_s, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::No, NewFrameOpenerPolicy::Allow, ShouldOpenExternalURLsPolicy::ShouldNotAllow, InitiatedByMainFrame::Unknown };
392     frame.loader().changeLocation(WTFMove(frameLoadRequest));
393 }
394
395 void InspectorPageAgent::overrideUserAgent(ErrorString&, const String* value)
396 {
397     m_userAgentOverride = value ? *value : String();
398 }
399
400 void InspectorPageAgent::overrideSetting(ErrorString& errorString, const String& settingString, const bool* value)
401 {
402     if (settingString.isEmpty()) {
403         errorString = "Preference is empty"_s;
404         return;
405     }
406
407     auto setting = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::Page::Setting>(settingString);
408     if (!setting) {
409         errorString = makeString("Unknown setting: "_s, settingString);
410         return;
411     }
412
413     switch (setting.value()) {
414 #define CASE_INSPECTOR_OVERRIDE_SETTING(name) \
415     case Inspector::Protocol::Page::Setting::name: { \
416         if (value) \
417             m_page.settings().set##name##InspectorOverride(*value); \
418         else \
419             m_page.settings().set##name##InspectorOverride(WTF::nullopt); \
420         return; \
421     } \
422
423     FOR_EACH_INSPECTOR_OVERRIDE_SETTING(CASE_INSPECTOR_OVERRIDE_SETTING)
424
425 #undef CASE_INSPECTOR_OVERRIDE_SETTING
426     }
427
428     ASSERT_NOT_REACHED();
429 }
430
431 static Inspector::Protocol::Page::CookieSameSitePolicy cookieSameSitePolicyJSON(Cookie::SameSitePolicy policy)
432 {
433     switch (policy) {
434     case Cookie::SameSitePolicy::None:
435         return Inspector::Protocol::Page::CookieSameSitePolicy::None;
436     case Cookie::SameSitePolicy::Lax:
437         return Inspector::Protocol::Page::CookieSameSitePolicy::Lax;
438     case Cookie::SameSitePolicy::Strict:
439         return Inspector::Protocol::Page::CookieSameSitePolicy::Strict;
440     }
441     ASSERT_NOT_REACHED();
442     return Inspector::Protocol::Page::CookieSameSitePolicy::None;
443 }
444
445 static Ref<Inspector::Protocol::Page::Cookie> buildObjectForCookie(const Cookie& cookie)
446 {
447     return Inspector::Protocol::Page::Cookie::create()
448         .setName(cookie.name)
449         .setValue(cookie.value)
450         .setDomain(cookie.domain)
451         .setPath(cookie.path)
452         .setExpires(cookie.expires)
453         .setSize((cookie.name.length() + cookie.value.length()))
454         .setHttpOnly(cookie.httpOnly)
455         .setSecure(cookie.secure)
456         .setSession(cookie.session)
457         .setSameSite(cookieSameSitePolicyJSON(cookie.sameSite))
458         .release();
459 }
460
461 static Ref<JSON::ArrayOf<Inspector::Protocol::Page::Cookie>> buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
462 {
463     auto cookies = JSON::ArrayOf<Inspector::Protocol::Page::Cookie>::create();
464
465     for (const auto& cookie : cookiesList)
466         cookies->addItem(buildObjectForCookie(cookie));
467
468     return cookies;
469 }
470
471 static Vector<URL> allResourcesURLsForFrame(Frame* frame)
472 {
473     Vector<URL> result;
474
475     result.append(frame->loader().documentLoader()->url());
476
477     for (auto* cachedResource : InspectorPageAgent::cachedResourcesForFrame(frame))
478         result.append(cachedResource->url());
479
480     return result;
481 }
482
483 void InspectorPageAgent::getCookies(ErrorString&, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::Cookie>>& cookies)
484 {
485     // If we can get raw cookies.
486     ListHashSet<Cookie> rawCookiesList;
487
488     // If we can't get raw cookies - fall back to String representation
489     StringBuilder stringCookiesList;
490
491     // Return value to getRawCookies should be the same for every call because
492     // the return value is platform/network backend specific, and the call will
493     // always return the same true/false value.
494     bool rawCookiesImplemented = false;
495
496     for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
497         Document* document = frame->document();
498         if (!document || !document->page())
499             continue;
500
501         for (auto& url : allResourcesURLsForFrame(frame)) {
502             Vector<Cookie> docCookiesList;
503             rawCookiesImplemented = document->page()->cookieJar().getRawCookies(*document, URL({ }, url), docCookiesList);
504
505             if (!rawCookiesImplemented) {
506                 // FIXME: We need duplication checking for the String representation of cookies.
507                 // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
508                 // because "document" is the document of the main frame of the page.
509                 stringCookiesList.append(document->cookie().releaseReturnValue());
510             } else {
511                 for (auto& cookie : docCookiesList)
512                     rawCookiesList.add(cookie);
513             }
514         }
515     }
516
517     // FIXME: Do not return empty string/empty array. Make returns optional instead. https://bugs.webkit.org/show_bug.cgi?id=80855
518     if (rawCookiesImplemented)
519         cookies = buildArrayForCookies(rawCookiesList);
520     else
521         cookies = JSON::ArrayOf<Inspector::Protocol::Page::Cookie>::create();
522 }
523
524 void InspectorPageAgent::deleteCookie(ErrorString&, const String& cookieName, const String& url)
525 {
526     URL parsedURL({ }, url);
527     for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
528         if (auto* document = frame->document()) {
529             if (auto* page = document->page())
530                 page->cookieJar().deleteCookie(*document, parsedURL, cookieName);
531         }
532     }
533 }
534
535 void InspectorPageAgent::getResourceTree(ErrorString&, RefPtr<Inspector::Protocol::Page::FrameResourceTree>& object)
536 {
537     object = buildObjectForFrameTree(&m_page.mainFrame());
538 }
539
540 void InspectorPageAgent::getResourceContent(ErrorString& errorString, const String& frameId, const String& url, String* content, bool* base64Encoded)
541 {
542     Frame* frame = assertFrame(errorString, frameId);
543     if (!frame)
544         return;
545
546     resourceContent(errorString, frame, URL({ }, url), content, base64Encoded);
547 }
548
549 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)
550 {
551     results = JSON::ArrayOf<Inspector::Protocol::GenericTypes::SearchMatch>::create();
552
553     bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
554     bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
555
556     if (optionalRequestId) {
557         if (InspectorNetworkAgent* networkAgent = m_instrumentingAgents.inspectorNetworkAgent()) {
558             networkAgent->searchInRequest(errorString, *optionalRequestId, query, caseSensitive, isRegex, results);
559             return;
560         }
561     }
562
563     Frame* frame = assertFrame(errorString, frameId);
564     if (!frame)
565         return;
566
567     DocumentLoader* loader = assertDocumentLoader(errorString, frame);
568     if (!loader)
569         return;
570
571     URL kurl({ }, url);
572
573     String content;
574     bool success = false;
575     if (equalIgnoringFragmentIdentifier(kurl, loader->url()))
576         success = mainResourceContent(frame, false, &content);
577
578     if (!success) {
579         if (auto* resource = cachedResource(frame, kurl)) {
580             if (auto textContent = InspectorNetworkAgent::textContentForCachedResource(*resource)) {
581                 content = *textContent;
582                 success = true;
583             }
584         }
585     }
586
587     if (!success)
588         return;
589
590     results = ContentSearchUtilities::searchInTextByLines(content, query, caseSensitive, isRegex);
591 }
592
593 static Ref<Inspector::Protocol::Page::SearchResult> buildObjectForSearchResult(const String& frameId, const String& url, int matchesCount)
594 {
595     return Inspector::Protocol::Page::SearchResult::create()
596         .setUrl(url)
597         .setFrameId(frameId)
598         .setMatchesCount(matchesCount)
599         .release();
600 }
601
602 void InspectorPageAgent::searchInResources(ErrorString&, const String& text, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::SearchResult>>& result)
603 {
604     result = JSON::ArrayOf<Inspector::Protocol::Page::SearchResult>::create();
605
606     bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
607     bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
608     JSC::Yarr::RegularExpression regex = ContentSearchUtilities::createSearchRegex(text, caseSensitive, isRegex);
609
610     for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
611         for (auto* cachedResource : cachedResourcesForFrame(frame)) {
612             if (auto textContent = InspectorNetworkAgent::textContentForCachedResource(*cachedResource)) {
613                 int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, *textContent);
614                 if (matchesCount)
615                     result->addItem(buildObjectForSearchResult(frameId(frame), cachedResource->url(), matchesCount));
616             }
617         }
618     }
619
620     if (InspectorNetworkAgent* networkAgent = m_instrumentingAgents.inspectorNetworkAgent())
621         networkAgent->searchOtherRequests(regex, result);
622 }
623
624 void InspectorPageAgent::setShowRulers(ErrorString&, bool showRulers)
625 {
626     m_overlay->setShowRulers(showRulers);
627 }
628
629 void InspectorPageAgent::setShowPaintRects(ErrorString&, bool show)
630 {
631     m_showPaintRects = show;
632     m_client->setShowPaintRects(show);
633
634     if (m_client->overridesShowPaintRects())
635         return;
636
637     m_overlay->setShowPaintRects(show);
638 }
639
640 void InspectorPageAgent::domContentEventFired()
641 {
642     m_isFirstLayoutAfterOnLoad = true;
643     m_frontendDispatcher->domContentEventFired(timestamp());
644 }
645
646 void InspectorPageAgent::loadEventFired()
647 {
648     m_frontendDispatcher->loadEventFired(timestamp());
649 }
650
651 void InspectorPageAgent::frameNavigated(Frame& frame)
652 {
653     m_frontendDispatcher->frameNavigated(buildObjectForFrame(&frame));
654 }
655
656 void InspectorPageAgent::frameDetached(Frame& frame)
657 {
658     auto identifier = m_frameToIdentifier.take(&frame);
659     if (identifier.isNull())
660         return;
661     m_frontendDispatcher->frameDetached(identifier);
662     m_identifierToFrame.remove(identifier);
663 }
664
665 Frame& InspectorPageAgent::mainFrame()
666 {
667     return m_page.mainFrame();
668 }
669
670 Frame* InspectorPageAgent::frameForId(const String& frameId)
671 {
672     return frameId.isEmpty() ? nullptr : m_identifierToFrame.get(frameId);
673 }
674
675 String InspectorPageAgent::frameId(Frame* frame)
676 {
677     if (!frame)
678         return emptyString();
679     return m_frameToIdentifier.ensure(frame, [this, frame] {
680         auto identifier = IdentifiersFactory::createIdentifier();
681         m_identifierToFrame.set(identifier, frame);
682         return identifier;
683     }).iterator->value;
684 }
685
686 bool InspectorPageAgent::hasIdForFrame(Frame* frame) const
687 {
688     return frame && m_frameToIdentifier.contains(frame);
689 }
690
691 String InspectorPageAgent::loaderId(DocumentLoader* loader)
692 {
693     if (!loader)
694         return emptyString();
695     return m_loaderToIdentifier.ensure(loader, [] {
696         return IdentifiersFactory::createIdentifier();
697     }).iterator->value;
698 }
699
700 Frame* InspectorPageAgent::findFrameWithSecurityOrigin(const String& originRawString)
701 {
702     for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
703         if (frame->document()->securityOrigin().toRawString() == originRawString)
704             return frame;
705     }
706     return nullptr;
707 }
708
709 Frame* InspectorPageAgent::assertFrame(ErrorString& errorString, const String& frameId)
710 {
711     Frame* frame = frameForId(frameId);
712     if (!frame)
713         errorString = "No frame for given id found"_s;
714     return frame;
715 }
716
717 DocumentLoader* InspectorPageAgent::assertDocumentLoader(ErrorString& errorString, Frame* frame)
718 {
719     FrameLoader& frameLoader = frame->loader();
720     DocumentLoader* documentLoader = frameLoader.documentLoader();
721     if (!documentLoader)
722         errorString = "No documentLoader for given frame found"_s;
723     return documentLoader;
724 }
725
726 void InspectorPageAgent::loaderDetachedFromFrame(DocumentLoader& loader)
727 {
728     m_loaderToIdentifier.remove(&loader);
729 }
730
731 void InspectorPageAgent::frameStartedLoading(Frame& frame)
732 {
733     m_frontendDispatcher->frameStartedLoading(frameId(&frame));
734 }
735
736 void InspectorPageAgent::frameStoppedLoading(Frame& frame)
737 {
738     m_frontendDispatcher->frameStoppedLoading(frameId(&frame));
739 }
740
741 void InspectorPageAgent::frameScheduledNavigation(Frame& frame, Seconds delay)
742 {
743     m_frontendDispatcher->frameScheduledNavigation(frameId(&frame), delay.value());
744 }
745
746 void InspectorPageAgent::frameClearedScheduledNavigation(Frame& frame)
747 {
748     m_frontendDispatcher->frameClearedScheduledNavigation(frameId(&frame));
749 }
750
751 void InspectorPageAgent::defaultAppearanceDidChange(bool useDarkAppearance)
752 {
753     m_frontendDispatcher->defaultAppearanceDidChange(useDarkAppearance ? Inspector::Protocol::Page::Appearance::Dark : Inspector::Protocol::Page::Appearance::Light);
754 }
755
756 void InspectorPageAgent::didPaint(RenderObject& renderer, const LayoutRect& rect)
757 {
758     if (!m_enabled || !m_showPaintRects)
759         return;
760
761     LayoutRect absoluteRect = LayoutRect(renderer.localToAbsoluteQuad(FloatRect(rect)).boundingBox());
762     FrameView* view = renderer.document().view();
763
764     LayoutRect rootRect = absoluteRect;
765     if (!view->frame().isMainFrame()) {
766         IntRect rootViewRect = view->contentsToRootView(snappedIntRect(absoluteRect));
767         rootRect = view->frame().mainFrame().view()->rootViewToContents(rootViewRect);
768     }
769
770     if (m_client->overridesShowPaintRects()) {
771         m_client->showPaintRect(rootRect);
772         return;
773     }
774
775     m_overlay->showPaintRect(rootRect);
776 }
777
778 void InspectorPageAgent::didLayout()
779 {
780     bool isFirstLayout = m_isFirstLayoutAfterOnLoad;
781     if (isFirstLayout)
782         m_isFirstLayoutAfterOnLoad = false;
783
784     if (!m_enabled)
785         return;
786
787     m_overlay->update();
788 }
789
790 void InspectorPageAgent::didScroll()
791 {
792     if (m_enabled)
793         m_overlay->update();
794 }
795
796 void InspectorPageAgent::didRecalculateStyle()
797 {
798     if (m_enabled)
799         m_overlay->update();
800 }
801
802 Ref<Inspector::Protocol::Page::Frame> InspectorPageAgent::buildObjectForFrame(Frame* frame)
803 {
804     ASSERT_ARG(frame, frame);
805
806     auto frameObject = Inspector::Protocol::Page::Frame::create()
807         .setId(frameId(frame))
808         .setLoaderId(loaderId(frame->loader().documentLoader()))
809         .setUrl(frame->document()->url().string())
810         .setMimeType(frame->loader().documentLoader()->responseMIMEType())
811         .setSecurityOrigin(frame->document()->securityOrigin().toRawString())
812         .release();
813     if (frame->tree().parent())
814         frameObject->setParentId(frameId(frame->tree().parent()));
815     if (frame->ownerElement()) {
816         String name = frame->ownerElement()->getNameAttribute();
817         if (name.isEmpty())
818             name = frame->ownerElement()->attributeWithoutSynchronization(HTMLNames::idAttr);
819         frameObject->setName(name);
820     }
821
822     return frameObject;
823 }
824
825 Ref<Inspector::Protocol::Page::FrameResourceTree> InspectorPageAgent::buildObjectForFrameTree(Frame* frame)
826 {
827     ASSERT_ARG(frame, frame);
828
829     Ref<Inspector::Protocol::Page::Frame> frameObject = buildObjectForFrame(frame);
830     auto subresources = JSON::ArrayOf<Inspector::Protocol::Page::FrameResource>::create();
831     auto result = Inspector::Protocol::Page::FrameResourceTree::create()
832         .setFrame(WTFMove(frameObject))
833         .setResources(subresources.copyRef())
834         .release();
835
836     for (auto* cachedResource : cachedResourcesForFrame(frame)) {
837         auto resourceObject = Inspector::Protocol::Page::FrameResource::create()
838             .setUrl(cachedResource->url())
839             .setType(cachedResourceTypeJSON(*cachedResource))
840             .setMimeType(cachedResource->response().mimeType())
841             .release();
842         if (cachedResource->wasCanceled())
843             resourceObject->setCanceled(true);
844         else if (cachedResource->status() == CachedResource::LoadError || cachedResource->status() == CachedResource::DecodeError)
845             resourceObject->setFailed(true);
846         String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource);
847         if (!sourceMappingURL.isEmpty())
848             resourceObject->setSourceMapURL(sourceMappingURL);
849         String targetId = cachedResource->resourceRequest().initiatorIdentifier();
850         if (!targetId.isEmpty())
851             resourceObject->setTargetId(targetId);
852         subresources->addItem(WTFMove(resourceObject));
853     }
854
855     RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::FrameResourceTree>> childrenArray;
856     for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
857         if (!childrenArray) {
858             childrenArray = JSON::ArrayOf<Inspector::Protocol::Page::FrameResourceTree>::create();
859             result->setChildFrames(childrenArray);
860         }
861         childrenArray->addItem(buildObjectForFrameTree(child));
862     }
863     return result;
864 }
865
866 void InspectorPageAgent::setEmulatedMedia(ErrorString&, const String& media)
867 {
868     if (media == m_emulatedMedia)
869         return;
870
871     m_emulatedMedia = media;
872
873     m_page.updateStyleAfterChangeInEnvironment();
874
875     if (auto* document = m_page.mainFrame().document())
876         document->updateLayout();
877 }
878
879 void InspectorPageAgent::setForcedAppearance(ErrorString&, const String& appearance)
880 {
881     if (appearance == m_forcedAppearance)
882         return;
883
884     m_forcedAppearance = appearance;
885
886     if (appearance == "Light"_s)
887         m_page.setUseDarkAppearanceOverride(false);
888     else if (appearance == "Dark"_s)
889         m_page.setUseDarkAppearanceOverride(true);
890     else
891         m_page.setUseDarkAppearanceOverride(WTF::nullopt);
892 }
893
894 void InspectorPageAgent::applyUserAgentOverride(String& userAgent)
895 {
896     if (!m_userAgentOverride.isEmpty())
897         userAgent = m_userAgentOverride;
898 }
899
900 void InspectorPageAgent::applyEmulatedMedia(String& media)
901 {
902     if (!m_emulatedMedia.isEmpty())
903         media = m_emulatedMedia;
904 }
905
906 void InspectorPageAgent::getCompositingBordersVisible(ErrorString&, bool* outParam)
907 {
908     *outParam = m_page.settings().showDebugBorders() || m_page.settings().showRepaintCounter();
909 }
910
911 void InspectorPageAgent::setCompositingBordersVisible(ErrorString&, bool visible)
912 {
913     m_page.settings().setShowDebugBorders(visible);
914     m_page.settings().setShowRepaintCounter(visible);
915 }
916
917 void InspectorPageAgent::snapshotNode(ErrorString& errorString, int nodeId, String* outDataURL)
918 {
919     Frame& frame = mainFrame();
920
921     InspectorDOMAgent* domAgent = m_instrumentingAgents.inspectorDOMAgent();
922     ASSERT(domAgent);
923     Node* node = domAgent->assertNode(errorString, nodeId);
924     if (!node)
925         return;
926
927     std::unique_ptr<ImageBuffer> snapshot = WebCore::snapshotNode(frame, *node);
928     if (!snapshot) {
929         errorString = "Could not capture snapshot"_s;
930         return;
931     }
932
933     *outDataURL = snapshot->toDataURL("image/png"_s, WTF::nullopt, PreserveResolution::Yes);
934 }
935
936 void InspectorPageAgent::snapshotRect(ErrorString& errorString, int x, int y, int width, int height, const String& coordinateSystem, String* outDataURL)
937 {
938     Frame& frame = mainFrame();
939
940     SnapshotOptions options = SnapshotOptionsNone;
941     if (coordinateSystem == "Viewport")
942         options |= SnapshotOptionsInViewCoordinates;
943
944     IntRect rectangle(x, y, width, height);
945     std::unique_ptr<ImageBuffer> snapshot = snapshotFrameRect(frame, rectangle, options);
946
947     if (!snapshot) {
948         errorString = "Could not capture snapshot"_s;
949         return;
950     }
951
952     *outDataURL = snapshot->toDataURL("image/png"_s, WTF::nullopt, PreserveResolution::Yes);
953 }
954
955 void InspectorPageAgent::archive(ErrorString& errorString, String* data)
956 {
957 #if ENABLE(WEB_ARCHIVE) && USE(CF)
958     Frame& frame = mainFrame();
959     auto archive = LegacyWebArchive::create(frame);
960     if (!archive) {
961         errorString = "Could not create web archive for main frame"_s;
962         return;
963     }
964
965     RetainPtr<CFDataRef> buffer = archive->rawDataRepresentation();
966     *data = base64Encode(CFDataGetBytePtr(buffer.get()), CFDataGetLength(buffer.get()));
967 #else
968     UNUSED_PARAM(data);
969     errorString = "No support for creating archives"_s;
970 #endif
971 }
972
973 } // namespace WebCore