Web Inspector: Add a dedicated Network tab that is always live
[WebKit-https.git] / Source / WebCore / inspector / InspectorPageAgent.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "InspectorPageAgent.h"
33
34 #include "CachedCSSStyleSheet.h"
35 #include "CachedFont.h"
36 #include "CachedImage.h"
37 #include "CachedResource.h"
38 #include "CachedResourceLoader.h"
39 #include "CachedScript.h"
40 #include "Cookie.h"
41 #include "CookieJar.h"
42 #include "DOMImplementation.h"
43 #include "DOMPatchSupport.h"
44 #include "DOMWrapperWorld.h"
45 #include "Document.h"
46 #include "DocumentLoader.h"
47 #include "Frame.h"
48 #include "FrameLoadRequest.h"
49 #include "FrameLoader.h"
50 #include "FrameSnapshotting.h"
51 #include "FrameView.h"
52 #include "HTMLFrameOwnerElement.h"
53 #include "HTMLNames.h"
54 #include "ImageBuffer.h"
55 #include "InspectorClient.h"
56 #include "InspectorDOMAgent.h"
57 #include "InspectorInstrumentation.h"
58 #include "InspectorOverlay.h"
59 #include "InspectorTimelineAgent.h"
60 #include "InstrumentingAgents.h"
61 #include "MainFrame.h"
62 #include "MemoryCache.h"
63 #include "Page.h"
64 #include "ScriptController.h"
65 #include "SecurityOrigin.h"
66 #include "Settings.h"
67 #include "TextEncoding.h"
68 #include "TextResourceDecoder.h"
69 #include "UserGestureIndicator.h"
70 #include <bindings/ScriptValue.h>
71 #include <inspector/ContentSearchUtilities.h>
72 #include <inspector/IdentifiersFactory.h>
73 #include <inspector/InspectorValues.h>
74 #include <wtf/ListHashSet.h>
75 #include <wtf/text/Base64.h>
76 #include <wtf/text/StringBuilder.h>
77 #include <yarr/RegularExpression.h>
78
79 #if ENABLE(WEB_ARCHIVE) && USE(CF)
80 #include "LegacyWebArchive.h"
81 #endif
82
83 using namespace Inspector;
84
85 namespace WebCore {
86
87 static bool decodeBuffer(const char* buffer, unsigned size, const String& textEncodingName, String* result)
88 {
89     if (buffer) {
90         TextEncoding encoding(textEncodingName);
91         if (!encoding.isValid())
92             encoding = WindowsLatin1Encoding();
93         *result = encoding.decode(buffer, size);
94         return true;
95     }
96     return false;
97 }
98
99 static bool prepareCachedResourceBuffer(CachedResource* cachedResource, bool* hasZeroSize)
100 {
101     *hasZeroSize = false;
102     if (!cachedResource)
103         return false;
104
105     // Zero-sized resources don't have data at all -- so fake the empty buffer, instead of indicating error by returning 0.
106     if (!cachedResource->encodedSize()) {
107         *hasZeroSize = true;
108         return true;
109     }
110
111     return true;
112 }
113
114 static bool hasTextContent(CachedResource* cachedResource)
115 {
116     InspectorPageAgent::ResourceType type = InspectorPageAgent::cachedResourceType(*cachedResource);
117     return type == InspectorPageAgent::DocumentResource || type == InspectorPageAgent::StylesheetResource || type == InspectorPageAgent::ScriptResource || type == InspectorPageAgent::XHRResource;
118 }
119
120 static RefPtr<TextResourceDecoder> createXHRTextDecoder(const String& mimeType, const String& textEncodingName)
121 {
122     RefPtr<TextResourceDecoder> decoder;
123     if (!textEncodingName.isEmpty())
124         decoder = TextResourceDecoder::create("text/plain", textEncodingName);
125     else if (DOMImplementation::isXMLMIMEType(mimeType.lower())) {
126         decoder = TextResourceDecoder::create("application/xml");
127         decoder->useLenientXMLDecoding();
128     } else if (equalIgnoringCase(mimeType, "text/html"))
129         decoder = TextResourceDecoder::create("text/html", "UTF-8");
130     else
131         decoder = TextResourceDecoder::create("text/plain", "UTF-8");
132     return WTF::move(decoder);
133 }
134
135 bool InspectorPageAgent::cachedResourceContent(CachedResource* cachedResource, String* result, bool* base64Encoded)
136 {
137     bool hasZeroSize;
138     bool prepared = prepareCachedResourceBuffer(cachedResource, &hasZeroSize);
139     if (!prepared)
140         return false;
141
142     *base64Encoded = !hasTextContent(cachedResource);
143     if (*base64Encoded) {
144         RefPtr<SharedBuffer> buffer = hasZeroSize ? SharedBuffer::create() : cachedResource->resourceBuffer();
145         if (!buffer)
146             return false;
147         *result = base64Encode(buffer->data(), buffer->size());
148         return true;
149     }
150
151     if (hasZeroSize) {
152         *result = emptyString();
153         return true;
154     }
155
156     if (cachedResource) {
157         switch (cachedResource->type()) {
158         case CachedResource::CSSStyleSheet:
159             // This can return a null String if the MIME type is invalid.
160             *result = downcast<CachedCSSStyleSheet>(*cachedResource).sheetText();
161             return !result->isNull();
162         case CachedResource::Script:
163             *result = downcast<CachedScript>(*cachedResource).script();
164             return true;
165         case CachedResource::RawResource: {
166             auto* buffer = cachedResource->resourceBuffer();
167             if (!buffer)
168                 return false;
169             RefPtr<TextResourceDecoder> decoder = createXHRTextDecoder(cachedResource->response().mimeType(), cachedResource->response().textEncodingName());
170             // We show content for raw resources only for certain mime types (text, html and xml). Otherwise decoder will be null.
171             if (!decoder)
172                 return false;
173             *result = decoder->decodeAndFlush(buffer->data(), buffer->size());
174             return true;
175         }
176         default:
177             auto* buffer = cachedResource->resourceBuffer();
178             return decodeBuffer(buffer ? buffer->data() : nullptr, buffer ? buffer->size() : 0, cachedResource->encoding(), result);
179         }
180     }
181     return false;
182 }
183
184 bool InspectorPageAgent::mainResourceContent(Frame* frame, bool withBase64Encode, String* result)
185 {
186     RefPtr<SharedBuffer> buffer = frame->loader().documentLoader()->mainResourceData();
187     if (!buffer)
188         return false;
189     return InspectorPageAgent::dataContent(buffer->data(), buffer->size(), frame->document()->inputEncoding(), withBase64Encode, result);
190 }
191
192 // static
193 bool InspectorPageAgent::sharedBufferContent(PassRefPtr<SharedBuffer> buffer, const String& textEncodingName, bool withBase64Encode, String* result)
194 {
195     return dataContent(buffer ? buffer->data() : nullptr, buffer ? buffer->size() : 0, textEncodingName, withBase64Encode, result);
196 }
197
198 bool InspectorPageAgent::dataContent(const char* data, unsigned size, const String& textEncodingName, bool withBase64Encode, String* result)
199 {
200     if (withBase64Encode) {
201         *result = base64Encode(data, size);
202         return true;
203     }
204
205     return decodeBuffer(data, size, textEncodingName, result);
206 }
207
208 // static
209 void InspectorPageAgent::resourceContent(ErrorString& errorString, Frame* frame, const URL& url, String* result, bool* base64Encoded)
210 {
211     DocumentLoader* loader = assertDocumentLoader(errorString, frame);
212     if (!loader)
213         return;
214
215     RefPtr<SharedBuffer> buffer;
216     bool success = false;
217     if (equalIgnoringFragmentIdentifier(url, loader->url())) {
218         *base64Encoded = false;
219         success = mainResourceContent(frame, *base64Encoded, result);
220     }
221
222     if (!success)
223         success = cachedResourceContent(cachedResource(frame, url), result, base64Encoded);
224
225     if (!success)
226         errorString = ASCIILiteral("No resource with given URL found");
227 }
228
229 //static
230 String InspectorPageAgent::sourceMapURLForResource(CachedResource* cachedResource)
231 {
232     DEPRECATED_DEFINE_STATIC_LOCAL(String, sourceMapHTTPHeader, (ASCIILiteral("SourceMap")));
233     DEPRECATED_DEFINE_STATIC_LOCAL(String, sourceMapHTTPHeaderDeprecated, (ASCIILiteral("X-SourceMap")));
234
235     if (!cachedResource)
236         return String();
237
238     // Scripts are handled in a separate path.
239     if (cachedResource->type() != CachedResource::CSSStyleSheet)
240         return String();
241
242     String sourceMapHeader = cachedResource->response().httpHeaderField(sourceMapHTTPHeader);
243     if (!sourceMapHeader.isEmpty())
244         return sourceMapHeader;
245
246     sourceMapHeader = cachedResource->response().httpHeaderField(sourceMapHTTPHeaderDeprecated);
247     if (!sourceMapHeader.isEmpty())
248         return sourceMapHeader;
249
250     String content;
251     bool base64Encoded;
252     if (InspectorPageAgent::cachedResourceContent(cachedResource, &content, &base64Encoded) && !base64Encoded)
253         return ContentSearchUtilities::findStylesheetSourceMapURL(content);
254
255     return String();
256 }
257
258 CachedResource* InspectorPageAgent::cachedResource(Frame* frame, const URL& url)
259 {
260     CachedResource* cachedResource = frame->document()->cachedResourceLoader().cachedResource(url);
261     if (!cachedResource) {
262         ResourceRequest request(url);
263 #if ENABLE(CACHE_PARTITIONING)
264         request.setDomainForCachePartition(frame->document()->topOrigin()->domainForCachePartition());
265 #endif
266         cachedResource = MemoryCache::singleton().resourceForRequest(request, frame->page()->sessionID());
267     }
268
269     return cachedResource;
270 }
271
272 Inspector::Protocol::Page::ResourceType InspectorPageAgent::resourceTypeJson(InspectorPageAgent::ResourceType resourceType)
273 {
274     switch (resourceType) {
275     case DocumentResource:
276         return Inspector::Protocol::Page::ResourceType::Document;
277     case ImageResource:
278         return Inspector::Protocol::Page::ResourceType::Image;
279     case FontResource:
280         return Inspector::Protocol::Page::ResourceType::Font;
281     case StylesheetResource:
282         return Inspector::Protocol::Page::ResourceType::Stylesheet;
283     case ScriptResource:
284         return Inspector::Protocol::Page::ResourceType::Script;
285     case XHRResource:
286         return Inspector::Protocol::Page::ResourceType::XHR;
287     case WebSocketResource:
288         return Inspector::Protocol::Page::ResourceType::WebSocket;
289     case OtherResource:
290         return Inspector::Protocol::Page::ResourceType::Other;
291     }
292     return Inspector::Protocol::Page::ResourceType::Other;
293 }
294
295 InspectorPageAgent::ResourceType InspectorPageAgent::cachedResourceType(const CachedResource& cachedResource)
296 {
297     switch (cachedResource.type()) {
298     case CachedResource::ImageResource:
299         return InspectorPageAgent::ImageResource;
300 #if ENABLE(SVG_FONTS)
301     case CachedResource::SVGFontResource:
302 #endif
303     case CachedResource::FontResource:
304         return InspectorPageAgent::FontResource;
305     case CachedResource::CSSStyleSheet:
306         // Fall through.
307 #if ENABLE(XSLT)
308     case CachedResource::XSLStyleSheet:
309 #endif
310         return InspectorPageAgent::StylesheetResource;
311     case CachedResource::Script:
312         return InspectorPageAgent::ScriptResource;
313     case CachedResource::RawResource:
314         return InspectorPageAgent::XHRResource;
315     case CachedResource::MainResource:
316         return InspectorPageAgent::DocumentResource;
317     default:
318         break;
319     }
320     return InspectorPageAgent::OtherResource;
321 }
322
323 Inspector::Protocol::Page::ResourceType InspectorPageAgent::cachedResourceTypeJson(const CachedResource& cachedResource)
324 {
325     return resourceTypeJson(cachedResourceType(cachedResource));
326 }
327
328 InspectorPageAgent::InspectorPageAgent(InstrumentingAgents* instrumentingAgents, Page* page, InspectorClient* client, InspectorOverlay* overlay)
329     : InspectorAgentBase(ASCIILiteral("Page"), instrumentingAgents)
330     , m_page(page)
331     , m_client(client)
332     , m_overlay(overlay)
333     , m_lastScriptIdentifier(0)
334     , m_enabled(false)
335     , m_isFirstLayoutAfterOnLoad(false)
336     , m_originalScriptExecutionDisabled(false)
337     , m_ignoreScriptsEnabledNotification(false)
338     , m_showPaintRects(false)
339 {
340 }
341
342 void InspectorPageAgent::didCreateFrontendAndBackend(Inspector::FrontendChannel* frontendChannel, Inspector::BackendDispatcher* backendDispatcher)
343 {
344     m_frontendDispatcher = std::make_unique<Inspector::PageFrontendDispatcher>(frontendChannel);
345     m_backendDispatcher = Inspector::PageBackendDispatcher::create(backendDispatcher, this);
346 }
347
348 void InspectorPageAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
349 {
350     m_frontendDispatcher = nullptr;
351     m_backendDispatcher = nullptr;
352
353     ErrorString unused;
354     disable(unused);
355 #if ENABLE(TOUCH_EVENTS)
356     updateTouchEventEmulationInPage(false);
357 #endif
358 }
359
360 double InspectorPageAgent::timestamp()
361 {
362     return m_instrumentingAgents->inspectorEnvironment().executionStopwatch()->elapsedTime();
363 }
364
365 void InspectorPageAgent::enable(ErrorString&)
366 {
367     m_enabled = true;
368     m_instrumentingAgents->setInspectorPageAgent(this);
369
370     m_instrumentingAgents->inspectorEnvironment().executionStopwatch()->start();
371
372     if (Frame* frame = mainFrame())
373         m_originalScriptExecutionDisabled = !frame->settings().isScriptEnabled();
374 }
375
376 void InspectorPageAgent::disable(ErrorString&)
377 {
378     m_enabled = false;
379     m_scriptsToEvaluateOnLoad = nullptr;
380     m_instrumentingAgents->setInspectorPageAgent(nullptr);
381
382     ErrorString unused;
383     setScriptExecutionDisabled(unused, m_originalScriptExecutionDisabled);
384     setShowPaintRects(unused, false);
385     setEmulatedMedia(unused, emptyString());
386 }
387
388 void InspectorPageAgent::addScriptToEvaluateOnLoad(ErrorString&, const String& source, String* identifier)
389 {
390     if (!m_scriptsToEvaluateOnLoad)
391         m_scriptsToEvaluateOnLoad = InspectorObject::create();
392
393     // Assure we don't override existing ids -- m_lastScriptIdentifier could get out of sync WRT actual
394     // scripts once we restored the scripts from the cookie during navigation.
395     do {
396         *identifier = String::number(++m_lastScriptIdentifier);
397     } while (m_scriptsToEvaluateOnLoad->find(*identifier) != m_scriptsToEvaluateOnLoad->end());
398
399     m_scriptsToEvaluateOnLoad->setString(*identifier, source);
400 }
401
402 void InspectorPageAgent::removeScriptToEvaluateOnLoad(ErrorString& error, const String& identifier)
403 {
404     if (!m_scriptsToEvaluateOnLoad || m_scriptsToEvaluateOnLoad->find(identifier) == m_scriptsToEvaluateOnLoad->end()) {
405         error = ASCIILiteral("Script not found");
406         return;
407     }
408
409     m_scriptsToEvaluateOnLoad->remove(identifier);
410 }
411
412 void InspectorPageAgent::reload(ErrorString&, const bool* const optionalIgnoreCache, const String* optionalScriptToEvaluateOnLoad)
413 {
414     m_pendingScriptToEvaluateOnLoadOnce = optionalScriptToEvaluateOnLoad ? *optionalScriptToEvaluateOnLoad : "";
415     m_page->mainFrame().loader().reload(optionalIgnoreCache ? *optionalIgnoreCache : false);
416 }
417
418 void InspectorPageAgent::navigate(ErrorString&, const String& url)
419 {
420     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
421     Frame& frame = m_page->mainFrame();
422
423     ResourceRequest resourceRequest(frame.document()->completeURL(url));
424     FrameLoadRequest frameRequest(frame.document()->securityOrigin(), resourceRequest, "_self", LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::No, NewFrameOpenerPolicy::Allow, ShouldReplaceDocumentIfJavaScriptURL::ReplaceDocumentIfJavaScriptURL, ShouldOpenExternalURLsPolicy::ShouldNotAllow);
425     frame.loader().changeLocation(frameRequest);
426 }
427
428 static Ref<Inspector::Protocol::Page::Cookie> buildObjectForCookie(const Cookie& cookie)
429 {
430     return Inspector::Protocol::Page::Cookie::create()
431         .setName(cookie.name)
432         .setValue(cookie.value)
433         .setDomain(cookie.domain)
434         .setPath(cookie.path)
435         .setExpires(cookie.expires)
436         .setSize((cookie.name.length() + cookie.value.length()))
437         .setHttpOnly(cookie.httpOnly)
438         .setSecure(cookie.secure)
439         .setSession(cookie.session)
440         .release();
441 }
442
443 static Ref<Inspector::Protocol::Array<Inspector::Protocol::Page::Cookie>> buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
444 {
445     auto cookies = Inspector::Protocol::Array<Inspector::Protocol::Page::Cookie>::create();
446
447     for (const auto& cookie : cookiesList)
448         cookies->addItem(buildObjectForCookie(cookie));
449
450     return WTF::move(cookies);
451 }
452
453 static Vector<CachedResource*> cachedResourcesForFrame(Frame* frame)
454 {
455     Vector<CachedResource*> result;
456
457     for (auto& cachedResourceHandle : frame->document()->cachedResourceLoader().allCachedResources().values()) {
458         auto* cachedResource = cachedResourceHandle.get();
459         if (cachedResource->resourceRequest().hiddenFromInspector())
460             continue;
461
462         switch (cachedResource->type()) {
463         case CachedResource::ImageResource:
464             // Skip images that were not auto loaded (images disabled in the user agent).
465 #if ENABLE(SVG_FONTS)
466         case CachedResource::SVGFontResource:
467 #endif
468         case CachedResource::FontResource:
469             // Skip fonts that were referenced in CSS but never used/downloaded.
470             if (cachedResource->stillNeedsLoad())
471                 continue;
472             break;
473         default:
474             // All other CachedResource types download immediately.
475             break;
476         }
477
478         result.append(cachedResource);
479     }
480
481     return result;
482 }
483
484 static Vector<URL> allResourcesURLsForFrame(Frame* frame)
485 {
486     Vector<URL> result;
487
488     result.append(frame->loader().documentLoader()->url());
489
490     for (auto* cachedResource : cachedResourcesForFrame(frame))
491         result.append(cachedResource->url());
492
493     return result;
494 }
495
496 void InspectorPageAgent::getCookies(ErrorString&, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Page::Cookie>>& cookies)
497 {
498     // If we can get raw cookies.
499     ListHashSet<Cookie> rawCookiesList;
500
501     // If we can't get raw cookies - fall back to String representation
502     StringBuilder stringCookiesList;
503
504     // Return value to getRawCookies should be the same for every call because
505     // the return value is platform/network backend specific, and the call will
506     // always return the same true/false value.
507     bool rawCookiesImplemented = false;
508
509     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
510         Document* document = frame->document();
511
512         for (auto& url : allResourcesURLsForFrame(frame)) {
513             Vector<Cookie> docCookiesList;
514             rawCookiesImplemented = getRawCookies(document, URL(ParsedURLString, url), docCookiesList);
515
516             if (!rawCookiesImplemented) {
517                 // FIXME: We need duplication checking for the String representation of cookies.
518                 // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
519                 // because "document" is the document of the main frame of the page.
520                 stringCookiesList.append(document->cookie(ASSERT_NO_EXCEPTION));
521             } else {
522                 int cookiesSize = docCookiesList.size();
523                 for (int i = 0; i < cookiesSize; i++) {
524                     if (!rawCookiesList.contains(docCookiesList[i]))
525                         rawCookiesList.add(docCookiesList[i]);
526                 }
527             }
528         }
529     }
530
531     // FIXME: Do not return empty string/empty array. Make returns optional instead. https://bugs.webkit.org/show_bug.cgi?id=80855
532     if (rawCookiesImplemented)
533         cookies = buildArrayForCookies(rawCookiesList);
534     else
535         cookies = Inspector::Protocol::Array<Inspector::Protocol::Page::Cookie>::create();
536 }
537
538 void InspectorPageAgent::deleteCookie(ErrorString&, const String& cookieName, const String& url)
539 {
540     URL parsedURL(ParsedURLString, url);
541     for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext())
542         WebCore::deleteCookie(frame->document(), parsedURL, cookieName);
543 }
544
545 void InspectorPageAgent::getResourceTree(ErrorString&, RefPtr<Inspector::Protocol::Page::FrameResourceTree>& object)
546 {
547     object = buildObjectForFrameTree(&m_page->mainFrame());
548 }
549
550 void InspectorPageAgent::getResourceContent(ErrorString& errorString, const String& frameId, const String& url, String* content, bool* base64Encoded)
551 {
552     Frame* frame = assertFrame(errorString, frameId);
553     if (!frame)
554         return;
555
556     resourceContent(errorString, frame, URL(ParsedURLString, url), content, base64Encoded);
557 }
558
559 static bool textContentForCachedResource(CachedResource* cachedResource, String* result)
560 {
561     if (hasTextContent(cachedResource)) {
562         String content;
563         bool base64Encoded;
564         if (InspectorPageAgent::cachedResourceContent(cachedResource, result, &base64Encoded)) {
565             ASSERT(!base64Encoded);
566             return true;
567         }
568     }
569     return false;
570 }
571
572 void InspectorPageAgent::searchInResource(ErrorString&, const String& frameId, const String& url, const String& query, const bool* const optionalCaseSensitive, const bool* const optionalIsRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>>& results)
573 {
574     results = Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>::create();
575
576     bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
577     bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
578
579     Frame* frame = frameForId(frameId);
580     if (!frame)
581         return;
582
583     DocumentLoader* loader = frame->loader().documentLoader();
584     if (!loader)
585         return;
586
587     URL kurl(ParsedURLString, url);
588
589     String content;
590     bool success = false;
591     if (equalIgnoringFragmentIdentifier(kurl, loader->url()))
592         success = mainResourceContent(frame, false, &content);
593
594     if (!success) {
595         CachedResource* resource = cachedResource(frame, kurl);
596         if (resource)
597             success = textContentForCachedResource(resource, &content);
598     }
599
600     if (!success)
601         return;
602
603     results = ContentSearchUtilities::searchInTextByLines(content, query, caseSensitive, isRegex);
604 }
605
606 static Ref<Inspector::Protocol::Page::SearchResult> buildObjectForSearchResult(const String& frameId, const String& url, int matchesCount)
607 {
608     return Inspector::Protocol::Page::SearchResult::create()
609         .setUrl(url)
610         .setFrameId(frameId)
611         .setMatchesCount(matchesCount)
612         .release();
613 }
614
615 void InspectorPageAgent::searchInResources(ErrorString&, const String& text, const bool* const optionalCaseSensitive, const bool* const optionalIsRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Page::SearchResult>>& result)
616 {
617     result = Inspector::Protocol::Array<Inspector::Protocol::Page::SearchResult>::create();
618
619     bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
620     bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
621     JSC::Yarr::RegularExpression regex = ContentSearchUtilities::createSearchRegex(text, caseSensitive, isRegex);
622
623     for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
624         String content;
625
626         for (auto* cachedResource : cachedResourcesForFrame(frame)) {
627             if (textContentForCachedResource(cachedResource, &content)) {
628                 int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, content);
629                 if (matchesCount)
630                     result->addItem(buildObjectForSearchResult(frameId(frame), cachedResource->url(), matchesCount));
631             }
632         }
633
634         if (mainResourceContent(frame, false, &content)) {
635             int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, content);
636             if (matchesCount)
637                 result->addItem(buildObjectForSearchResult(frameId(frame), frame->document()->url(), matchesCount));
638         }
639     }
640 }
641
642 void InspectorPageAgent::setDocumentContent(ErrorString& errorString, const String& frameId, const String& html)
643 {
644     Frame* frame = assertFrame(errorString, frameId);
645     if (!frame)
646         return;
647
648     Document* document = frame->document();
649     if (!document) {
650         errorString = ASCIILiteral("No Document instance to set HTML for");
651         return;
652     }
653     DOMPatchSupport::patchDocument(document, html);
654 }
655
656 void InspectorPageAgent::setShowPaintRects(ErrorString&, bool show)
657 {
658     m_showPaintRects = show;
659     m_client->setShowPaintRects(show);
660
661     if (m_client->overridesShowPaintRects())
662         return;
663
664     m_overlay->setShowingPaintRects(show);
665 }
666
667 void InspectorPageAgent::getScriptExecutionStatus(ErrorString&, Inspector::PageBackendDispatcherHandler::Result* status)
668 {
669     bool disabledByScriptController = false;
670     bool disabledInSettings = false;
671     Frame* frame = mainFrame();
672     if (frame) {
673         disabledByScriptController = !frame->script().canExecuteScripts(NotAboutToExecuteScript);
674         disabledInSettings = !frame->settings().isScriptEnabled();
675     }
676
677     if (!disabledByScriptController) {
678         *status = Inspector::PageBackendDispatcherHandler::Result::Allowed;
679         return;
680     }
681
682     if (disabledInSettings)
683         *status = Inspector::PageBackendDispatcherHandler::Result::Disabled;
684     else
685         *status = Inspector::PageBackendDispatcherHandler::Result::Forbidden;
686 }
687
688 void InspectorPageAgent::setScriptExecutionDisabled(ErrorString&, bool value)
689 {
690     if (!mainFrame())
691         return;
692
693     m_ignoreScriptsEnabledNotification = true;
694     mainFrame()->settings().setScriptEnabled(!value);
695     m_ignoreScriptsEnabledNotification = false;
696 }
697
698 void InspectorPageAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld& world)
699 {
700     if (&world != &mainThreadNormalWorld())
701         return;
702
703     if (!m_frontendDispatcher)
704         return;
705
706     if (m_scriptsToEvaluateOnLoad) {
707         for (auto& keyValuePair : *m_scriptsToEvaluateOnLoad) {
708             String scriptText;
709             if (keyValuePair.value->asString(scriptText))
710                 frame->script().executeScript(scriptText);
711         }
712     }
713
714     if (!m_scriptToEvaluateOnLoadOnce.isEmpty())
715         frame->script().executeScript(m_scriptToEvaluateOnLoadOnce);
716 }
717
718 void InspectorPageAgent::domContentEventFired()
719 {
720     m_isFirstLayoutAfterOnLoad = true;
721     m_frontendDispatcher->domContentEventFired(timestamp());
722 }
723
724 void InspectorPageAgent::loadEventFired()
725 {
726     m_frontendDispatcher->loadEventFired(timestamp());
727 }
728
729 void InspectorPageAgent::frameNavigated(DocumentLoader* loader)
730 {
731     if (loader->frame()->isMainFrame()) {
732         m_scriptToEvaluateOnLoadOnce = m_pendingScriptToEvaluateOnLoadOnce;
733         m_pendingScriptToEvaluateOnLoadOnce = String();
734     }
735     m_frontendDispatcher->frameNavigated(buildObjectForFrame(loader->frame()));
736 }
737
738 void InspectorPageAgent::frameDetached(Frame& frame)
739 {
740     HashMap<Frame*, String>::iterator iterator = m_frameToIdentifier.find(&frame);
741     if (iterator != m_frameToIdentifier.end()) {
742         m_frontendDispatcher->frameDetached(iterator->value);
743         m_identifierToFrame.remove(iterator->value);
744         m_frameToIdentifier.remove(iterator);
745     }
746 }
747
748 Frame* InspectorPageAgent::mainFrame()
749 {
750     // FIXME: This should return a Frame&
751     return &m_page->mainFrame();
752 }
753
754 Frame* InspectorPageAgent::frameForId(const String& frameId)
755 {
756     return frameId.isEmpty() ? nullptr : m_identifierToFrame.get(frameId);
757 }
758
759 String InspectorPageAgent::frameId(Frame* frame)
760 {
761     if (!frame)
762         return "";
763     String identifier = m_frameToIdentifier.get(frame);
764     if (identifier.isNull()) {
765         identifier = IdentifiersFactory::createIdentifier();
766         m_frameToIdentifier.set(frame, identifier);
767         m_identifierToFrame.set(identifier, frame);
768     }
769     return identifier;
770 }
771
772 bool InspectorPageAgent::hasIdForFrame(Frame* frame) const
773 {
774     return frame && m_frameToIdentifier.contains(frame);
775 }
776
777 String InspectorPageAgent::loaderId(DocumentLoader* loader)
778 {
779     if (!loader)
780         return "";
781     String identifier = m_loaderToIdentifier.get(loader);
782     if (identifier.isNull()) {
783         identifier = IdentifiersFactory::createIdentifier();
784         m_loaderToIdentifier.set(loader, identifier);
785     }
786     return identifier;
787 }
788
789 Frame* InspectorPageAgent::findFrameWithSecurityOrigin(const String& originRawString)
790 {
791     for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
792         RefPtr<SecurityOrigin> documentOrigin = frame->document()->securityOrigin();
793         if (documentOrigin->toRawString() == originRawString)
794             return frame;
795     }
796     return nullptr;
797 }
798
799 Frame* InspectorPageAgent::assertFrame(ErrorString& errorString, const String& frameId)
800 {
801     Frame* frame = frameForId(frameId);
802     if (!frame)
803         errorString = ASCIILiteral("No frame for given id found");
804     return frame;
805 }
806
807 // static
808 DocumentLoader* InspectorPageAgent::assertDocumentLoader(ErrorString& errorString, Frame* frame)
809 {
810     FrameLoader& frameLoader = frame->loader();
811     DocumentLoader* documentLoader = frameLoader.documentLoader();
812     if (!documentLoader)
813         errorString = ASCIILiteral("No documentLoader for given frame found");
814     return documentLoader;
815 }
816
817 void InspectorPageAgent::loaderDetachedFromFrame(DocumentLoader& loader)
818 {
819     m_loaderToIdentifier.remove(&loader);
820 }
821
822 void InspectorPageAgent::frameStartedLoading(Frame& frame)
823 {
824     if (frame.isMainFrame()) {
825         auto stopwatch = m_instrumentingAgents->inspectorEnvironment().executionStopwatch();
826         stopwatch->reset();
827         stopwatch->start();
828     }
829
830     m_frontendDispatcher->frameStartedLoading(frameId(&frame));
831 }
832
833 void InspectorPageAgent::frameStoppedLoading(Frame& frame)
834 {
835     m_frontendDispatcher->frameStoppedLoading(frameId(&frame));
836 }
837
838 void InspectorPageAgent::frameScheduledNavigation(Frame& frame, double delay)
839 {
840     m_frontendDispatcher->frameScheduledNavigation(frameId(&frame), delay);
841 }
842
843 void InspectorPageAgent::frameClearedScheduledNavigation(Frame& frame)
844 {
845     m_frontendDispatcher->frameClearedScheduledNavigation(frameId(&frame));
846 }
847
848 void InspectorPageAgent::willRunJavaScriptDialog(const String& message)
849 {
850     m_frontendDispatcher->javascriptDialogOpening(message);
851 }
852
853 void InspectorPageAgent::didRunJavaScriptDialog()
854 {
855     m_frontendDispatcher->javascriptDialogClosed();
856 }
857
858 void InspectorPageAgent::didPaint(RenderObject* renderer, const LayoutRect& rect)
859 {
860     if (!m_enabled || !m_showPaintRects)
861         return;
862
863     LayoutRect absoluteRect = LayoutRect(renderer->localToAbsoluteQuad(FloatRect(rect)).boundingBox());
864     FrameView* view = renderer->document().view();
865
866     LayoutRect rootRect = absoluteRect;
867     if (!view->frame().isMainFrame()) {
868         IntRect rootViewRect = view->contentsToRootView(snappedIntRect(absoluteRect));
869         rootRect = view->frame().mainFrame().view()->rootViewToContents(rootViewRect);
870     }
871
872     if (m_client->overridesShowPaintRects()) {
873         m_client->showPaintRect(rootRect);
874         return;
875     }
876
877     m_overlay->showPaintRect(rootRect);
878 }
879
880 void InspectorPageAgent::didLayout()
881 {
882     bool isFirstLayout = m_isFirstLayoutAfterOnLoad;
883     if (isFirstLayout)
884         m_isFirstLayoutAfterOnLoad = false;
885
886     if (!m_enabled)
887         return;
888
889     m_overlay->update();
890 }
891
892 void InspectorPageAgent::didScroll()
893 {
894     if (m_enabled)
895         m_overlay->update();
896 }
897
898 void InspectorPageAgent::didRecalculateStyle()
899 {
900     if (m_enabled)
901         m_overlay->update();
902 }
903
904 void InspectorPageAgent::scriptsEnabled(bool isEnabled)
905 {
906     if (m_ignoreScriptsEnabledNotification)
907         return;
908
909     m_frontendDispatcher->scriptsEnabled(isEnabled);
910 }
911
912 Ref<Inspector::Protocol::Page::Frame> InspectorPageAgent::buildObjectForFrame(Frame* frame)
913 {
914     auto frameObject = Inspector::Protocol::Page::Frame::create()
915         .setId(frameId(frame))
916         .setLoaderId(loaderId(frame->loader().documentLoader()))
917         .setUrl(frame->document()->url().string())
918         .setMimeType(frame->loader().documentLoader()->responseMIMEType())
919         .setSecurityOrigin(frame->document()->securityOrigin()->toRawString())
920         .release();
921     if (frame->tree().parent())
922         frameObject->setParentId(frameId(frame->tree().parent()));
923     if (frame->ownerElement()) {
924         String name = frame->ownerElement()->getNameAttribute();
925         if (name.isEmpty())
926             name = frame->ownerElement()->getAttribute(HTMLNames::idAttr);
927         frameObject->setName(name);
928     }
929
930     return WTF::move(frameObject);
931 }
932
933 Ref<Inspector::Protocol::Page::FrameResourceTree> InspectorPageAgent::buildObjectForFrameTree(Frame* frame)
934 {
935     Ref<Inspector::Protocol::Page::Frame> frameObject = buildObjectForFrame(frame);
936     auto subresources = Inspector::Protocol::Array<Inspector::Protocol::Page::FrameResource>::create();
937     auto result = Inspector::Protocol::Page::FrameResourceTree::create()
938         .setFrame(WTF::move(frameObject))
939         .setResources(subresources.copyRef())
940         .release();
941
942     for (auto* cachedResource : cachedResourcesForFrame(frame)) {
943         auto resourceObject = Inspector::Protocol::Page::FrameResource::create()
944             .setUrl(cachedResource->url())
945             .setType(cachedResourceTypeJson(*cachedResource))
946             .setMimeType(cachedResource->response().mimeType())
947             .release();
948         if (cachedResource->wasCanceled())
949             resourceObject->setCanceled(true);
950         else if (cachedResource->status() == CachedResource::LoadError)
951             resourceObject->setFailed(true);
952         String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource);
953         if (!sourceMappingURL.isEmpty())
954             resourceObject->setSourceMapURL(sourceMappingURL);
955         subresources->addItem(WTF::move(resourceObject));
956     }
957
958     RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Page::FrameResourceTree>> childrenArray;
959     for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
960         if (!childrenArray) {
961             childrenArray = Inspector::Protocol::Array<Inspector::Protocol::Page::FrameResourceTree>::create();
962             result->setChildFrames(childrenArray);
963         }
964         childrenArray->addItem(buildObjectForFrameTree(child));
965     }
966     return result;
967 }
968
969 #if ENABLE(TOUCH_EVENTS)
970 void InspectorPageAgent::updateTouchEventEmulationInPage(bool enabled)
971 {
972     if (mainFrame())
973         mainFrame()->settings().setTouchEventEmulationEnabled(enabled);
974 }
975 #endif
976
977 void InspectorPageAgent::setTouchEmulationEnabled(ErrorString& error, bool enabled)
978 {
979 #if ENABLE(TOUCH_EVENTS)
980     UNUSED_PARAM(error);
981     updateTouchEventEmulationInPage(enabled);
982 #else
983     error = ASCIILiteral("Touch events emulation not supported");
984     UNUSED_PARAM(enabled);
985 #endif
986 }
987
988 void InspectorPageAgent::setEmulatedMedia(ErrorString&, const String& media)
989 {
990     if (media == m_emulatedMedia)
991         return;
992
993     m_emulatedMedia = media;
994     Document* document = m_page->mainFrame().document();
995     if (document) {
996         document->styleResolverChanged(RecalcStyleImmediately);
997         document->updateLayout();
998     }
999 }
1000
1001 void InspectorPageAgent::applyEmulatedMedia(String& media)
1002 {
1003     if (!m_emulatedMedia.isEmpty())
1004         media = m_emulatedMedia;
1005 }
1006
1007 void InspectorPageAgent::getCompositingBordersVisible(ErrorString&, bool* outParam)
1008 {
1009     *outParam = m_page->settings().showDebugBorders() || m_page->settings().showRepaintCounter();
1010 }
1011
1012 void InspectorPageAgent::setCompositingBordersVisible(ErrorString&, bool visible)
1013 {
1014     m_page->settings().setShowDebugBorders(visible);
1015     m_page->settings().setShowRepaintCounter(visible);
1016 }
1017
1018 void InspectorPageAgent::snapshotNode(ErrorString& errorString, int nodeId, String* outDataURL)
1019 {
1020     Frame* frame = mainFrame();
1021     ASSERT(frame);
1022
1023     InspectorDOMAgent* domAgent = m_instrumentingAgents->inspectorDOMAgent();
1024     ASSERT(domAgent);
1025     Node* node = domAgent->assertNode(errorString, nodeId);
1026     if (!node)
1027         return;
1028
1029     std::unique_ptr<ImageBuffer> snapshot = WebCore::snapshotNode(*frame, *node);
1030     if (!snapshot) {
1031         errorString = ASCIILiteral("Could not capture snapshot");
1032         return;
1033     }
1034
1035     *outDataURL = snapshot->toDataURL(ASCIILiteral("image/png"));
1036 }
1037
1038 void InspectorPageAgent::snapshotRect(ErrorString& errorString, int x, int y, int width, int height, const String& coordinateSystem, String* outDataURL)
1039 {
1040     Frame* frame = mainFrame();
1041     ASSERT(frame);
1042
1043     SnapshotOptions options = SnapshotOptionsNone;
1044     if (coordinateSystem == "Viewport")
1045         options |= SnapshotOptionsInViewCoordinates;
1046
1047     IntRect rectangle(x, y, width, height);
1048     std::unique_ptr<ImageBuffer> snapshot = snapshotFrameRect(*frame, rectangle, options);
1049
1050     if (!snapshot) {
1051         errorString = ASCIILiteral("Could not capture snapshot");
1052         return;
1053     }
1054
1055     *outDataURL = snapshot->toDataURL(ASCIILiteral("image/png"));
1056 }
1057
1058 void InspectorPageAgent::handleJavaScriptDialog(ErrorString& errorString, bool accept, const String* promptText)
1059 {
1060     if (!m_client->handleJavaScriptDialog(accept, promptText))
1061         errorString = ASCIILiteral("Could not handle JavaScript dialog");
1062 }
1063
1064 void InspectorPageAgent::archive(ErrorString& errorString, String* data)
1065 {
1066     Frame* frame = mainFrame();
1067     if (!frame) {
1068         errorString = ASCIILiteral("No main frame");
1069         return;
1070     }
1071
1072 #if ENABLE(WEB_ARCHIVE) && USE(CF)
1073     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::create(frame);
1074     if (!archive) {
1075         errorString = ASCIILiteral("Could not create web archive for main frame");
1076         return;
1077     }
1078
1079     RetainPtr<CFDataRef> buffer = archive->rawDataRepresentation();
1080     *data = base64Encode(CFDataGetBytePtr(buffer.get()), CFDataGetLength(buffer.get()));
1081 #else
1082     UNUSED_PARAM(data);
1083     errorString = ASCIILiteral("No support for creating archives");
1084 #endif
1085 }
1086
1087 } // namespace WebCore