blob: cd51eefb75b47416a05121c61d2a5c9b0b89267e [file] [log] [blame]
/*
* 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/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/script_regexp.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/settings.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 PageAgentState {
static const char kPageAgentEnabled[] = "pageAgentEnabled";
static const char kPageAgentScriptsToEvaluateOnLoad[] =
"pageAgentScriptsToEvaluateOnLoad";
static const char kScreencastEnabled[] = "screencastEnabled";
static const char kLifecycleEventsEnabled[] = "lifecycleEventsEnabled";
static const char kBypassCSPEnabled[] = "bypassCSPEnabled";
static const char kStandardFontFamily[] = "standardFontFamily";
static const char kFixedFontFamily[] = "fixedFontFamily";
static const char kSerifFontFamily[] = "serifFontFamily";
static const char kSansSerifFontFamily[] = "sansSerifFontFamily";
static const char kCursiveFontFamily[] = "cursiveFontFamily";
static const char kFantasyFontFamily[] = "fantasyFontFamily";
static const char kPictographFontFamily[] = "pictographFontFamily";
static const char kStandardFontSize[] = "standardFontSize";
static const char kFixedFontSize[] = "fixedFontSize";
} // namespace PageAgentState
namespace {
String ScheduledNavigationReasonToProtocol(ScheduledNavigation::Reason reason) {
using ReasonEnum =
protocol::Page::FrameScheduledNavigationNotification::ReasonEnum;
switch (reason) {
case ScheduledNavigation::Reason::kFormSubmissionGet:
return ReasonEnum::FormSubmissionGet;
case ScheduledNavigation::Reason::kFormSubmissionPost:
return ReasonEnum::FormSubmissionPost;
case ScheduledNavigation::Reason::kHttpHeaderRefresh:
return ReasonEnum::HttpHeaderRefresh;
case ScheduledNavigation::Reason::kFrameNavigation:
return ReasonEnum::ScriptInitiated;
case ScheduledNavigation::Reason::kMetaTagRefresh:
return ReasonEnum::MetaTagRefresh;
case ScheduledNavigation::Reason::kPageBlock:
return ReasonEnum::PageBlockInterstitial;
case ScheduledNavigation::Reason::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) {
Resource::Type type = cached_resource->GetType();
return type == Resource::kCSSStyleSheet || type == Resource::kXSLStyleSheet ||
type == Resource::kScript || type == Resource::kRaw ||
type == Resource::kImportResource || type == Resource::kMainResource;
}
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,
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(),
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(), flat_buffer.size());
}
MaybeEncodeTextContent(text_content, flat_buffer.Data(), 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(), 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 Resource::kCSSStyleSheet:
MaybeEncodeTextContent(
ToCSSStyleSheetResource(cached_resource)
->SheetText(nullptr, CSSStyleSheetResource::MIMETypeCheck::kLax),
cached_resource->ResourceBuffer(), result, base64_encoded);
return true;
case Resource::kScript:
MaybeEncodeTextContent(
cached_resource->ResourceBuffer()
? ToScriptResource(cached_resource)->DecodedText()
: ToScriptResource(cached_resource)->SourceText(),
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() != Resource::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 new InspectorPageAgent(inspected_frames, client,
resource_content_loader, v8_session);
}
String InspectorPageAgent::ResourceTypeJson(
InspectorPageAgent::ResourceType resource_type) {
switch (resource_type) {
case kDocumentResource:
return protocol::Page::ResourceTypeEnum::Document;
case kFontResource:
return protocol::Page::ResourceTypeEnum::Font;
case kImageResource:
return protocol::Page::ResourceTypeEnum::Image;
case kMediaResource:
return protocol::Page::ResourceTypeEnum::Media;
case kScriptResource:
return protocol::Page::ResourceTypeEnum::Script;
case kStylesheetResource:
return protocol::Page::ResourceTypeEnum::Stylesheet;
case kTextTrackResource:
return protocol::Page::ResourceTypeEnum::TextTrack;
case kXHRResource:
return protocol::Page::ResourceTypeEnum::XHR;
case kFetchResource:
return protocol::Page::ResourceTypeEnum::Fetch;
case kEventSourceResource:
return protocol::Page::ResourceTypeEnum::EventSource;
case kWebSocketResource:
return protocol::Page::ResourceTypeEnum::WebSocket;
case kManifestResource:
return protocol::Page::ResourceTypeEnum::Manifest;
case kSignedExchangeResource:
return protocol::Page::ResourceTypeEnum::SignedExchange;
case kOtherResource:
return protocol::Page::ResourceTypeEnum::Other;
}
return protocol::Page::ResourceTypeEnum::Other;
}
InspectorPageAgent::ResourceType InspectorPageAgent::ToResourceType(
const Resource::Type resource_type) {
switch (resource_type) {
case Resource::kImage:
return InspectorPageAgent::kImageResource;
case Resource::kFont:
return InspectorPageAgent::kFontResource;
case Resource::kAudio:
case Resource::kVideo:
return InspectorPageAgent::kMediaResource;
case Resource::kManifest:
return InspectorPageAgent::kManifestResource;
case Resource::kTextTrack:
return InspectorPageAgent::kTextTrackResource;
case Resource::kCSSStyleSheet:
// Fall through.
case Resource::kXSLStyleSheet:
return InspectorPageAgent::kStylesheetResource;
case Resource::kScript:
return InspectorPageAgent::kScriptResource;
case Resource::kImportResource:
// Fall through.
case Resource::kMainResource:
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),
last_script_identifier_(0),
enabled_(false),
reloading_(false),
inspector_resource_content_loader_(resource_content_loader),
resource_content_loader_client_id_(
resource_content_loader->CreateClientId()) {}
void InspectorPageAgent::Restore() {
if (state_->booleanProperty(PageAgentState::kPageAgentEnabled, false))
enable();
if (state_->booleanProperty(PageAgentState::kBypassCSPEnabled, false))
setBypassCSP(true);
// Re-apply generic fonts overrides.
String font;
bool notifyGenericFontFamilyChange = false;
LocalFrame* frame = inspected_frames_->Root();
auto* settings = frame->GetSettings();
if (settings) {
auto& family_settings = settings->GetGenericFontFamilySettings();
if (state_->getString(PageAgentState::kStandardFontFamily, &font)) {
family_settings.UpdateStandard(AtomicString(font));
notifyGenericFontFamilyChange = true;
}
if (state_->getString(PageAgentState::kFixedFontFamily, &font)) {
family_settings.UpdateFixed(AtomicString(font));
notifyGenericFontFamilyChange = true;
}
if (state_->getString(PageAgentState::kSerifFontFamily, &font)) {
family_settings.UpdateSerif(AtomicString(font));
notifyGenericFontFamilyChange = true;
}
if (state_->getString(PageAgentState::kSansSerifFontFamily, &font)) {
family_settings.UpdateSansSerif(AtomicString(font));
notifyGenericFontFamilyChange = true;
}
if (state_->getString(PageAgentState::kCursiveFontFamily, &font)) {
family_settings.UpdateCursive(AtomicString(font));
notifyGenericFontFamilyChange = true;
}
if (state_->getString(PageAgentState::kFantasyFontFamily, &font)) {
family_settings.UpdateFantasy(AtomicString(font));
notifyGenericFontFamilyChange = true;
}
if (state_->getString(PageAgentState::kPictographFontFamily, &font)) {
family_settings.UpdatePictograph(AtomicString(font));
notifyGenericFontFamilyChange = true;
}
if (notifyGenericFontFamilyChange) {
settings->NotifyGenericFontFamilyChange();
}
}
// Re-apply default font size overrides.
int font_size;
if (settings) {
if (state_->getInteger(PageAgentState::kStandardFontSize, &font_size)) {
settings->SetDefaultFontSize(font_size);
}
if (state_->getInteger(PageAgentState::kFixedFontSize, &font_size)) {
settings->SetDefaultFixedFontSize(font_size);
}
}
}
Response InspectorPageAgent::enable() {
enabled_ = true;
state_->setBoolean(PageAgentState::kPageAgentEnabled, true);
instrumenting_agents_->addInspectorPageAgent(this);
return Response::OK();
}
Response InspectorPageAgent::disable() {
enabled_ = false;
state_->setBoolean(PageAgentState::kPageAgentEnabled, false);
state_->remove(PageAgentState::kPageAgentScriptsToEvaluateOnLoad);
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();
FinishReload();
return Response::OK();
}
Response InspectorPageAgent::addScriptToEvaluateOnLoad(const String& source,
String* identifier) {
protocol::DictionaryValue* scripts =
state_->getObject(PageAgentState::kPageAgentScriptsToEvaluateOnLoad);
if (!scripts) {
std::unique_ptr<protocol::DictionaryValue> new_scripts =
protocol::DictionaryValue::create();
scripts = new_scripts.get();
state_->setObject(PageAgentState::kPageAgentScriptsToEvaluateOnLoad,
std::move(new_scripts));
}
// Assure we don't override existing ids -- m_lastScriptIdentifier could get
// out of sync WRT actual scripts once we restored the scripts from the cookie
// during navigation.
do {
*identifier = String::Number(++last_script_identifier_);
} while (scripts->get(*identifier));
scripts->setString(*identifier, source);
return Response::OK();
}
Response InspectorPageAgent::removeScriptToEvaluateOnLoad(
const String& identifier) {
protocol::DictionaryValue* scripts =
state_->getObject(PageAgentState::kPageAgentScriptsToEvaluateOnLoad);
if (!scripts || !scripts->get(identifier))
return Response::Error("Script not found");
scripts->remove(identifier);
return Response::OK();
}
Response InspectorPageAgent::addScriptToEvaluateOnNewDocument(
const String& source,
String* identifier) {
return addScriptToEvaluateOnLoad(source, identifier);
}
Response InspectorPageAgent::removeScriptToEvaluateOnNewDocument(
const String& identifier) {
return removeScriptToEvaluateOnLoad(identifier);
}
Response InspectorPageAgent::setLifecycleEventsEnabled(bool enabled) {
state_->setBoolean(PageAgentState::kLifecycleEventsEnabled, 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);
reloading_ = true;
inspected_frames_->Root()->Reload(
optional_bypass_cache.fromMaybe(false)
? WebFrameLoadType::kReloadBypassingCache
: WebFrameLoadType::kReload,
ClientRedirectPolicy::kNotClientRedirect);
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() == Resource::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 (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 (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::FinishReload() {
if (!reloading_)
return;
reloading_ = false;
v8_session_->setSkipAllPauses(false);
}
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_) {
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_) {
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);
state_->setBoolean(PageAgentState::kBypassCSPEnabled, 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;
protocol::DictionaryValue* scripts =
state_->getObject(PageAgentState::kPageAgentScriptsToEvaluateOnLoad);
if (scripts) {
for (size_t i = 0; i < scripts->size(); ++i) {
auto script = scripts->at(i);
String script_text;
if (script.second->asString(&script_text))
frame->GetScriptController().ExecuteScriptInMainWorld(script_text);
}
}
if (!script_to_evaluate_on_load_once_.IsEmpty()) {
frame->GetScriptController().ExecuteScriptInMainWorld(
script_to_evaluate_on_load_once_);
}
}
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()) {
FinishReload();
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_ &&
state_->booleanProperty(PageAgentState::kScreencastEnabled, false);
}
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,
ScheduledNavigation* scheduled_navigation) {
GetFrontend()->frameScheduledNavigation(
IdentifiersFactory::FrameId(frame), scheduled_navigation->Delay(),
ScheduledNavigationReasonToProtocol(scheduled_navigation->GetReason()),
scheduled_navigation->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 ||
!state_->booleanProperty(PageAgentState::kLifecycleEventsEnabled, false))
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_ && 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();
KURL url = loader->GetRequest().Url();
std::unique_ptr<protocol::Page::Frame> frame_object =
protocol::Page::Frame::create()
.setId(IdentifiersFactory::FrameId(frame))
.setLoaderId(IdentifiersFactory::LoaderId(loader))
.setUrl(UrlWithoutFragment(url).GetString())
.setMimeType(frame->Loader().GetDocumentLoader()->MimeType())
.setSecurityOrigin(SecurityOrigin::Create(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(HTMLNames::idAttr);
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) {
state_->setBoolean(PageAgentState::kScreencastEnabled, true);
return Response::OK();
}
Response InspectorPageAgent::stopScreencast() {
state_->setBoolean(PageAgentState::kScreencastEnabled, 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();
float page_zoom = main_frame->PageZoomFactor();
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)
.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");
if (grant_universal_access.fromMaybe(false)) {
scoped_refptr<SecurityOrigin> security_origin =
frame->GetSecurityContext()->GetSecurityOrigin()->IsolatedCopy();
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()) {
state_->setString(PageAgentState::kStandardFontFamily,
font_families->getStandard(String()));
family_settings.UpdateStandard(
AtomicString(font_families->getStandard(String())));
}
if (font_families->hasFixed()) {
state_->setString(PageAgentState::kFixedFontFamily,
font_families->getFixed(String()));
family_settings.UpdateFixed(
AtomicString(font_families->getFixed(String())));
}
if (font_families->hasSerif()) {
state_->setString(PageAgentState::kSerifFontFamily,
font_families->getSerif(String()));
family_settings.UpdateSerif(
AtomicString(font_families->getSerif(String())));
}
if (font_families->hasSansSerif()) {
state_->setString(PageAgentState::kSansSerifFontFamily,
font_families->getSansSerif(String()));
family_settings.UpdateSansSerif(
AtomicString(font_families->getSansSerif(String())));
}
if (font_families->hasCursive()) {
state_->setString(PageAgentState::kCursiveFontFamily,
font_families->getCursive(String()));
family_settings.UpdateCursive(
AtomicString(font_families->getCursive(String())));
}
if (font_families->hasFantasy()) {
state_->setString(PageAgentState::kFantasyFontFamily,
font_families->getFantasy(String()));
family_settings.UpdateFantasy(
AtomicString(font_families->getFantasy(String())));
}
if (font_families->hasPictograph()) {
state_->setString(PageAgentState::kPictographFontFamily,
font_families->getPictograph(String()));
family_settings.UpdatePictograph(
AtomicString(font_families->getPictograph(String())));
}
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()) {
state_->setInteger(PageAgentState::kStandardFontSize,
font_sizes->getStandard(0));
settings->SetDefaultFontSize(font_sizes->getStandard(0));
}
if (font_sizes->hasFixed()) {
state_->setInteger(PageAgentState::kFixedFontSize,
font_sizes->getFixed(0));
settings->SetDefaultFixedFontSize(font_sizes->getFixed(0));
}
}
return Response::OK();
}
void InspectorPageAgent::Trace(blink::Visitor* visitor) {
visitor->Trace(inspected_frames_);
visitor->Trace(inspector_resource_content_loader_);
InspectorBaseAgent::Trace(visitor);
}
} // namespace blink