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