| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/inspector/inspector_page_agent.h" |
| |
| #include <memory> |
| |
| #include "build/build_config.h" |
| #include "third_party/blink/public/platform/web_screen_info.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_regexp.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/document_timing.h" |
| #include "third_party/blink/renderer/core/dom/dom_implementation.h" |
| #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_client.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/report.h" |
| #include "third_party/blink/renderer/core/frame/reporting_context.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/frame/test_report_body.h" |
| #include "third_party/blink/renderer/core/frame/visual_viewport.h" |
| #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" |
| #include "third_party/blink/renderer/core/html/imports/html_import_loader.h" |
| #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h" |
| #include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h" |
| #include "third_party/blink/renderer/core/html_names.h" |
| #include "third_party/blink/renderer/core/inspector/identifiers_factory.h" |
| #include "third_party/blink/renderer/core/inspector/inspected_frames.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_css_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_resource_content_loader.h" |
| #include "third_party/blink/renderer/core/inspector/v8_inspector_string.h" |
| #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h" |
| #include "third_party/blink/renderer/core/loader/document_loader.h" |
| #include "third_party/blink/renderer/core/loader/frame_loader.h" |
| #include "third_party/blink/renderer/core/loader/idleness_detector.h" |
| #include "third_party/blink/renderer/core/loader/resource/css_style_sheet_resource.h" |
| #include "third_party/blink/renderer/core/loader/resource/script_resource.h" |
| #include "third_party/blink/renderer/core/loader/scheduled_navigation.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" |
| #include "third_party/blink/renderer/core/probe/core_probes.h" |
| #include "third_party/blink/renderer/core/style/computed_style.h" |
| #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h" |
| #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" |
| #include "third_party/blink/renderer/platform/weborigin/security_origin.h" |
| #include "third_party/blink/renderer/platform/wtf/text/base64.h" |
| #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" |
| #include "third_party/blink/renderer/platform/wtf/time.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| #include "v8/include/v8-inspector.h" |
| |
| namespace blink { |
| |
| using protocol::Response; |
| |
| namespace { |
| |
| String ClientNavigationReasonToProtocol(ClientNavigationReason reason) { |
| using ReasonEnum = |
| protocol::Page::FrameScheduledNavigationNotification::ReasonEnum; |
| switch (reason) { |
| case ClientNavigationReason::kFormSubmissionGet: |
| return ReasonEnum::FormSubmissionGet; |
| case ClientNavigationReason::kFormSubmissionPost: |
| return ReasonEnum::FormSubmissionPost; |
| case ClientNavigationReason::kHttpHeaderRefresh: |
| return ReasonEnum::HttpHeaderRefresh; |
| case ClientNavigationReason::kFrameNavigation: |
| return ReasonEnum::ScriptInitiated; |
| case ClientNavigationReason::kMetaTagRefresh: |
| return ReasonEnum::MetaTagRefresh; |
| case ClientNavigationReason::kPageBlock: |
| return ReasonEnum::PageBlockInterstitial; |
| case ClientNavigationReason::kReload: |
| return ReasonEnum::Reload; |
| default: |
| NOTREACHED(); |
| } |
| return ReasonEnum::Reload; |
| } |
| |
| Resource* CachedResource(LocalFrame* frame, |
| const KURL& url, |
| InspectorResourceContentLoader* loader) { |
| Document* document = frame->GetDocument(); |
| if (!document) |
| return nullptr; |
| Resource* cached_resource = document->Fetcher()->CachedResource(url); |
| if (!cached_resource) { |
| HeapVector<Member<Document>> all_imports = |
| InspectorPageAgent::ImportsForFrame(frame); |
| for (Document* import : all_imports) { |
| cached_resource = import->Fetcher()->CachedResource(url); |
| if (cached_resource) |
| break; |
| } |
| } |
| if (!cached_resource) { |
| cached_resource = GetMemoryCache()->ResourceForURL( |
| url, document->Fetcher()->GetCacheIdentifier()); |
| } |
| if (!cached_resource) |
| cached_resource = loader->ResourceForURL(url); |
| return cached_resource; |
| } |
| |
| std::unique_ptr<protocol::Array<String>> GetEnabledWindowFeatures( |
| const WebWindowFeatures& window_features) { |
| std::unique_ptr<protocol::Array<String>> feature_strings = |
| protocol::Array<String>::create(); |
| if (window_features.x_set) { |
| feature_strings->addItem( |
| String::Format("left=%d", static_cast<int>(window_features.x))); |
| } |
| if (window_features.y_set) { |
| feature_strings->addItem( |
| String::Format("top=%d", static_cast<int>(window_features.y))); |
| } |
| if (window_features.width_set) { |
| feature_strings->addItem( |
| String::Format("width=%d", static_cast<int>(window_features.width))); |
| } |
| if (window_features.height_set) { |
| feature_strings->addItem( |
| String::Format("height=%d", static_cast<int>(window_features.height))); |
| } |
| if (window_features.menu_bar_visible) |
| feature_strings->addItem("menubar"); |
| if (window_features.tool_bar_visible) |
| feature_strings->addItem("toolbar"); |
| if (window_features.status_bar_visible) |
| feature_strings->addItem("status"); |
| if (window_features.scrollbars_visible) |
| feature_strings->addItem("scrollbars"); |
| if (window_features.resizable) |
| feature_strings->addItem("resizable"); |
| if (window_features.noopener) |
| feature_strings->addItem("noopener"); |
| if (window_features.background) |
| feature_strings->addItem("background"); |
| if (window_features.persistent) |
| feature_strings->addItem("persistent"); |
| return feature_strings; |
| } |
| |
| } // namespace |
| |
| static bool PrepareResourceBuffer(Resource* cached_resource, |
| bool* has_zero_size) { |
| if (!cached_resource) |
| return false; |
| |
| if (cached_resource->GetDataBufferingPolicy() == kDoNotBufferData) |
| return false; |
| |
| // Zero-sized resources don't have data at all -- so fake the empty buffer, |
| // instead of indicating error by returning 0. |
| if (!cached_resource->EncodedSize()) { |
| *has_zero_size = true; |
| return true; |
| } |
| |
| *has_zero_size = false; |
| return true; |
| } |
| |
| static bool HasTextContent(Resource* cached_resource) { |
| ResourceType type = cached_resource->GetType(); |
| return type == ResourceType::kCSSStyleSheet || |
| type == ResourceType::kXSLStyleSheet || |
| type == ResourceType::kScript || type == ResourceType::kRaw || |
| type == ResourceType::kImportResource; |
| } |
| |
| static std::unique_ptr<TextResourceDecoder> CreateResourceTextDecoder( |
| const String& mime_type, |
| const String& text_encoding_name) { |
| if (!text_encoding_name.IsEmpty()) { |
| return TextResourceDecoder::Create(TextResourceDecoderOptions( |
| TextResourceDecoderOptions::kPlainTextContent, |
| WTF::TextEncoding(text_encoding_name))); |
| } |
| if (DOMImplementation::IsXMLMIMEType(mime_type)) { |
| TextResourceDecoderOptions options(TextResourceDecoderOptions::kXMLContent); |
| options.SetUseLenientXMLDecoding(); |
| return TextResourceDecoder::Create(options); |
| } |
| if (DeprecatedEqualIgnoringCase(mime_type, "text/html")) { |
| return TextResourceDecoder::Create(TextResourceDecoderOptions( |
| TextResourceDecoderOptions::kHTMLContent, UTF8Encoding())); |
| } |
| if (MIMETypeRegistry::IsSupportedJavaScriptMIMEType(mime_type) || |
| DOMImplementation::IsJSONMIMEType(mime_type)) { |
| return TextResourceDecoder::Create(TextResourceDecoderOptions( |
| TextResourceDecoderOptions::kPlainTextContent, UTF8Encoding())); |
| } |
| if (DOMImplementation::IsTextMIMEType(mime_type)) { |
| return TextResourceDecoder::Create(TextResourceDecoderOptions( |
| TextResourceDecoderOptions::kPlainTextContent, |
| WTF::TextEncoding("ISO-8859-1"))); |
| } |
| return std::unique_ptr<TextResourceDecoder>(); |
| } |
| |
| static void MaybeEncodeTextContent(const String& text_content, |
| const char* buffer_data, |
| wtf_size_t buffer_size, |
| String* result, |
| bool* base64_encoded) { |
| if (!text_content.IsNull() && |
| !text_content.Utf8(WTF::kStrictUTF8Conversion).IsNull()) { |
| *result = text_content; |
| *base64_encoded = false; |
| } else if (buffer_data) { |
| *result = Base64Encode(buffer_data, buffer_size); |
| *base64_encoded = true; |
| } else if (text_content.IsNull()) { |
| *result = ""; |
| *base64_encoded = false; |
| } else { |
| DCHECK(!text_content.Is8Bit()); |
| *result = Base64Encode(text_content.Utf8(WTF::kLenientUTF8Conversion)); |
| *base64_encoded = true; |
| } |
| } |
| |
| static void MaybeEncodeTextContent(const String& text_content, |
| scoped_refptr<const SharedBuffer> buffer, |
| String* result, |
| bool* base64_encoded) { |
| if (!buffer) { |
| return MaybeEncodeTextContent(text_content, nullptr, 0, result, |
| base64_encoded); |
| } |
| |
| const SharedBuffer::DeprecatedFlatData flat_buffer(std::move(buffer)); |
| return MaybeEncodeTextContent(text_content, flat_buffer.Data(), |
| SafeCast<wtf_size_t>(flat_buffer.size()), |
| result, base64_encoded); |
| } |
| |
| // static |
| KURL InspectorPageAgent::UrlWithoutFragment(const KURL& url) { |
| KURL result = url; |
| result.RemoveFragmentIdentifier(); |
| return result; |
| } |
| |
| // static |
| bool InspectorPageAgent::SharedBufferContent( |
| scoped_refptr<const SharedBuffer> buffer, |
| const String& mime_type, |
| const String& text_encoding_name, |
| String* result, |
| bool* base64_encoded) { |
| if (!buffer) |
| return false; |
| |
| String text_content; |
| std::unique_ptr<TextResourceDecoder> decoder = |
| CreateResourceTextDecoder(mime_type, text_encoding_name); |
| WTF::TextEncoding encoding(text_encoding_name); |
| |
| const SharedBuffer::DeprecatedFlatData flat_buffer(std::move(buffer)); |
| if (decoder) { |
| text_content = decoder->Decode(flat_buffer.Data(), flat_buffer.size()); |
| text_content = text_content + decoder->Flush(); |
| } else if (encoding.IsValid()) { |
| text_content = encoding.Decode(flat_buffer.Data(), |
| SafeCast<wtf_size_t>(flat_buffer.size())); |
| } |
| |
| MaybeEncodeTextContent(text_content, flat_buffer.Data(), |
| SafeCast<wtf_size_t>(flat_buffer.size()), result, |
| base64_encoded); |
| return true; |
| } |
| |
| // static |
| bool InspectorPageAgent::CachedResourceContent(Resource* cached_resource, |
| String* result, |
| bool* base64_encoded) { |
| bool has_zero_size; |
| if (!PrepareResourceBuffer(cached_resource, &has_zero_size)) |
| return false; |
| |
| if (!HasTextContent(cached_resource)) { |
| scoped_refptr<const SharedBuffer> buffer = |
| has_zero_size ? SharedBuffer::Create() |
| : cached_resource->ResourceBuffer(); |
| if (!buffer) |
| return false; |
| |
| const SharedBuffer::DeprecatedFlatData flat_buffer(std::move(buffer)); |
| *result = Base64Encode(flat_buffer.Data(), |
| SafeCast<wtf_size_t>(flat_buffer.size())); |
| *base64_encoded = true; |
| return true; |
| } |
| |
| if (has_zero_size) { |
| *result = ""; |
| *base64_encoded = false; |
| return true; |
| } |
| |
| DCHECK(cached_resource); |
| switch (cached_resource->GetType()) { |
| case blink::ResourceType::kCSSStyleSheet: |
| MaybeEncodeTextContent( |
| ToCSSStyleSheetResource(cached_resource) |
| ->SheetText(nullptr, CSSStyleSheetResource::MIMETypeCheck::kLax), |
| cached_resource->ResourceBuffer(), result, base64_encoded); |
| return true; |
| case blink::ResourceType::kScript: |
| MaybeEncodeTextContent( |
| ToScriptResource(cached_resource)->TextForInspector(), |
| cached_resource->ResourceBuffer(), result, base64_encoded); |
| return true; |
| default: |
| String text_encoding_name = |
| cached_resource->GetResponse().TextEncodingName(); |
| if (text_encoding_name.IsEmpty() && |
| cached_resource->GetType() != blink::ResourceType::kRaw) |
| text_encoding_name = "WinLatin1"; |
| return InspectorPageAgent::SharedBufferContent( |
| cached_resource->ResourceBuffer(), |
| cached_resource->GetResponse().MimeType(), text_encoding_name, result, |
| base64_encoded); |
| } |
| } |
| |
| InspectorPageAgent* InspectorPageAgent::Create( |
| InspectedFrames* inspected_frames, |
| Client* client, |
| InspectorResourceContentLoader* resource_content_loader, |
| v8_inspector::V8InspectorSession* v8_session) { |
| return MakeGarbageCollected<InspectorPageAgent>( |
| inspected_frames, client, resource_content_loader, v8_session); |
| } |
| |
| String InspectorPageAgent::ResourceTypeJson( |
| InspectorPageAgent::ResourceType resource_type) { |
| switch (resource_type) { |
| case kDocumentResource: |
| return protocol::Network::ResourceTypeEnum::Document; |
| case kFontResource: |
| return protocol::Network::ResourceTypeEnum::Font; |
| case kImageResource: |
| return protocol::Network::ResourceTypeEnum::Image; |
| case kMediaResource: |
| return protocol::Network::ResourceTypeEnum::Media; |
| case kScriptResource: |
| return protocol::Network::ResourceTypeEnum::Script; |
| case kStylesheetResource: |
| return protocol::Network::ResourceTypeEnum::Stylesheet; |
| case kTextTrackResource: |
| return protocol::Network::ResourceTypeEnum::TextTrack; |
| case kXHRResource: |
| return protocol::Network::ResourceTypeEnum::XHR; |
| case kFetchResource: |
| return protocol::Network::ResourceTypeEnum::Fetch; |
| case kEventSourceResource: |
| return protocol::Network::ResourceTypeEnum::EventSource; |
| case kWebSocketResource: |
| return protocol::Network::ResourceTypeEnum::WebSocket; |
| case kManifestResource: |
| return protocol::Network::ResourceTypeEnum::Manifest; |
| case kSignedExchangeResource: |
| return protocol::Network::ResourceTypeEnum::SignedExchange; |
| case kOtherResource: |
| return protocol::Network::ResourceTypeEnum::Other; |
| } |
| return protocol::Network::ResourceTypeEnum::Other; |
| } |
| |
| InspectorPageAgent::ResourceType InspectorPageAgent::ToResourceType( |
| const blink::ResourceType resource_type) { |
| switch (resource_type) { |
| case blink::ResourceType::kImage: |
| return InspectorPageAgent::kImageResource; |
| case blink::ResourceType::kFont: |
| return InspectorPageAgent::kFontResource; |
| case blink::ResourceType::kAudio: |
| case blink::ResourceType::kVideo: |
| return InspectorPageAgent::kMediaResource; |
| case blink::ResourceType::kManifest: |
| return InspectorPageAgent::kManifestResource; |
| case blink::ResourceType::kTextTrack: |
| return InspectorPageAgent::kTextTrackResource; |
| case blink::ResourceType::kCSSStyleSheet: |
| // Fall through. |
| case blink::ResourceType::kXSLStyleSheet: |
| return InspectorPageAgent::kStylesheetResource; |
| case blink::ResourceType::kScript: |
| return InspectorPageAgent::kScriptResource; |
| case blink::ResourceType::kImportResource: |
| return InspectorPageAgent::kDocumentResource; |
| default: |
| break; |
| } |
| return InspectorPageAgent::kOtherResource; |
| } |
| |
| String InspectorPageAgent::CachedResourceTypeJson( |
| const Resource& cached_resource) { |
| return ResourceTypeJson(ToResourceType(cached_resource.GetType())); |
| } |
| |
| InspectorPageAgent::InspectorPageAgent( |
| InspectedFrames* inspected_frames, |
| Client* client, |
| InspectorResourceContentLoader* resource_content_loader, |
| v8_inspector::V8InspectorSession* v8_session) |
| : inspected_frames_(inspected_frames), |
| v8_session_(v8_session), |
| client_(client), |
| inspector_resource_content_loader_(resource_content_loader), |
| resource_content_loader_client_id_( |
| resource_content_loader->CreateClientId()), |
| enabled_(&agent_state_, /*default_value=*/false), |
| screencast_enabled_(&agent_state_, /*default_value=*/false), |
| lifecycle_events_enabled_(&agent_state_, /*default_value=*/false), |
| bypass_csp_enabled_(&agent_state_, /*default_value=*/false), |
| scripts_to_evaluate_on_load_(&agent_state_, |
| /*default_value=*/WTF::String()), |
| worlds_to_evaluate_on_load_(&agent_state_, |
| /*default_value=*/WTF::String()), |
| standard_font_family_(&agent_state_, /*default_value=*/WTF::String()), |
| fixed_font_family_(&agent_state_, /*default_value=*/WTF::String()), |
| serif_font_family_(&agent_state_, /*default_value=*/WTF::String()), |
| sans_serif_font_family_(&agent_state_, /*default_value=*/WTF::String()), |
| cursive_font_family_(&agent_state_, /*default_value=*/WTF::String()), |
| fantasy_font_family_(&agent_state_, /*default_value=*/WTF::String()), |
| pictograph_font_family_(&agent_state_, /*default_value=*/WTF::String()), |
| standard_font_size_(&agent_state_, /*default_value=*/0), |
| fixed_font_size_(&agent_state_, /*default_value=*/0), |
| produce_compilation_cache_(&agent_state_, /*default_value=*/false) {} |
| |
| void InspectorPageAgent::Restore() { |
| if (enabled_.Get()) |
| enable(); |
| if (bypass_csp_enabled_.Get()) |
| setBypassCSP(true); |
| // Re-apply generic fonts overrides. |
| bool notifyGenericFontFamilyChange = false; |
| LocalFrame* frame = inspected_frames_->Root(); |
| auto* settings = frame->GetSettings(); |
| if (settings) { |
| auto& family_settings = settings->GetGenericFontFamilySettings(); |
| if (!standard_font_family_.Get().IsNull()) { |
| family_settings.UpdateStandard(AtomicString(standard_font_family_.Get())); |
| notifyGenericFontFamilyChange = true; |
| } |
| if (!fixed_font_family_.Get().IsNull()) { |
| family_settings.UpdateFixed(AtomicString(fixed_font_family_.Get())); |
| notifyGenericFontFamilyChange = true; |
| } |
| if (!serif_font_family_.Get().IsNull()) { |
| family_settings.UpdateSerif(AtomicString(serif_font_family_.Get())); |
| notifyGenericFontFamilyChange = true; |
| } |
| if (!sans_serif_font_family_.Get().IsNull()) { |
| family_settings.UpdateSansSerif( |
| AtomicString(sans_serif_font_family_.Get())); |
| notifyGenericFontFamilyChange = true; |
| } |
| if (!cursive_font_family_.Get().IsNull()) { |
| family_settings.UpdateCursive(AtomicString(cursive_font_family_.Get())); |
| notifyGenericFontFamilyChange = true; |
| } |
| if (!fantasy_font_family_.Get().IsNull()) { |
| family_settings.UpdateFantasy(AtomicString(fantasy_font_family_.Get())); |
| notifyGenericFontFamilyChange = true; |
| } |
| if (!pictograph_font_family_.Get().IsNull()) { |
| family_settings.UpdatePictograph( |
| AtomicString(pictograph_font_family_.Get())); |
| notifyGenericFontFamilyChange = true; |
| } |
| if (notifyGenericFontFamilyChange) |
| settings->NotifyGenericFontFamilyChange(); |
| } |
| |
| // Re-apply default font size overrides. |
| if (settings) { |
| if (standard_font_size_.Get() != 0) |
| settings->SetDefaultFontSize(standard_font_size_.Get()); |
| if (fixed_font_size_.Get() != 0) |
| settings->SetDefaultFixedFontSize(fixed_font_size_.Get()); |
| } |
| } |
| |
| Response InspectorPageAgent::enable() { |
| enabled_.Set(true); |
| instrumenting_agents_->addInspectorPageAgent(this); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::disable() { |
| agent_state_.ClearAllFields(); |
| script_to_evaluate_on_load_once_ = String(); |
| pending_script_to_evaluate_on_load_once_ = String(); |
| instrumenting_agents_->removeInspectorPageAgent(this); |
| inspector_resource_content_loader_->Cancel( |
| resource_content_loader_client_id_); |
| |
| stopScreencast(); |
| |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::addScriptToEvaluateOnNewDocument( |
| const String& source, |
| Maybe<String> world_name, |
| String* identifier) { |
| std::vector<WTF::String> keys = scripts_to_evaluate_on_load_.Keys(); |
| auto result = std::max_element( |
| keys.begin(), keys.end(), [](const WTF::String& a, const WTF::String& b) { |
| return Decimal::FromString(a) < Decimal::FromString(b); |
| }); |
| if (result == keys.end()) { |
| *identifier = String::Number(1); |
| } else { |
| *identifier = String::Number(Decimal::FromString(*result).ToDouble() + 1); |
| } |
| |
| scripts_to_evaluate_on_load_.Set(*identifier, source); |
| worlds_to_evaluate_on_load_.Set(*identifier, world_name.fromMaybe("")); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::removeScriptToEvaluateOnNewDocument( |
| const String& identifier) { |
| if (scripts_to_evaluate_on_load_.Get(identifier).IsNull()) |
| return Response::Error("Script not found"); |
| scripts_to_evaluate_on_load_.Clear(identifier); |
| worlds_to_evaluate_on_load_.Clear(identifier); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::addScriptToEvaluateOnLoad(const String& source, |
| String* identifier) { |
| return addScriptToEvaluateOnNewDocument(source, Maybe<String>(""), |
| identifier); |
| } |
| |
| Response InspectorPageAgent::removeScriptToEvaluateOnLoad( |
| const String& identifier) { |
| return removeScriptToEvaluateOnNewDocument(identifier); |
| } |
| |
| Response InspectorPageAgent::setLifecycleEventsEnabled(bool enabled) { |
| lifecycle_events_enabled_.Set(enabled); |
| if (!enabled) |
| return Response::OK(); |
| |
| for (LocalFrame* frame : *inspected_frames_) { |
| Document* document = frame->GetDocument(); |
| DocumentLoader* loader = frame->Loader().GetDocumentLoader(); |
| if (!document || !loader) |
| continue; |
| |
| DocumentLoadTiming& timing = loader->GetTiming(); |
| TimeTicks commit_timestamp = timing.ResponseEnd(); |
| if (!commit_timestamp.is_null()) { |
| LifecycleEvent(frame, loader, "commit", |
| TimeTicksInSeconds(commit_timestamp)); |
| } |
| |
| TimeTicks domcontentloaded_timestamp = |
| document->GetTiming().DomContentLoadedEventEnd(); |
| if (!domcontentloaded_timestamp.is_null()) { |
| LifecycleEvent(frame, loader, "DOMContentLoaded", |
| TimeTicksInSeconds(domcontentloaded_timestamp)); |
| } |
| |
| TimeTicks load_timestamp = timing.LoadEventEnd(); |
| if (!load_timestamp.is_null()) { |
| LifecycleEvent(frame, loader, "load", TimeTicksInSeconds(load_timestamp)); |
| } |
| |
| IdlenessDetector* idleness_detector = frame->GetIdlenessDetector(); |
| TimeTicks network_almost_idle_timestamp = |
| idleness_detector->GetNetworkAlmostIdleTime(); |
| if (!network_almost_idle_timestamp.is_null()) { |
| LifecycleEvent(frame, loader, "networkAlmostIdle", |
| TimeTicksInSeconds(network_almost_idle_timestamp)); |
| } |
| TimeTicks network_idle_timestamp = idleness_detector->GetNetworkIdleTime(); |
| if (!network_idle_timestamp.is_null()) { |
| LifecycleEvent(frame, loader, "networkIdle", |
| TimeTicksInSeconds(network_idle_timestamp)); |
| } |
| } |
| |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::setAdBlockingEnabled(bool enable) { |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::reload( |
| Maybe<bool> optional_bypass_cache, |
| Maybe<String> optional_script_to_evaluate_on_load) { |
| pending_script_to_evaluate_on_load_once_ = |
| optional_script_to_evaluate_on_load.fromMaybe(""); |
| v8_session_->setSkipAllPauses(true); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::stopLoading() { |
| return Response::OK(); |
| } |
| |
| static void CachedResourcesForDocument(Document* document, |
| HeapVector<Member<Resource>>& result, |
| bool skip_xhrs) { |
| const ResourceFetcher::DocumentResourceMap& all_resources = |
| document->Fetcher()->AllResources(); |
| for (const auto& resource : all_resources) { |
| Resource* cached_resource = resource.value.Get(); |
| if (!cached_resource) |
| continue; |
| |
| // Skip images that were not auto loaded (images disabled in the user |
| // agent), fonts that were referenced in CSS but never used/downloaded, etc. |
| if (cached_resource->StillNeedsLoad()) |
| continue; |
| if (cached_resource->GetType() == ResourceType::kRaw && skip_xhrs) |
| continue; |
| result.push_back(cached_resource); |
| } |
| } |
| |
| // static |
| HeapVector<Member<Document>> InspectorPageAgent::ImportsForFrame( |
| LocalFrame* frame) { |
| HeapVector<Member<Document>> result; |
| Document* root_document = frame->GetDocument(); |
| |
| if (HTMLImportsController* controller = root_document->ImportsController()) { |
| for (wtf_size_t i = 0; i < controller->LoaderCount(); ++i) { |
| if (Document* document = controller->LoaderAt(i)->GetDocument()) |
| result.push_back(document); |
| } |
| } |
| |
| return result; |
| } |
| |
| static HeapVector<Member<Resource>> CachedResourcesForFrame(LocalFrame* frame, |
| bool skip_xhrs) { |
| HeapVector<Member<Resource>> result; |
| Document* root_document = frame->GetDocument(); |
| HeapVector<Member<Document>> loaders = |
| InspectorPageAgent::ImportsForFrame(frame); |
| |
| CachedResourcesForDocument(root_document, result, skip_xhrs); |
| for (wtf_size_t i = 0; i < loaders.size(); ++i) |
| CachedResourcesForDocument(loaders[i], result, skip_xhrs); |
| |
| return result; |
| } |
| |
| Response InspectorPageAgent::getResourceTree( |
| std::unique_ptr<protocol::Page::FrameResourceTree>* object) { |
| *object = BuildObjectForResourceTree(inspected_frames_->Root()); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::getFrameTree( |
| std::unique_ptr<protocol::Page::FrameTree>* object) { |
| *object = BuildObjectForFrameTree(inspected_frames_->Root()); |
| return Response::OK(); |
| } |
| |
| void InspectorPageAgent::GetResourceContentAfterResourcesContentLoaded( |
| const String& frame_id, |
| const String& url, |
| std::unique_ptr<GetResourceContentCallback> callback) { |
| LocalFrame* frame = |
| IdentifiersFactory::FrameById(inspected_frames_, frame_id); |
| if (!frame) { |
| callback->sendFailure(Response::Error("No frame for given id found")); |
| return; |
| } |
| String content; |
| bool base64_encoded; |
| if (InspectorPageAgent::CachedResourceContent( |
| CachedResource(frame, KURL(url), inspector_resource_content_loader_), |
| &content, &base64_encoded)) |
| callback->sendSuccess(content, base64_encoded); |
| else |
| callback->sendFailure(Response::Error("No resource with given URL found")); |
| } |
| |
| void InspectorPageAgent::getResourceContent( |
| const String& frame_id, |
| const String& url, |
| std::unique_ptr<GetResourceContentCallback> callback) { |
| if (!enabled_.Get()) { |
| callback->sendFailure(Response::Error("Agent is not enabled.")); |
| return; |
| } |
| inspector_resource_content_loader_->EnsureResourcesContentLoaded( |
| resource_content_loader_client_id_, |
| WTF::Bind( |
| &InspectorPageAgent::GetResourceContentAfterResourcesContentLoaded, |
| WrapPersistent(this), frame_id, url, |
| WTF::Passed(std::move(callback)))); |
| } |
| |
| void InspectorPageAgent::SearchContentAfterResourcesContentLoaded( |
| const String& frame_id, |
| const String& url, |
| const String& query, |
| bool case_sensitive, |
| bool is_regex, |
| std::unique_ptr<SearchInResourceCallback> callback) { |
| LocalFrame* frame = |
| IdentifiersFactory::FrameById(inspected_frames_, frame_id); |
| if (!frame) { |
| callback->sendFailure(Response::Error("No frame for given id found")); |
| return; |
| } |
| String content; |
| bool base64_encoded; |
| if (!InspectorPageAgent::CachedResourceContent( |
| CachedResource(frame, KURL(url), inspector_resource_content_loader_), |
| &content, &base64_encoded)) { |
| callback->sendFailure(Response::Error("No resource with given URL found")); |
| return; |
| } |
| |
| auto matches = v8_session_->searchInTextByLines( |
| ToV8InspectorStringView(content), ToV8InspectorStringView(query), |
| case_sensitive, is_regex); |
| auto results = protocol::Array< |
| v8_inspector::protocol::Debugger::API::SearchMatch>::create(); |
| for (size_t i = 0; i < matches.size(); ++i) |
| results->addItem(std::move(matches[i])); |
| callback->sendSuccess(std::move(results)); |
| } |
| |
| void InspectorPageAgent::searchInResource( |
| const String& frame_id, |
| const String& url, |
| const String& query, |
| Maybe<bool> optional_case_sensitive, |
| Maybe<bool> optional_is_regex, |
| std::unique_ptr<SearchInResourceCallback> callback) { |
| if (!enabled_.Get()) { |
| callback->sendFailure(Response::Error("Agent is not enabled.")); |
| return; |
| } |
| inspector_resource_content_loader_->EnsureResourcesContentLoaded( |
| resource_content_loader_client_id_, |
| WTF::Bind(&InspectorPageAgent::SearchContentAfterResourcesContentLoaded, |
| WrapPersistent(this), frame_id, url, query, |
| optional_case_sensitive.fromMaybe(false), |
| optional_is_regex.fromMaybe(false), |
| WTF::Passed(std::move(callback)))); |
| } |
| |
| Response InspectorPageAgent::setBypassCSP(bool enabled) { |
| LocalFrame* frame = inspected_frames_->Root(); |
| frame->GetSettings()->SetBypassCSP(enabled); |
| bypass_csp_enabled_.Set(enabled); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::setDocumentContent(const String& frame_id, |
| const String& html) { |
| LocalFrame* frame = |
| IdentifiersFactory::FrameById(inspected_frames_, frame_id); |
| if (!frame) |
| return Response::Error("No frame for given id found"); |
| |
| Document* document = frame->GetDocument(); |
| if (!document) |
| return Response::Error("No Document instance to set HTML for"); |
| document->SetContent(html); |
| return Response::OK(); |
| } |
| |
| void InspectorPageAgent::DidNavigateWithinDocument(LocalFrame* frame) { |
| Document* document = frame->GetDocument(); |
| if (document) { |
| return GetFrontend()->navigatedWithinDocument( |
| IdentifiersFactory::FrameId(frame), document->Url()); |
| } |
| } |
| |
| void InspectorPageAgent::DidClearDocumentOfWindowObject(LocalFrame* frame) { |
| if (!GetFrontend()) |
| return; |
| std::vector<WTF::String> keys = scripts_to_evaluate_on_load_.Keys(); |
| std::sort(keys.begin(), keys.end(), |
| [](const WTF::String& a, const WTF::String& b) { |
| return Decimal::FromString(a) < Decimal::FromString(b); |
| }); |
| |
| HashMap<String, int> world_id_by_name; |
| for (const WTF::String& key : keys) { |
| const String source = scripts_to_evaluate_on_load_.Get(key); |
| const String world_name = worlds_to_evaluate_on_load_.Get(key); |
| if (world_name.IsEmpty()) { |
| frame->GetScriptController().ExecuteScriptInMainWorld( |
| source, ScriptSourceLocationType::kUnknown, |
| ScriptController::kExecuteScriptWhenScriptsDisabled); |
| continue; |
| } |
| |
| auto it = world_id_by_name.find(world_name); |
| int world_id = 0; |
| if (it != world_id_by_name.end()) { |
| world_id = it->value; |
| } else { |
| scoped_refptr<DOMWrapperWorld> world = |
| frame->GetScriptController().CreateNewInspectorIsolatedWorld( |
| world_name); |
| if (!world) |
| continue; |
| world_id = world->GetWorldId(); |
| world_id_by_name.Set(world_name, world_id); |
| |
| scoped_refptr<SecurityOrigin> security_origin = |
| frame->GetSecurityContext()->GetSecurityOrigin()->IsolatedCopy(); |
| security_origin->GrantUniversalAccess(); |
| DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(world_id, |
| security_origin); |
| } |
| |
| // Note: An error event in an isolated world will never be dispatched to |
| // a foreign world. |
| v8::HandleScope handle_scope(V8PerIsolateData::MainThreadIsolate()); |
| frame->GetScriptController().ExecuteScriptInIsolatedWorld( |
| world_id, source, KURL(), SanitizeScriptErrors::kSanitize); |
| } |
| |
| if (!script_to_evaluate_on_load_once_.IsEmpty()) { |
| frame->GetScriptController().ExecuteScriptInMainWorld( |
| script_to_evaluate_on_load_once_, ScriptSourceLocationType::kUnknown, |
| ScriptController::kExecuteScriptWhenScriptsDisabled); |
| } |
| } |
| |
| void InspectorPageAgent::DOMContentLoadedEventFired(LocalFrame* frame) { |
| double timestamp = CurrentTimeTicksInSeconds(); |
| if (frame == inspected_frames_->Root()) |
| GetFrontend()->domContentEventFired(timestamp); |
| DocumentLoader* loader = frame->Loader().GetDocumentLoader(); |
| LifecycleEvent(frame, loader, "DOMContentLoaded", timestamp); |
| } |
| |
| void InspectorPageAgent::LoadEventFired(LocalFrame* frame) { |
| double timestamp = CurrentTimeTicksInSeconds(); |
| if (frame == inspected_frames_->Root()) |
| GetFrontend()->loadEventFired(timestamp); |
| DocumentLoader* loader = frame->Loader().GetDocumentLoader(); |
| LifecycleEvent(frame, loader, "load", timestamp); |
| } |
| |
| void InspectorPageAgent::WillCommitLoad(LocalFrame*, DocumentLoader* loader) { |
| if (loader->GetFrame() == inspected_frames_->Root()) { |
| script_to_evaluate_on_load_once_ = pending_script_to_evaluate_on_load_once_; |
| pending_script_to_evaluate_on_load_once_ = String(); |
| } |
| GetFrontend()->frameNavigated(BuildObjectForFrame(loader->GetFrame())); |
| } |
| |
| void InspectorPageAgent::FrameAttachedToParent(LocalFrame* frame) { |
| Frame* parent_frame = frame->Tree().Parent(); |
| std::unique_ptr<SourceLocation> location = |
| SourceLocation::CaptureWithFullStackTrace(); |
| GetFrontend()->frameAttached( |
| IdentifiersFactory::FrameId(frame), |
| IdentifiersFactory::FrameId(parent_frame), |
| location ? location->BuildInspectorObject() : nullptr); |
| // Some network events referencing this frame will be reported from the |
| // browser, so make sure to deliver FrameAttached without buffering, |
| // so it gets to the front-end first. |
| GetFrontend()->flush(); |
| } |
| |
| void InspectorPageAgent::FrameDetachedFromParent(LocalFrame* frame) { |
| GetFrontend()->frameDetached(IdentifiersFactory::FrameId(frame)); |
| } |
| |
| bool InspectorPageAgent::ScreencastEnabled() { |
| return enabled_.Get() && screencast_enabled_.Get(); |
| } |
| |
| void InspectorPageAgent::FrameStartedLoading(LocalFrame* frame) { |
| GetFrontend()->frameStartedLoading(IdentifiersFactory::FrameId(frame)); |
| } |
| |
| void InspectorPageAgent::FrameStoppedLoading(LocalFrame* frame) { |
| GetFrontend()->frameStoppedLoading(IdentifiersFactory::FrameId(frame)); |
| } |
| |
| void InspectorPageAgent::FrameScheduledNavigation( |
| LocalFrame* frame, |
| const KURL& url, |
| double delay, |
| ClientNavigationReason reason) { |
| GetFrontend()->frameScheduledNavigation( |
| IdentifiersFactory::FrameId(frame), delay, |
| ClientNavigationReasonToProtocol(reason), url.GetString()); |
| } |
| |
| void InspectorPageAgent::FrameClearedScheduledNavigation(LocalFrame* frame) { |
| GetFrontend()->frameClearedScheduledNavigation( |
| IdentifiersFactory::FrameId(frame)); |
| } |
| |
| void InspectorPageAgent::WillRunJavaScriptDialog() { |
| GetFrontend()->flush(); |
| } |
| |
| void InspectorPageAgent::DidRunJavaScriptDialog() { |
| GetFrontend()->flush(); |
| } |
| |
| void InspectorPageAgent::DidResizeMainFrame() { |
| if (!inspected_frames_->Root()->IsMainFrame()) |
| return; |
| #if !defined(OS_ANDROID) |
| PageLayoutInvalidated(true); |
| #endif |
| GetFrontend()->frameResized(); |
| } |
| |
| void InspectorPageAgent::DidChangeViewport() { |
| PageLayoutInvalidated(false); |
| } |
| |
| void InspectorPageAgent::LifecycleEvent(LocalFrame* frame, |
| DocumentLoader* loader, |
| const char* name, |
| double timestamp) { |
| if (!loader || !lifecycle_events_enabled_.Get()) |
| return; |
| GetFrontend()->lifecycleEvent(IdentifiersFactory::FrameId(frame), |
| IdentifiersFactory::LoaderId(loader), name, |
| timestamp); |
| } |
| |
| void InspectorPageAgent::PaintTiming(Document* document, |
| const char* name, |
| double timestamp) { |
| LocalFrame* frame = document->GetFrame(); |
| DocumentLoader* loader = frame->Loader().GetDocumentLoader(); |
| LifecycleEvent(frame, loader, name, timestamp); |
| } |
| |
| void InspectorPageAgent::Will(const probe::UpdateLayout&) {} |
| |
| void InspectorPageAgent::Did(const probe::UpdateLayout&) { |
| PageLayoutInvalidated(false); |
| } |
| |
| void InspectorPageAgent::Will(const probe::RecalculateStyle&) {} |
| |
| void InspectorPageAgent::Did(const probe::RecalculateStyle&) { |
| PageLayoutInvalidated(false); |
| } |
| |
| void InspectorPageAgent::PageLayoutInvalidated(bool resized) { |
| if (enabled_.Get() && client_) |
| client_->PageLayoutInvalidated(resized); |
| } |
| |
| void InspectorPageAgent::WindowOpen(Document* document, |
| const String& url, |
| const AtomicString& window_name, |
| const WebWindowFeatures& window_features, |
| bool user_gesture) { |
| KURL completed_url = url.IsEmpty() ? BlankURL() : document->CompleteURL(url); |
| GetFrontend()->windowOpen(completed_url.GetString(), window_name, |
| GetEnabledWindowFeatures(window_features), |
| user_gesture); |
| } |
| |
| std::unique_ptr<protocol::Page::Frame> InspectorPageAgent::BuildObjectForFrame( |
| LocalFrame* frame) { |
| DocumentLoader* loader = frame->Loader().GetDocumentLoader(); |
| std::unique_ptr<protocol::Page::Frame> frame_object = |
| protocol::Page::Frame::create() |
| .setId(IdentifiersFactory::FrameId(frame)) |
| .setLoaderId(IdentifiersFactory::LoaderId(loader)) |
| .setUrl(UrlWithoutFragment(loader->Url()).GetString()) |
| .setMimeType(frame->Loader().GetDocumentLoader()->MimeType()) |
| .setSecurityOrigin( |
| SecurityOrigin::Create(loader->Url())->ToRawString()) |
| .build(); |
| Frame* parent_frame = frame->Tree().Parent(); |
| if (parent_frame) { |
| frame_object->setParentId(IdentifiersFactory::FrameId(parent_frame)); |
| AtomicString name = frame->Tree().GetName(); |
| if (name.IsEmpty() && frame->DeprecatedLocalOwner()) |
| name = frame->DeprecatedLocalOwner()->getAttribute(html_names::kIdAttr); |
| frame_object->setName(name); |
| } |
| if (loader && !loader->UnreachableURL().IsEmpty()) |
| frame_object->setUnreachableUrl(loader->UnreachableURL().GetString()); |
| return frame_object; |
| } |
| |
| std::unique_ptr<protocol::Page::FrameTree> |
| InspectorPageAgent::BuildObjectForFrameTree(LocalFrame* frame) { |
| std::unique_ptr<protocol::Page::FrameTree> result = |
| protocol::Page::FrameTree::create() |
| .setFrame(BuildObjectForFrame(frame)) |
| .build(); |
| |
| std::unique_ptr<protocol::Array<protocol::Page::FrameTree>> children_array; |
| for (Frame* child = frame->Tree().FirstChild(); child; |
| child = child->Tree().NextSibling()) { |
| if (!child->IsLocalFrame()) |
| continue; |
| if (!children_array) |
| children_array = protocol::Array<protocol::Page::FrameTree>::create(); |
| children_array->addItem(BuildObjectForFrameTree(ToLocalFrame(child))); |
| } |
| result->setChildFrames(std::move(children_array)); |
| return result; |
| } |
| |
| std::unique_ptr<protocol::Page::FrameResourceTree> |
| InspectorPageAgent::BuildObjectForResourceTree(LocalFrame* frame) { |
| std::unique_ptr<protocol::Page::Frame> frame_object = |
| BuildObjectForFrame(frame); |
| std::unique_ptr<protocol::Array<protocol::Page::FrameResource>> subresources = |
| protocol::Array<protocol::Page::FrameResource>::create(); |
| |
| HeapVector<Member<Resource>> all_resources = |
| CachedResourcesForFrame(frame, true); |
| for (Resource* cached_resource : all_resources) { |
| std::unique_ptr<protocol::Page::FrameResource> resource_object = |
| protocol::Page::FrameResource::create() |
| .setUrl(UrlWithoutFragment(cached_resource->Url()).GetString()) |
| .setType(CachedResourceTypeJson(*cached_resource)) |
| .setMimeType(cached_resource->GetResponse().MimeType()) |
| .setContentSize(cached_resource->GetResponse().DecodedBodyLength()) |
| .build(); |
| double last_modified = cached_resource->GetResponse().LastModified(); |
| if (!std::isnan(last_modified)) |
| resource_object->setLastModified(last_modified); |
| if (cached_resource->WasCanceled()) |
| resource_object->setCanceled(true); |
| else if (cached_resource->GetStatus() == ResourceStatus::kLoadError) |
| resource_object->setFailed(true); |
| subresources->addItem(std::move(resource_object)); |
| } |
| |
| HeapVector<Member<Document>> all_imports = |
| InspectorPageAgent::ImportsForFrame(frame); |
| for (Document* import : all_imports) { |
| std::unique_ptr<protocol::Page::FrameResource> resource_object = |
| protocol::Page::FrameResource::create() |
| .setUrl(UrlWithoutFragment(import->Url()).GetString()) |
| .setType(ResourceTypeJson(InspectorPageAgent::kDocumentResource)) |
| .setMimeType(import->SuggestedMIMEType()) |
| .build(); |
| subresources->addItem(std::move(resource_object)); |
| } |
| |
| std::unique_ptr<protocol::Page::FrameResourceTree> result = |
| protocol::Page::FrameResourceTree::create() |
| .setFrame(std::move(frame_object)) |
| .setResources(std::move(subresources)) |
| .build(); |
| |
| std::unique_ptr<protocol::Array<protocol::Page::FrameResourceTree>> |
| children_array; |
| for (Frame* child = frame->Tree().FirstChild(); child; |
| child = child->Tree().NextSibling()) { |
| if (!child->IsLocalFrame()) |
| continue; |
| if (!children_array) |
| children_array = |
| protocol::Array<protocol::Page::FrameResourceTree>::create(); |
| children_array->addItem(BuildObjectForResourceTree(ToLocalFrame(child))); |
| } |
| result->setChildFrames(std::move(children_array)); |
| return result; |
| } |
| |
| Response InspectorPageAgent::startScreencast(Maybe<String> format, |
| Maybe<int> quality, |
| Maybe<int> max_width, |
| Maybe<int> max_height, |
| Maybe<int> every_nth_frame) { |
| screencast_enabled_.Set(true); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::stopScreencast() { |
| screencast_enabled_.Set(false); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::getLayoutMetrics( |
| std::unique_ptr<protocol::Page::LayoutViewport>* out_layout_viewport, |
| std::unique_ptr<protocol::Page::VisualViewport>* out_visual_viewport, |
| std::unique_ptr<protocol::DOM::Rect>* out_content_size) { |
| LocalFrame* main_frame = inspected_frames_->Root(); |
| VisualViewport& visual_viewport = main_frame->GetPage()->GetVisualViewport(); |
| |
| main_frame->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); |
| |
| IntRect visible_contents = |
| main_frame->View()->LayoutViewport()->VisibleContentRect(); |
| *out_layout_viewport = protocol::Page::LayoutViewport::create() |
| .setPageX(visible_contents.X()) |
| .setPageY(visible_contents.Y()) |
| .setClientWidth(visible_contents.Width()) |
| .setClientHeight(visible_contents.Height()) |
| .build(); |
| |
| LocalFrameView* frame_view = main_frame->View(); |
| ScrollOffset page_offset = frame_view->GetScrollableArea()->GetScrollOffset(); |
| // page_zoom is either CSS-to-DP or CSS-to-DIP depending on |
| // enable-use-zoom-for-dsf flag. |
| float page_zoom = main_frame->PageZoomFactor(); |
| // page_zoom_factor is CSS to DIP (device independent pixels). |
| float page_zoom_factor = |
| page_zoom / |
| main_frame->GetPage()->GetChromeClient().WindowToViewportScalar(1); |
| FloatRect visible_rect = visual_viewport.VisibleRect(); |
| float scale = visual_viewport.Scale(); |
| |
| IntSize content_size = frame_view->GetScrollableArea()->ContentsSize(); |
| *out_content_size = protocol::DOM::Rect::create() |
| .setX(0) |
| .setY(0) |
| .setWidth(content_size.Width()) |
| .setHeight(content_size.Height()) |
| .build(); |
| |
| *out_visual_viewport = protocol::Page::VisualViewport::create() |
| .setOffsetX(AdjustForAbsoluteZoom::AdjustScroll( |
| visible_rect.X(), page_zoom)) |
| .setOffsetY(AdjustForAbsoluteZoom::AdjustScroll( |
| visible_rect.Y(), page_zoom)) |
| .setPageX(AdjustForAbsoluteZoom::AdjustScroll( |
| page_offset.Width(), page_zoom)) |
| .setPageY(AdjustForAbsoluteZoom::AdjustScroll( |
| page_offset.Height(), page_zoom)) |
| .setClientWidth(visible_rect.Width()) |
| .setClientHeight(visible_rect.Height()) |
| .setScale(scale) |
| .setZoom(page_zoom_factor) |
| .build(); |
| return Response::OK(); |
| } |
| |
| protocol::Response InspectorPageAgent::createIsolatedWorld( |
| const String& frame_id, |
| Maybe<String> world_name, |
| Maybe<bool> grant_universal_access, |
| int* execution_context_id) { |
| LocalFrame* frame = |
| IdentifiersFactory::FrameById(inspected_frames_, frame_id); |
| if (!frame) |
| return Response::Error("No frame for given id found"); |
| |
| scoped_refptr<DOMWrapperWorld> world = |
| frame->GetScriptController().CreateNewInspectorIsolatedWorld( |
| world_name.fromMaybe("")); |
| if (!world) |
| return Response::Error("Could not create isolated world"); |
| |
| scoped_refptr<SecurityOrigin> security_origin = |
| frame->GetSecurityContext()->GetSecurityOrigin()->IsolatedCopy(); |
| if (grant_universal_access.fromMaybe(false)) |
| security_origin->GrantUniversalAccess(); |
| DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(world->GetWorldId(), |
| security_origin); |
| |
| LocalWindowProxy* isolated_world_window_proxy = |
| frame->GetScriptController().WindowProxy(*world); |
| v8::HandleScope handle_scope(V8PerIsolateData::MainThreadIsolate()); |
| *execution_context_id = v8_inspector::V8ContextInfo::executionContextId( |
| isolated_world_window_proxy->ContextIfInitialized()); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::setFontFamilies( |
| std::unique_ptr<protocol::Page::FontFamilies> font_families) { |
| LocalFrame* frame = inspected_frames_->Root(); |
| auto* settings = frame->GetSettings(); |
| if (settings) { |
| auto& family_settings = settings->GetGenericFontFamilySettings(); |
| if (font_families->hasStandard()) { |
| standard_font_family_.Set(font_families->getStandard(String())); |
| family_settings.UpdateStandard(AtomicString(standard_font_family_.Get())); |
| } |
| if (font_families->hasFixed()) { |
| fixed_font_family_.Set(font_families->getFixed(String())); |
| family_settings.UpdateFixed(AtomicString(fixed_font_family_.Get())); |
| } |
| if (font_families->hasSerif()) { |
| serif_font_family_.Set(font_families->getSerif(String())); |
| family_settings.UpdateSerif(AtomicString(serif_font_family_.Get())); |
| } |
| if (font_families->hasSansSerif()) { |
| sans_serif_font_family_.Set(font_families->getSansSerif(String())); |
| family_settings.UpdateSansSerif( |
| AtomicString(sans_serif_font_family_.Get())); |
| } |
| if (font_families->hasCursive()) { |
| cursive_font_family_.Set(font_families->getCursive(String())); |
| family_settings.UpdateCursive(AtomicString(cursive_font_family_.Get())); |
| } |
| if (font_families->hasFantasy()) { |
| fantasy_font_family_.Set(font_families->getFantasy(String())); |
| family_settings.UpdateFantasy(AtomicString(fantasy_font_family_.Get())); |
| } |
| if (font_families->hasPictograph()) { |
| pictograph_font_family_.Set(font_families->getPictograph(String())); |
| family_settings.UpdatePictograph( |
| AtomicString(pictograph_font_family_.Get())); |
| } |
| settings->NotifyGenericFontFamilyChange(); |
| } |
| |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::setFontSizes( |
| std::unique_ptr<protocol::Page::FontSizes> font_sizes) { |
| LocalFrame* frame = inspected_frames_->Root(); |
| auto* settings = frame->GetSettings(); |
| if (settings) { |
| if (font_sizes->hasStandard()) { |
| standard_font_size_.Set(font_sizes->getStandard(0)); |
| settings->SetDefaultFontSize(standard_font_size_.Get()); |
| } |
| if (font_sizes->hasFixed()) { |
| fixed_font_size_.Set(font_sizes->getFixed(0)); |
| settings->SetDefaultFixedFontSize(fixed_font_size_.Get()); |
| } |
| } |
| |
| return Response::OK(); |
| } |
| |
| void InspectorPageAgent::ConsumeCompilationCache( |
| const ScriptSourceCode& source, |
| v8::ScriptCompiler::CachedData** cached_data) { |
| if (source.SourceLocationType() != ScriptSourceLocationType::kExternalFile) |
| return; |
| if (source.Url().IsEmpty()) |
| return; |
| auto it = compilation_cache_.find(source.Url().GetString()); |
| if (it == compilation_cache_.end()) |
| return; |
| const protocol::Binary& data = it->value; |
| *cached_data = new v8::ScriptCompiler::CachedData( |
| data.data(), data.size(), v8::ScriptCompiler::CachedData::BufferNotOwned); |
| } |
| |
| void InspectorPageAgent::ProduceCompilationCache(const ScriptSourceCode& source, |
| v8::Local<v8::Script> script) { |
| if (!produce_compilation_cache_.Get()) |
| return; |
| KURL url = source.Url(); |
| if (source.Streamer()) |
| return; |
| if (source.SourceLocationType() != ScriptSourceLocationType::kExternalFile) |
| return; |
| if (url.IsEmpty()) |
| return; |
| String url_string = url.GetString(); |
| auto it = compilation_cache_.find(url_string); |
| if (it != compilation_cache_.end()) |
| return; |
| static const int kMinimalCodeLength = 1024; |
| if (source.Source().length() < kMinimalCodeLength) |
| return; |
| std::unique_ptr<v8::ScriptCompiler::CachedData> cached_data( |
| v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript())); |
| if (cached_data) { |
| // CachedData produced by CreateCodeCache always owns its buffer. |
| CHECK_EQ(cached_data->buffer_policy, |
| v8::ScriptCompiler::CachedData::BufferOwned); |
| GetFrontend()->compilationCacheProduced( |
| url_string, protocol::Binary::fromCachedData(std::move(cached_data))); |
| } |
| } |
| |
| Response InspectorPageAgent::setProduceCompilationCache(bool enabled) { |
| produce_compilation_cache_.Set(enabled); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::addCompilationCache(const String& url, |
| const protocol::Binary& data) { |
| compilation_cache_.Set(url, data); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::clearCompilationCache() { |
| compilation_cache_.clear(); |
| return Response::OK(); |
| } |
| |
| Response InspectorPageAgent::waitForDebugger() { |
| client_->WaitForDebugger(); |
| return Response::OK(); |
| } |
| |
| protocol::Response InspectorPageAgent::generateTestReport(const String& message, |
| Maybe<String> group) { |
| Document* document = inspected_frames_->Root()->GetDocument(); |
| |
| // Construct the test report. |
| TestReportBody* body = MakeGarbageCollected<TestReportBody>(message); |
| Report* report = |
| MakeGarbageCollected<Report>("test", document->Url().GetString(), body); |
| |
| // Send the test report to any ReportingObservers. |
| ReportingContext::From(document)->QueueReport(report); |
| |
| return Response::OK(); |
| } |
| |
| void InspectorPageAgent::Trace(blink::Visitor* visitor) { |
| visitor->Trace(inspected_frames_); |
| visitor->Trace(inspector_resource_content_loader_); |
| InspectorBaseAgent::Trace(visitor); |
| } |
| |
| } // namespace blink |