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