electron/shell/browser/ui/inspectable_web_contents.cc
electron-roller[bot] c3b4cd987c
chore: bump chromium to 127.0.6521.0 (main) (#42118)
* chore: bump chromium in DEPS to 126.0.6470.0

* 5492605: Migrate TODOs referencing old crbug IDs to the new issue tracker IDs | https://chromium-review.googlesource.com/c/chromium/src/+/5492605

* 5513277: Move subresource-filter-ruleset to GCS | https://chromium-review.googlesource.com/c/chromium/src/+/5513277

* 5512656: Remove CustomizeChromeSupportsChromeRefresh2023 | https://chromium-review.googlesource.com/c/chromium/src/+/5512656

* 5516009: Accept mouse events in inactive window for Top Chrome WebUIs | https://chromium-review.googlesource.com/c/chromium/src/+/5516009

* 5376861: Change references to RWHVB in RWHIER and RenderWidgetTargeter to RWHVI. | https://chromium-review.googlesource.com/c/chromium/src/+/5376861

* 5490530: Use partition_alloc PA_BUILDFLAG(...) outside PA. #cleanup | https://chromium-review.googlesource.com/c/chromium/src/+/5490530

* 5296870: network: Allow trusted loaders to learn the sent request cookies. | https://chromium-review.googlesource.com/c/chromium/src/+/5296870

* 5453438: Delegate delegated ink trails to RWHI from RWHIER. | https://chromium-review.googlesource.com/c/chromium/src/+/5453438

* chore: update patches

* chore: bump chromium in DEPS to 126.0.6472.0

* chore: bump chromium in DEPS to 126.0.6474.0

* chore: update patches

* chore: bump chromium in DEPS to 126.0.6476.0

* chore: bump chromium in DEPS to 126.0.6478.0

* chore: bump chromium in DEPS to 126.0.6478.3

* chore: bump chromium in DEPS to 126.0.6478.8

* update patches

* only disable enterprise_cloud_content_analysis

* 5403888: [api] support v8::Data in v8::TracedReference and v8::EmbedderGraph

https://chromium-review.googlesource.com/c/v8/v8/+/5403888

* chore: bump chromium in DEPS to 127.0.6484.0

* chore: bump chromium in DEPS to 127.0.6485.0

* 5539004: Use NOTREACHED_IN_MIGRATION() in remaining chrome/ | https://chromium-review.googlesource.com/c/chromium/src/+/5539004

* src: cast to v8::Value before using v8::EmbedderGraph::V8Node | https://github.com/nodejs/node/pull/52638/files

* chore: update patches

* chore: update v8 patches

* chore: bump chromium in DEPS to 127.0.6486.0

* chore: bump chromium in DEPS to 127.0.6488.0

* chore: bump chromium in DEPS to 127.0.6490.0

* chore: bump chromium in DEPS to 127.0.6492.0

* chore: update patches

For some reason, `feat_expose_raw_response_headers_from_urlloader.patch` got messed up in an earlier commit.

* chore: update patches

printing.patch was updated due to https://chromium-review.googlesource.com/c/chromium/src/+/5535938

* 5527572: Move Connectors prefs files to components/enterprise/connectors/

https://chromium-review.googlesource.com/c/chromium/src/+/5527572

* chore: bump chromium in DEPS to 127.0.6494.0

* chore: bump chromium in DEPS to 127.0.6495.0

* chore: bump chromium in DEPS to 127.0.6496.0

* 5465511: [api] Mark v8::ObjectTemplate::SetAccessor(..) for deprecation
https://chromium-review.googlesource.com/c/v8/v8/+/5465511

* chore: revert v8 deprecation

See patch message for more details.

https://chromium-review.googlesource.com/c/v8/v8/+/5526611

* chore: update patches

* 5538771: Remove srcdoc else-if block in CalculateOrigin()
https://chromium-review.googlesource.com/c/chromium/src/+/5538771

* 5522321: [devtools] Support saving base64 encoded files via host bindings
https://chromium-review.googlesource.com/c/chromium/src/+/5522321

* 5376861: Change references to RWHVB in RWHIER and RenderWidgetTargeter to RWHVI.
https://chromium-review.googlesource.com/c/chromium/src/+/5376861

* 5530163: [media] Use VideoFrame::Plane typed enum instead of nameless enum
https://chromium-review.googlesource.com/c/chromium/src/+/5530163

* 5463431: iwa: Only create IsolatedWebAppURLLoaderFactory for subresources in IWAs
https://chromium-review.googlesource.com/c/chromium/src/+/5463431

* fixup! 5465511: [api] Mark v8::ObjectTemplate::SetAccessor(..) for deprecation https://chromium-review.googlesource.com/c/v8/v8/+/5465511

* 5512176: Remove OnEnvironmentEstimationComplete()
https://chromium-review.googlesource.com/c/chromium/src/+/5512176

* 5528282: Move Web Speech API .mojom files to //media/mojo/mojom
https://chromium-review.googlesource.com/c/chromium/src/+/5528282

* 5513740: Reland "[Extensions] Restructure extensions::ProcessMap"
https://chromium-review.googlesource.com/c/chromium/src/+/5513740

* 5483406: [PEPC] Make PEPC permission subscription take into account device status
https://chromium-review.googlesource.com/c/chromium/src/+/5483406

* 5526034: [DoH] Remove kDnsOverHttps feature flag
https://chromium-review.googlesource.com/c/chromium/src/+/5526034

The title is a bit misleading. They removed handling for the feature flag and generally intend to remove it but haven't yet.

I only changed our code to address the flag that was removed. A quick search on GitHub for `DnsOverHttpsFallback` yielded a few results, but they were all C++ chromium code or patches, 0 app code or discussion results. Since I couldn't find any evidence of this flag being used in developer applications, I've chosen to exclude this change from the breaking changes docs.

* chore: revert v8 removal

https://chromium-review.googlesource.com/c/v8/v8/+/5497515

See patch message for more details.

* chore: cherry-pick Node.js patch for V8 API removal fix

Node.js PR: https://github.com/nodejs/node/pull/52996
V8 API Removal CL: https://chromium-review.googlesource.com/c/v8/v8/+/5539888

See the patch description for more details.

* 5492183: Extensions: CodeHealth: Give enums some class
https://chromium-review.googlesource.com/c/chromium/src/+/5492183

* fixup! 5528282: Move Web Speech API .mojom files to //media/mojo/mojom https://chromium-review.googlesource.com/c/chromium/src/+/5528282

* 5514687: Reland "Add a secret handshake to the base::Feature constructor"
https://chromium-review.googlesource.com/c/chromium/src/+/5514687

* fixup! 5530163: [media] Use VideoFrame::Plane typed enum instead of nameless enum https://chromium-review.googlesource.com/c/chromium/src/+/5530163

* 5466238: PDF Viewer: add metrics to record if PDF is opened with a11y
https://chromium-review.googlesource.com/c/chromium/src/+/5466238

* 5502081: Migrate OnDisplayRemoved to OnDisplaysRemoved
https://chromium-review.googlesource.com/c/chromium/src/+/5502081

* 5539888: [api] Remove several APIs deprecated in version 12.6
https://chromium-review.googlesource.com/c/v8/v8/+/5539888

This commit essentially only removes the `only_terminate_in_safe_scope` isolate creation parameter. This undoes some work that was originally done in #35766.

* 5498236: Make browser_tests force full async initialization for OSCrypt Async
https://chromium-review.googlesource.com/c/chromium/src/+/5498236

* fixup! 5528282: Move Web Speech API .mojom files to //media/mojo/mojom https://chromium-review.googlesource.com/c/chromium/src/+/5528282

* 5545807: Migrate most remaining NOTREACHED()
https://chromium-review.googlesource.com/c/chromium/src/+/5545807

I took a systematic approach to modifying all of our uses of `NOTREACHED` that were causing errors:
* If there was a `return` or `break` (etc.) immediately after `NOTREACHED`, I removed the control flow instruction and left the `NOTREACHED` unmodified
* All other instances were migrated to `NOTREACHED_IN_MIGRATION`

We should revisit pretty much all usage of `NOTREACHED` as an upgrade follow-up item.

* fixup! 5526034: [DoH] Remove kDnsOverHttps feature flag https://chromium-review.googlesource.com/c/chromium/src/+/5526034

Turns out the feature flags were removed in the `.cc` file, but not the
`.h` feature list file. This means that the feature flags are pretty
much officially gone. (The leftover symbols in the header are likely an
oversight from what I can gather.)

We may potentially decide to put this in the breaking changes doc if we
decide this feature flag is important enough to highlight.

* chore: bump chromium in DEPS to 127.0.6498.3

* chore: bump chromium in DEPS to 127.0.6500.0

* chore: bump chromium in DEPS to 127.0.6502.0

* chore: bump chromium in DEPS to 127.0.6504.0

* chore: bump chromium in DEPS to 127.0.6505.0

* chore: bump chromium in DEPS to 127.0.6508.0

* build: use Sha256Sum in script/sysroots.json

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5506275

* chore: update chore_add_electron_deps_to_gitignores.patch

Xref: no manual changes; patch applied with fuzz 2

* chore: update feat_allow_code_cache_in_custom_schemes.patch

Xref: no manual changes; patch applied with fuzz 1

* chore: e patches all

* fixup! build: use Sha256Sum in script/sysroots.json

`sync` succeeds now

* chore: replace absl::optional with std::optional

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5253843

* chore: update CalculatePreferredSize() to new upstream semantics

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5459174
Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5541220
Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5514708
Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5504212
Xref: https://chromium-review.googlesource.com/516542

* chore: replace absl::optional with std::optional

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5296147

* chore: add kPip to enumeration as a no-op

https://chromium-review.googlesource.com/c/chromium/src/+/5546257

* [Autofill] Remove RenderFrame::ElementBoundsInWindow()

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5553982

* chore: fix feat_add_streaming-protocol_registry_to_multibuffer_data_source.patch

need new header to pick up definition of BLINK_PLATFORM_EXPORT macro

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5463143

* chore: bump chromium in DEPS to 127.0.6510.0

* chore: update patches

* chore: fix include path for native_web_keyboard_event.h

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5541976

* chore: add currently-unused should_include_device_status arg to GetPermissionStatusForCurrentDocument()

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5545382

* chore: bump chromium in DEPS to 127.0.6512.0

* chore: update mas_avoid_private_macos_api_usage.patch.patch

No manual changes; patch applied with fuzz 1

* chore: update feat_add_streaming-protocol_registry_to_multibuffer_data_source.patch

No manual changes; patch applied with fuzz 1

* chore: update webview_fullscreen.patch

No manual changes; patch applied with fuzz 1

* chore=: remove cherry-pick-22db6918bac9.patch

already present upstream

* chore: remove nonexistent patchfiles from .patches

* chore: remove cherry-pick-3e037e195e50.patch

no longer needed; merged upstream

* Update namespace for files moved to //components/input

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5563251

* Require client for InitParams to always specify an ownership mode.

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5532482

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5578714

* chore: e patches all

* fixup! Update namespace for files moved to //components/input

* chore: remove profile_keyed_service_factory, profile_selections from chromium_src

already being linked in via chrome browser for printing

* chore: bump chromium in DEPS to 127.0.6515.0

* chore: bump chromium in DEPS to 127.0.6516.0

* chore: update render_widget_host_view_base.patch

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5547803

patch applied manually due to simple upstream shear

* chore: update feat_allow_code_cache_in_custom_schemes.patch

No manual changes; patch applied with fuzz 1

* chore: e patches all

* Pull RWHIER and RWT to //content/common/input.

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5397681

* chore: bump chromium in DEPS to 127.0.6517.0

* chore: update patches

* fixup: Update namespace for files moved to //components/input

* Remove 0-arg (default) constructor for views::Widget::InitParams.

https://chromium-review.googlesource.com/c/chromium/src/+/5578714

* fixup: only disable enterprise_cloud_content_analysis

The original commit a5480accc2, was due to this CL 5527572: Move Connectors prefs files to components/enterprise/connectors/ | https://chromium-review.googlesource.com/c/chromium/src/+/5527572

* chore: bump chromium in DEPS to 127.0.6519.0

* chore: update patches

* src: do not use deprecated V8 API

https://github.com/nodejs/node/pull/53084

* src: remove dependency on wrapper-descriptor-based cpp heap

https://github.com/nodejs/node/pull/53086

* 5344413: [DevTools] Add `getHostConfig` UI binding for sending status of `base::Features` to DevTools

https://chromium-review.googlesource.com/c/chromium/src/+/5344413

* 5585788: Extensions: ManifestHandler: Separate Registry like ExtensionRegistry

https://chromium-review.googlesource.com/c/chromium/src/+/5585788

* chore: update filenames.libcxx.gni

* 5506857: Reland "Migrate clang-format to gcs first class deps"

https://chromium-review.googlesource.com/c/chromium/src/+/5506857

* fixup: 5539888: [api] Remove several APIs deprecated in version 12.6

* fixup:  5506857: Reland Migrate clang-format to gcs first class deps

* chore: bump chromium in DEPS to 127.0.6521.0

* chore: update patches

* spec: update navigator.keyboard should lock the keyboard

* Block or allow all MIDI using the existing SysEx permission

Refs https://chromium-review.googlesource.com/c/chromium/src/+/5154368
Refs https://chromium-review.googlesource.com/c/chromium/src/+/5499157

* spec: update test/parallel/test-v8-stats

* views: remove CalculatePreferredSize()

Refs https://chromium-review.googlesource.com/c/chromium/src/+/5504212

* chore: update patches after rebase

* 5560288: Re-enable ChromeOS XNNPack on Intel only

https://chromium-review.googlesource.com/c/chromium/src/+/5560288

* chore: add nan patches for v8 changes

Refs
5539888: [api] Remove several APIs deprecated in version 12.6 | https://chromium-review.googlesource.com/c/v8/v8/+/5539888
and
5539852: [heap][api] Remove deprecated v8::Isolate::IdleNotificationDeadline | https://chromium-review.googlesource.com/c/v8/v8/+/5539852

* 5573603: Modularize //chrome/browser/themes

https://chromium-review.googlesource.com/c/chromium/src/+/5573603

* 5539888: [api] Remove several APIs deprecated in version 12.6

https://chromium-review.googlesource.com/c/v8/v8/+/5539888

* chore: update patches

* test: fixup navigator.keyboard.lock on Windows

* chore: remove unneeded profile target

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
Co-authored-by: VerteDinde <vertedinde@electronjs.org>
Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
Co-authored-by: clavin <clavin@electronjs.org>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2024-06-07 17:18:35 -04:00

1075 lines
38 KiB
C++

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright (c) 2013 Adam Roben <adam@roben.org>. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.
#include "shell/browser/ui/inspectable_web_contents.h"
#include <memory>
#include <string_view>
#include <utility>
#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/json/string_escape.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram.h"
#include "base/ranges/algorithm.h"
#include "base/stl_util.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/uuid.h"
#include "base/values.h"
#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/file_url_loader.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/user_agent.h"
#include "ipc/ipc_channel.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/native_window_views.h"
#include "shell/browser/net/asar/asar_url_loader_factory.h"
#include "shell/browser/protocol_registry.h"
#include "shell/browser/ui/inspectable_web_contents_delegate.h"
#include "shell/browser/ui/inspectable_web_contents_view.h"
#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
#include "shell/common/application_info.h"
#include "shell/common/platform_util.h"
#include "third_party/blink/public/common/logging/logging_utils.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "v8/include/v8.h"
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/permissions/permissions_data.h"
#include "shell/browser/electron_browser_context.h"
#endif
namespace electron {
namespace {
const double kPresetZoomFactors[] = {0.25, 0.333, 0.5, 0.666, 0.75, 0.9,
1.0, 1.1, 1.25, 1.5, 1.75, 2.0,
2.5, 3.0, 4.0, 5.0};
const char kChromeUIDevToolsURL[] =
"devtools://devtools/bundled/devtools_app.html?"
"remoteBase=%s&"
"can_dock=%s&"
"toolbarColor=rgba(223,223,223,1)&"
"textColor=rgba(0,0,0,1)&"
"experiments=true";
const char kChromeUIDevToolsRemoteFrontendBase[] =
"https://chrome-devtools-frontend.appspot.com/";
const char kChromeUIDevToolsRemoteFrontendPath[] = "serve_file";
const char kDevToolsBoundsPref[] = "electron.devtools.bounds";
const char kDevToolsZoomPref[] = "electron.devtools.zoom";
const char kDevToolsPreferences[] = "electron.devtools.preferences";
const char kFrontendHostId[] = "id";
const char kFrontendHostMethod[] = "method";
const char kFrontendHostParams[] = "params";
const char kTitleFormat[] = "Developer Tools - %s";
const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
base::Value::Dict RectToDictionary(const gfx::Rect& bounds) {
return base::Value::Dict{}
.Set("x", bounds.x())
.Set("y", bounds.y())
.Set("width", bounds.width())
.Set("height", bounds.height());
}
gfx::Rect DictionaryToRect(const base::Value::Dict& dict) {
return gfx::Rect{dict.FindInt("x").value_or(0), dict.FindInt("y").value_or(0),
dict.FindInt("width").value_or(800),
dict.FindInt("height").value_or(600)};
}
bool IsPointInScreen(const gfx::Point& point) {
return base::ranges::any_of(display::Screen::GetScreen()->GetAllDisplays(),
[&point](auto const& display) {
return display.bounds().Contains(point);
});
}
void SetZoomLevelForWebContents(content::WebContents* web_contents,
double level) {
content::HostZoomMap::SetZoomLevel(web_contents, level);
}
double GetNextZoomLevel(double level, bool out) {
double factor = blink::PageZoomLevelToZoomFactor(level);
size_t size = std::size(kPresetZoomFactors);
for (size_t i = 0; i < size; ++i) {
if (!blink::PageZoomValuesEqual(kPresetZoomFactors[i], factor))
continue;
if (out && i > 0)
return blink::PageZoomFactorToZoomLevel(kPresetZoomFactors[i - 1]);
if (!out && i != size - 1)
return blink::PageZoomFactorToZoomLevel(kPresetZoomFactors[i + 1]);
}
return level;
}
GURL GetRemoteBaseURL() {
return GURL(base::StringPrintf("%s%s/%s/",
kChromeUIDevToolsRemoteFrontendBase,
kChromeUIDevToolsRemoteFrontendPath,
content::GetChromiumGitRevision().c_str()));
}
GURL GetDevToolsURL(bool can_dock) {
auto url_string = base::StringPrintf(kChromeUIDevToolsURL,
GetRemoteBaseURL().spec().c_str(),
can_dock ? "true" : "");
return GURL(url_string);
}
void OnOpenItemComplete(const base::FilePath& path, const std::string& result) {
platform_util::ShowItemInFolder(path);
}
constexpr base::TimeDelta kInitialBackoffDelay = base::Milliseconds(250);
constexpr base::TimeDelta kMaxBackoffDelay = base::Seconds(10);
} // namespace
class InspectableWebContents::NetworkResourceLoader
: public network::SimpleURLLoaderStreamConsumer {
public:
class URLLoaderFactoryHolder {
public:
network::mojom::URLLoaderFactory* get() {
return ptr_.get() ? ptr_.get() : refptr_.get();
}
void operator=(std::unique_ptr<network::mojom::URLLoaderFactory>&& ptr) {
ptr_ = std::move(ptr);
}
void operator=(scoped_refptr<network::SharedURLLoaderFactory>&& refptr) {
refptr_ = std::move(refptr);
}
private:
std::unique_ptr<network::mojom::URLLoaderFactory> ptr_;
scoped_refptr<network::SharedURLLoaderFactory> refptr_;
};
static void Create(int stream_id,
InspectableWebContents* bindings,
const network::ResourceRequest& resource_request,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
URLLoaderFactoryHolder url_loader_factory,
DispatchCallback callback,
base::TimeDelta retry_delay = base::TimeDelta()) {
bindings->loaders_.insert(
std::make_unique<InspectableWebContents::NetworkResourceLoader>(
stream_id, bindings, resource_request, traffic_annotation,
std::move(url_loader_factory), std::move(callback), retry_delay));
}
NetworkResourceLoader(
int stream_id,
InspectableWebContents* bindings,
const network::ResourceRequest& resource_request,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
URLLoaderFactoryHolder url_loader_factory,
DispatchCallback callback,
base::TimeDelta delay)
: stream_id_(stream_id),
bindings_(bindings),
resource_request_(resource_request),
traffic_annotation_(traffic_annotation),
loader_(network::SimpleURLLoader::Create(
std::make_unique<network::ResourceRequest>(resource_request),
traffic_annotation)),
url_loader_factory_(std::move(url_loader_factory)),
callback_(std::move(callback)),
retry_delay_(delay) {
loader_->SetOnResponseStartedCallback(base::BindOnce(
&NetworkResourceLoader::OnResponseStarted, base::Unretained(this)));
timer_.Start(FROM_HERE, delay,
base::BindRepeating(&NetworkResourceLoader::DownloadAsStream,
base::Unretained(this)));
}
NetworkResourceLoader(const NetworkResourceLoader&) = delete;
NetworkResourceLoader& operator=(const NetworkResourceLoader&) = delete;
private:
void DownloadAsStream() {
loader_->DownloadAsStream(url_loader_factory_.get(), this);
}
base::TimeDelta GetNextExponentialBackoffDelay(const base::TimeDelta& delta) {
if (delta.is_zero()) {
return kInitialBackoffDelay;
} else {
return delta * 1.3;
}
}
void OnResponseStarted(const GURL& final_url,
const network::mojom::URLResponseHead& response_head) {
response_headers_ = response_head.headers;
}
void OnDataReceived(base::StringPiece chunk,
base::OnceClosure resume) override {
bool encoded = !base::IsStringUTF8(chunk);
bindings_->CallClientFunction(
"DevToolsAPI", "streamWrite", base::Value{stream_id_},
base::Value{encoded ? base::Base64Encode(chunk) : chunk},
base::Value{encoded});
std::move(resume).Run();
}
void OnComplete(bool success) override {
if (!success && loader_->NetError() == net::ERR_INSUFFICIENT_RESOURCES &&
retry_delay_ < kMaxBackoffDelay) {
const base::TimeDelta delay =
GetNextExponentialBackoffDelay(retry_delay_);
LOG(WARNING) << "InspectableWebContents::NetworkResourceLoader id = "
<< stream_id_
<< " failed with insufficient resources, retrying in "
<< delay << "." << std::endl;
NetworkResourceLoader::Create(
stream_id_, bindings_, resource_request_, traffic_annotation_,
std::move(url_loader_factory_), std::move(callback_), delay);
} else {
base::Value response(base::Value::Type::DICT);
response.GetDict().Set(
"statusCode", response_headers_ ? response_headers_->response_code()
: net::HTTP_OK);
base::Value::Dict headers;
size_t iterator = 0;
std::string name;
std::string value;
while (response_headers_ &&
response_headers_->EnumerateHeaderLines(&iterator, &name, &value))
headers.Set(name, value);
response.GetDict().Set("headers", std::move(headers));
std::move(callback_).Run(&response);
}
bindings_->loaders_.erase(this);
}
void OnRetry(base::OnceClosure start_retry) override {}
const int stream_id_;
raw_ptr<InspectableWebContents> const bindings_;
const network::ResourceRequest resource_request_;
const net::NetworkTrafficAnnotationTag traffic_annotation_;
std::unique_ptr<network::SimpleURLLoader> loader_;
URLLoaderFactoryHolder url_loader_factory_;
DispatchCallback callback_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;
base::OneShotTimer timer_;
base::TimeDelta retry_delay_;
};
// static
void InspectableWebContents::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kDevToolsBoundsPref,
RectToDictionary(gfx::Rect{0, 0, 800, 600}));
registry->RegisterDoublePref(kDevToolsZoomPref, 0.);
registry->RegisterDictionaryPref(kDevToolsPreferences);
}
InspectableWebContents::InspectableWebContents(
std::unique_ptr<content::WebContents> web_contents,
PrefService* pref_service,
bool is_guest)
: pref_service_(pref_service),
web_contents_(std::move(web_contents)),
is_guest_(is_guest),
view_(new InspectableWebContentsView(this)) {
const base::Value* bounds_dict =
&pref_service_->GetValue(kDevToolsBoundsPref);
if (bounds_dict->is_dict()) {
devtools_bounds_ = DictionaryToRect(bounds_dict->GetDict());
// Sometimes the devtools window is out of screen or has too small size.
if (devtools_bounds_.height() < 100 || devtools_bounds_.width() < 100) {
devtools_bounds_.set_height(600);
devtools_bounds_.set_width(800);
}
if (!IsPointInScreen(devtools_bounds_.origin())) {
gfx::Rect display;
if (!is_guest && web_contents_->GetNativeView()) {
display = display::Screen::GetScreen()
->GetDisplayNearestView(web_contents_->GetNativeView())
.bounds();
} else {
display = display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
}
devtools_bounds_.set_x(display.x() +
(display.width() - devtools_bounds_.width()) / 2);
devtools_bounds_.set_y(
display.y() + (display.height() - devtools_bounds_.height()) / 2);
}
}
}
InspectableWebContents::~InspectableWebContents() {
// Unsubscribe from devtools and Clean up resources.
if (GetDevToolsWebContents())
WebContentsDestroyed();
// Let destructor destroy managed_devtools_web_contents_.
}
InspectableWebContentsView* InspectableWebContents::GetView() const {
return view_.get();
}
content::WebContents* InspectableWebContents::GetWebContents() const {
return web_contents_.get();
}
content::WebContents* InspectableWebContents::GetDevToolsWebContents() const {
if (external_devtools_web_contents_)
return external_devtools_web_contents_;
else
return managed_devtools_web_contents_.get();
}
void InspectableWebContents::InspectElement(int x, int y) {
if (agent_host_)
agent_host_->InspectElement(web_contents_->GetPrimaryMainFrame(), x, y);
}
void InspectableWebContents::SetDelegate(
InspectableWebContentsDelegate* delegate) {
delegate_ = delegate;
}
InspectableWebContentsDelegate* InspectableWebContents::GetDelegate() const {
return delegate_;
}
void InspectableWebContents::ReleaseWebContents() {
web_contents_.release();
WebContentsDestroyed();
view_.reset();
}
void InspectableWebContents::SetDockState(const std::string& state) {
if (state == "detach") {
can_dock_ = false;
} else {
can_dock_ = true;
dock_state_ = state;
}
}
void InspectableWebContents::SetDevToolsTitle(const std::u16string& title) {
devtools_title_ = title;
view_->SetTitle(devtools_title_);
}
void InspectableWebContents::SetDevToolsWebContents(
content::WebContents* devtools) {
if (!managed_devtools_web_contents_)
external_devtools_web_contents_ = devtools;
}
void InspectableWebContents::ShowDevTools(bool activate) {
if (embedder_message_dispatcher_) {
if (managed_devtools_web_contents_)
view_->ShowDevTools(activate);
return;
}
activate_ = activate;
// Show devtools only after it has done loading, this is to make sure the
// SetIsDocked is called *BEFORE* ShowDevTools.
embedder_message_dispatcher_ =
DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this);
if (!external_devtools_web_contents_) { // no external devtools
managed_devtools_web_contents_ = content::WebContents::Create(
content::WebContents::CreateParams(web_contents_->GetBrowserContext()));
managed_devtools_web_contents_->SetDelegate(this);
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
api::WebContents::FromOrCreate(isolate,
managed_devtools_web_contents_.get());
}
Observe(GetDevToolsWebContents());
AttachTo(content::DevToolsAgentHost::GetOrCreateFor(web_contents_.get()));
GetDevToolsWebContents()->GetController().LoadURL(
GetDevToolsURL(can_dock_), content::Referrer(),
ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
}
void InspectableWebContents::CloseDevTools() {
if (GetDevToolsWebContents()) {
frontend_loaded_ = false;
if (managed_devtools_web_contents_) {
view_->CloseDevTools();
managed_devtools_web_contents_.reset();
}
embedder_message_dispatcher_.reset();
if (!is_guest())
web_contents_->Focus();
}
}
bool InspectableWebContents::IsDevToolsViewShowing() {
return managed_devtools_web_contents_ && view_->IsDevToolsViewShowing();
}
std::u16string InspectableWebContents::GetDevToolsTitle() {
return view_->GetTitle();
}
void InspectableWebContents::AttachTo(
scoped_refptr<content::DevToolsAgentHost> host) {
Detach();
agent_host_ = std::move(host);
// We could use ForceAttachClient here if problem arises with
// devtools multiple session support.
agent_host_->AttachClient(this);
}
void InspectableWebContents::Detach() {
if (agent_host_)
agent_host_->DetachClient(this);
agent_host_ = nullptr;
}
void InspectableWebContents::Reattach(DispatchCallback callback) {
if (agent_host_) {
agent_host_->DetachClient(this);
agent_host_->AttachClient(this);
}
std::move(callback).Run(nullptr);
}
void InspectableWebContents::CallClientFunction(
const std::string& object_name,
const std::string& method_name,
base::Value arg1,
base::Value arg2,
base::Value arg3,
base::OnceCallback<void(base::Value)> cb) {
if (!GetDevToolsWebContents())
return;
base::Value::List arguments;
if (!arg1.is_none()) {
arguments.Append(std::move(arg1));
if (!arg2.is_none()) {
arguments.Append(std::move(arg2));
if (!arg3.is_none()) {
arguments.Append(std::move(arg3));
}
}
}
GetDevToolsWebContents()->GetPrimaryMainFrame()->ExecuteJavaScriptMethod(
base::ASCIIToUTF16(object_name), base::ASCIIToUTF16(method_name),
std::move(arguments), std::move(cb));
}
void InspectableWebContents::SaveDevToolsBounds(const gfx::Rect& bounds) {
pref_service_->Set(kDevToolsBoundsPref,
base::Value{RectToDictionary(bounds)});
devtools_bounds_ = bounds;
}
double InspectableWebContents::GetDevToolsZoomLevel() const {
return pref_service_->GetDouble(kDevToolsZoomPref);
}
void InspectableWebContents::UpdateDevToolsZoomLevel(double level) {
pref_service_->SetDouble(kDevToolsZoomPref, level);
}
void InspectableWebContents::ActivateWindow() {
// Set the zoom level.
SetZoomLevelForWebContents(GetDevToolsWebContents(), GetDevToolsZoomLevel());
}
void InspectableWebContents::CloseWindow() {
GetDevToolsWebContents()->DispatchBeforeUnload(false /* auto_cancel */);
}
void InspectableWebContents::LoadCompleted() {
frontend_loaded_ = true;
if (managed_devtools_web_contents_)
view_->ShowDevTools(activate_);
// If the devtools can dock, "SetIsDocked" will be called by devtools itself.
if (!can_dock_) {
SetIsDocked(DispatchCallback(), false);
if (!devtools_title_.empty()) {
view_->SetTitle(devtools_title_);
}
} else {
if (dock_state_.empty()) {
const base::Value::Dict& prefs =
pref_service_->GetDict(kDevToolsPreferences);
const std::string* current_dock_state =
prefs.FindString("currentDockState");
base::RemoveChars(*current_dock_state, "\"", &dock_state_);
}
#if BUILDFLAG(IS_WIN)
auto* api_web_contents = api::WebContents::From(GetWebContents());
if (api_web_contents) {
auto* win =
static_cast<NativeWindowViews*>(api_web_contents->owner_window());
// When WCO is enabled, undock the devtools if the current dock
// position overlaps with the position of window controls to avoid
// broken layout.
if (win && win->IsWindowControlsOverlayEnabled()) {
if (IsAppRTL() && dock_state_ == "left") {
dock_state_ = "undocked";
} else if (dock_state_ == "right") {
dock_state_ = "undocked";
}
}
}
#endif
std::u16string javascript = base::UTF8ToUTF16(
"EUI.DockController.DockController.instance().setDockSide(\"" +
dock_state_ + "\");");
GetDevToolsWebContents()->GetPrimaryMainFrame()->ExecuteJavaScript(
javascript, base::NullCallback());
}
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
AddDevToolsExtensionsToClient();
#endif
if (view_->GetDelegate())
view_->GetDelegate()->DevToolsOpened();
}
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
void InspectableWebContents::AddDevToolsExtensionsToClient() {
// get main browser context
auto* browser_context = web_contents_->GetBrowserContext();
const extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(browser_context);
if (!registry)
return;
base::Value::List results;
for (auto& extension : registry->enabled_extensions()) {
auto devtools_page_url =
extensions::chrome_manifest_urls::GetDevToolsPage(extension.get());
if (devtools_page_url.is_empty())
continue;
// Each devtools extension will need to be able to run in the devtools
// process. Grant the devtools process the ability to request URLs from the
// extension.
content::ChildProcessSecurityPolicy::GetInstance()->GrantRequestOrigin(
web_contents_->GetPrimaryMainFrame()->GetProcess()->GetID(),
url::Origin::Create(extension->url()));
base::Value::Dict extension_info;
extension_info.Set("startPage", devtools_page_url.spec());
extension_info.Set("name", extension->name());
extension_info.Set("exposeExperimentalAPIs",
extension->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::kExperimental));
extension_info.Set("allowFileAccess",
(extension->creation_flags() &
extensions::Extension::ALLOW_FILE_ACCESS) != 0);
results.Append(std::move(extension_info));
}
CallClientFunction("DevToolsAPI", "addExtensions",
base::Value(std::move(results)));
}
#endif
void InspectableWebContents::SetInspectedPageBounds(const gfx::Rect& rect) {
if (managed_devtools_web_contents_)
view_->SetContentsResizingStrategy(DevToolsContentsResizingStrategy{rect});
}
void InspectableWebContents::InspectElementCompleted() {}
void InspectableWebContents::InspectedURLChanged(const std::string& url) {
if (managed_devtools_web_contents_) {
if (devtools_title_.empty()) {
view_->SetTitle(
base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, url.c_str())));
}
}
}
void InspectableWebContents::LoadNetworkResource(DispatchCallback callback,
const std::string& url,
const std::string& headers,
int stream_id) {
GURL gurl(url);
if (!gurl.is_valid()) {
base::Value response(base::Value::Type::DICT);
response.GetDict().Set("statusCode", net::HTTP_NOT_FOUND);
std::move(callback).Run(&response);
return;
}
// Create traffic annotation tag.
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("devtools_network_resource", R"(
semantics {
sender: "Developer Tools"
description:
"When user opens Developer Tools, the browser may fetch additional "
"resources from the network to enrich the debugging experience "
"(e.g. source map resources)."
trigger: "User opens Developer Tools to debug a web page."
data: "Any resources requested by Developer Tools."
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting:
"It's not possible to disable this feature from settings."
})");
network::ResourceRequest resource_request;
resource_request.url = gurl;
resource_request.site_for_cookies = net::SiteForCookies::FromUrl(gurl);
resource_request.headers.AddHeadersFromString(headers);
const auto* const protocol_registry = ProtocolRegistry::FromBrowserContext(
GetDevToolsWebContents()->GetBrowserContext());
NetworkResourceLoader::URLLoaderFactoryHolder url_loader_factory;
if (gurl.SchemeIsFile()) {
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote =
AsarURLLoaderFactory::Create();
url_loader_factory = network::SharedURLLoaderFactory::Create(
std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
std::move(pending_remote)));
} else if (const auto* const protocol_handler =
protocol_registry->FindRegistered(gurl.scheme())) {
url_loader_factory = network::SharedURLLoaderFactory::Create(
std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
ElectronURLLoaderFactory::Create(protocol_handler->first,
protocol_handler->second)));
} else {
auto* partition = GetDevToolsWebContents()
->GetBrowserContext()
->GetDefaultStoragePartition();
url_loader_factory = partition->GetURLLoaderFactoryForBrowserProcess();
}
NetworkResourceLoader::Create(
stream_id, this, resource_request, traffic_annotation,
std::move(url_loader_factory), std::move(callback));
}
void InspectableWebContents::SetIsDocked(DispatchCallback callback,
bool docked) {
if (managed_devtools_web_contents_)
view_->SetIsDocked(docked, activate_);
if (!callback.is_null())
std::move(callback).Run(nullptr);
}
void InspectableWebContents::OpenInNewTab(const std::string& url) {
if (delegate_)
delegate_->DevToolsOpenInNewTab(url);
}
void InspectableWebContents::OpenSearchResultsInNewTab(
const std::string& query) {
if (delegate_)
delegate_->DevToolsOpenSearchResultsInNewTab(query);
}
void InspectableWebContents::ShowItemInFolder(
const std::string& file_system_path) {
if (file_system_path.empty())
return;
base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
platform_util::OpenPath(path.DirName(),
base::BindOnce(&OnOpenItemComplete, path));
}
void InspectableWebContents::SaveToFile(const std::string& url,
const std::string& content,
bool save_as,
bool is_base64) {
if (delegate_)
delegate_->DevToolsSaveToFile(url, content, save_as, is_base64);
}
void InspectableWebContents::AppendToFile(const std::string& url,
const std::string& content) {
if (delegate_)
delegate_->DevToolsAppendToFile(url, content);
}
void InspectableWebContents::RequestFileSystems() {
if (delegate_)
delegate_->DevToolsRequestFileSystems();
}
void InspectableWebContents::AddFileSystem(const std::string& type) {
if (delegate_)
delegate_->DevToolsAddFileSystem(type, base::FilePath());
}
void InspectableWebContents::RemoveFileSystem(
const std::string& file_system_path) {
if (delegate_)
delegate_->DevToolsRemoveFileSystem(
base::FilePath::FromUTF8Unsafe(file_system_path));
}
void InspectableWebContents::UpgradeDraggedFileSystemPermissions(
const std::string& file_system_url) {}
void InspectableWebContents::IndexPath(int request_id,
const std::string& file_system_path,
const std::string& excluded_folders) {
if (delegate_)
delegate_->DevToolsIndexPath(request_id, file_system_path,
excluded_folders);
}
void InspectableWebContents::StopIndexing(int request_id) {
if (delegate_)
delegate_->DevToolsStopIndexing(request_id);
}
void InspectableWebContents::SearchInPath(int request_id,
const std::string& file_system_path,
const std::string& query) {
if (delegate_)
delegate_->DevToolsSearchInPath(request_id, file_system_path, query);
}
void InspectableWebContents::SetWhitelistedShortcuts(
const std::string& message) {}
void InspectableWebContents::SetEyeDropperActive(bool active) {
if (delegate_)
delegate_->DevToolsSetEyeDropperActive(active);
}
void InspectableWebContents::ShowCertificateViewer(
const std::string& cert_chain) {}
void InspectableWebContents::ZoomIn() {
double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), false);
SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level);
UpdateDevToolsZoomLevel(new_level);
}
void InspectableWebContents::ZoomOut() {
double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), true);
SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level);
UpdateDevToolsZoomLevel(new_level);
}
void InspectableWebContents::ResetZoom() {
SetZoomLevelForWebContents(GetDevToolsWebContents(), 0.);
UpdateDevToolsZoomLevel(0.);
}
void InspectableWebContents::SetDevicesDiscoveryConfig(
bool discover_usb_devices,
bool port_forwarding_enabled,
const std::string& port_forwarding_config,
bool network_discovery_enabled,
const std::string& network_discovery_config) {}
void InspectableWebContents::SetDevicesUpdatesEnabled(bool enabled) {}
void InspectableWebContents::OpenRemotePage(const std::string& browser_id,
const std::string& url) {}
void InspectableWebContents::OpenNodeFrontend() {}
void InspectableWebContents::DispatchProtocolMessageFromDevToolsFrontend(
const std::string& message) {
// If the devtools wants to reload the page, hijack the message and handle it
// to the delegate.
if (base::MatchPattern(message,
"{\"id\":*,"
"\"method\":\"Page.reload\","
"\"params\":*}")) {
if (delegate_)
delegate_->DevToolsReloadPage();
return;
}
if (agent_host_)
agent_host_->DispatchProtocolMessage(
this, base::as_bytes(base::make_span(message)));
}
void InspectableWebContents::SendJsonRequest(DispatchCallback callback,
const std::string& browser_id,
const std::string& url) {
std::move(callback).Run(nullptr);
}
void InspectableWebContents::GetPreferences(DispatchCallback callback) {
const base::Value& prefs = pref_service_->GetValue(kDevToolsPreferences);
std::move(callback).Run(&prefs);
}
void InspectableWebContents::GetPreference(DispatchCallback callback,
const std::string& name) {
if (auto* pref = pref_service_->GetDict(kDevToolsPreferences).Find(name)) {
std::move(callback).Run(pref);
return;
}
// Pref wasn't found, return an empty value
base::Value no_pref;
std::move(callback).Run(&no_pref);
}
void InspectableWebContents::SetPreference(const std::string& name,
const std::string& value) {
ScopedDictPrefUpdate update(pref_service_, kDevToolsPreferences);
update->Set(name, base::Value(value));
}
void InspectableWebContents::RemovePreference(const std::string& name) {
ScopedDictPrefUpdate update(pref_service_, kDevToolsPreferences);
update->Remove(name);
}
void InspectableWebContents::ClearPreferences() {
ScopedDictPrefUpdate unsynced_update(pref_service_, kDevToolsPreferences);
unsynced_update->clear();
}
void InspectableWebContents::GetSyncInformation(DispatchCallback callback) {
base::Value result(base::Value::Type::DICT);
result.GetDict().Set("isSyncActive", false);
std::move(callback).Run(&result);
}
void InspectableWebContents::GetHostConfig(DispatchCallback callback) {
base::Value::Dict response_dict;
base::Value response = base::Value(std::move(response_dict));
std::move(callback).Run(&response);
}
void InspectableWebContents::ConnectionReady() {}
void InspectableWebContents::RegisterExtensionsAPI(const std::string& origin,
const std::string& script) {
extensions_api_[origin + "/"] = script;
}
void InspectableWebContents::HandleMessageFromDevToolsFrontend(
base::Value::Dict message) {
// TODO(alexeykuzmin): Should we expect it to exist?
if (!embedder_message_dispatcher_) {
return;
}
const std::string* method = message.FindString(kFrontendHostMethod);
base::Value* params = message.Find(kFrontendHostParams);
if (!method || (params && !params->is_list())) {
LOG(ERROR) << "Invalid message was sent to embedder: " << message;
return;
}
const base::Value::List no_params;
const base::Value::List& params_list =
params != nullptr && params->is_list() ? params->GetList() : no_params;
const int id = message.FindInt(kFrontendHostId).value_or(0);
embedder_message_dispatcher_->Dispatch(
base::BindRepeating(&InspectableWebContents::SendMessageAck,
weak_factory_.GetWeakPtr(), id),
*method, params_list);
}
void InspectableWebContents::DispatchProtocolMessage(
content::DevToolsAgentHost* agent_host,
base::span<const uint8_t> message) {
if (!frontend_loaded_)
return;
const std::string_view str_message{
reinterpret_cast<const char*>(message.data()), message.size()};
if (str_message.length() < kMaxMessageChunkSize) {
CallClientFunction("DevToolsAPI", "dispatchMessage",
base::Value(std::string(str_message)));
} else {
size_t total_size = str_message.length();
for (size_t pos = 0; pos < str_message.length();
pos += kMaxMessageChunkSize) {
base::StringPiece str_message_chunk =
str_message.substr(pos, kMaxMessageChunkSize);
CallClientFunction(
"DevToolsAPI", "dispatchMessageChunk",
base::Value(std::string(str_message_chunk)),
base::Value(base::NumberToString(pos ? 0 : total_size)));
}
}
}
void InspectableWebContents::AgentHostClosed(
content::DevToolsAgentHost* agent_host) {}
void InspectableWebContents::RenderFrameHostChanged(
content::RenderFrameHost* old_host,
content::RenderFrameHost* new_host) {
if (new_host->GetParent())
return;
frontend_host_ = content::DevToolsFrontendHost::Create(
new_host, base::BindRepeating(
&InspectableWebContents::HandleMessageFromDevToolsFrontend,
weak_factory_.GetWeakPtr()));
}
void InspectableWebContents::WebContentsDestroyed() {
if (managed_devtools_web_contents_)
managed_devtools_web_contents_->SetDelegate(nullptr);
frontend_loaded_ = false;
external_devtools_web_contents_ = nullptr;
Observe(nullptr);
Detach();
embedder_message_dispatcher_.reset();
frontend_host_.reset();
if (view_ && view_->GetDelegate())
view_->GetDelegate()->DevToolsClosed();
}
bool InspectableWebContents::HandleKeyboardEvent(
content::WebContents* source,
const input::NativeWebKeyboardEvent& event) {
auto* delegate = web_contents_->GetDelegate();
return !delegate || delegate->HandleKeyboardEvent(source, event);
}
void InspectableWebContents::CloseContents(content::WebContents* source) {
// This is where the devtools closes itself (by clicking the x button).
CloseDevTools();
}
void InspectableWebContents::RunFileChooser(
content::RenderFrameHost* render_frame_host,
scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) {
auto* delegate = web_contents_->GetDelegate();
if (delegate)
delegate->RunFileChooser(render_frame_host, std::move(listener), params);
}
void InspectableWebContents::EnumerateDirectory(
content::WebContents* source,
scoped_refptr<content::FileSelectListener> listener,
const base::FilePath& path) {
auto* delegate = web_contents_->GetDelegate();
if (delegate)
delegate->EnumerateDirectory(source, std::move(listener), path);
}
void InspectableWebContents::OnWebContentsFocused(
content::RenderWidgetHost* render_widget_host) {
#if defined(TOOLKIT_VIEWS)
if (view_->GetDelegate())
view_->GetDelegate()->DevToolsFocused();
#endif
}
void InspectableWebContents::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
if (navigation_handle->IsInPrimaryMainFrame()) {
if (navigation_handle->GetRenderFrameHost() ==
GetDevToolsWebContents()->GetPrimaryMainFrame() &&
frontend_host_) {
return;
}
frontend_host_ = content::DevToolsFrontendHost::Create(
web_contents()->GetPrimaryMainFrame(),
base::BindRepeating(
&InspectableWebContents::HandleMessageFromDevToolsFrontend,
base::Unretained(this)));
return;
}
}
void InspectableWebContents::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (navigation_handle->IsInPrimaryMainFrame() ||
!navigation_handle->GetURL().SchemeIs("chrome-extension") ||
!navigation_handle->HasCommitted())
return;
content::RenderFrameHost* frame = navigation_handle->GetRenderFrameHost();
auto origin = navigation_handle->GetURL().DeprecatedGetOriginAsURL().spec();
auto it = extensions_api_.find(origin);
if (it == extensions_api_.end())
return;
// Injected Script from devtools frontend doesn't expose chrome,
// most likely bug in chromium.
base::ReplaceFirstSubstringAfterOffset(&it->second, 0, "var chrome",
"var chrome = window.chrome ");
auto script = base::StringPrintf(
"%s(\"%s\")", it->second.c_str(),
base::Uuid::GenerateRandomV4().AsLowercaseString().c_str());
// Invoking content::DevToolsFrontendHost::SetupExtensionsAPI(frame, script);
// should be enough, but it seems to be a noop currently.
frame->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script),
base::NullCallback());
}
void InspectableWebContents::SendMessageAck(int request_id,
const base::Value* arg) {
if (arg) {
CallClientFunction("DevToolsAPI", "embedderMessageAck",
base::Value(request_id), arg->Clone());
} else {
CallClientFunction("DevToolsAPI", "embedderMessageAck",
base::Value(request_id));
}
}
} // namespace electron