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