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