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