// Copyright (c) 2014 GitHub, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #include "shell/browser/api/electron_api_web_contents.h" #include #include #include #include #include #include #include #include #include "base/containers/contains.h" #include "base/containers/fixed_flat_map.h" #include "base/containers/id_map.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" #include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" #include "base/task/current_thread.h" #include "base/task/thread_pool.h" #include "base/threading/scoped_blocking_call.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h" #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" #include "chrome/browser/ui/views/eye_dropper/eye_dropper.h" #include "chrome/common/pref_names.h" #include "components/embedder_support/user_agent_utils.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/security_state/content/content_utils.h" #include "components/security_state/core/security_state.h" #include "content/browser/renderer_host/frame_tree_node.h" // nogncheck #include "content/browser/renderer_host/navigation_controller_impl.h" // nogncheck #include "content/browser/renderer_host/render_frame_host_manager.h" // nogncheck #include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck #include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/context_menu_params.h" #include "content/public/browser/desktop_media_id.h" #include "content/public/browser/desktop_streams_registry.h" #include "content/public/browser/download_request_utils.h" #include "content/public/browser/favicon_status.h" #include "content/public/browser/file_select_listener.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/service_worker_context.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/common/input/native_web_keyboard_event.h" #include "content/public/common/referrer_type_converters.h" #include "content/public/common/result_codes.h" #include "content/public/common/webplugininfo.h" #include "electron/buildflags/buildflags.h" #include "electron/shell/common/api/api.mojom.h" #include "gin/arguments.h" #include "gin/data_object_builder.h" #include "gin/handle.h" #include "gin/object_template_builder.h" #include "gin/wrappable.h" #include "media/base/mime_util.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/platform_handle.h" #include "ppapi/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "shell/browser/api/electron_api_browser_window.h" #include "shell/browser/api/electron_api_debugger.h" #include "shell/browser/api/electron_api_session.h" #include "shell/browser/api/electron_api_web_frame_main.h" #include "shell/browser/api/message_port.h" #include "shell/browser/browser.h" #include "shell/browser/child_web_contents_tracker.h" #include "shell/browser/electron_autofill_driver_factory.h" #include "shell/browser/electron_browser_client.h" #include "shell/browser/electron_browser_context.h" #include "shell/browser/electron_browser_main_parts.h" #include "shell/browser/electron_navigation_throttle.h" #include "shell/browser/file_select_helper.h" #include "shell/browser/native_window.h" #include "shell/browser/osr/osr_render_widget_host_view.h" #include "shell/browser/osr/osr_web_contents_view.h" #include "shell/browser/session_preferences.h" #include "shell/browser/ui/drag_util.h" #include "shell/browser/ui/file_dialog.h" #include "shell/browser/ui/inspectable_web_contents.h" #include "shell/browser/ui/inspectable_web_contents_view.h" #include "shell/browser/web_contents_permission_helper.h" #include "shell/browser/web_contents_preferences.h" #include "shell/browser/web_contents_zoom_controller.h" #include "shell/browser/web_view_guest_delegate.h" #include "shell/browser/web_view_manager.h" #include "shell/common/api/electron_api_native_image.h" #include "shell/common/api/electron_bindings.h" #include "shell/common/color_util.h" #include "shell/common/electron_constants.h" #include "shell/common/gin_converters/base_converter.h" #include "shell/common/gin_converters/blink_converter.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/content_converter.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/frame_converter.h" #include "shell/common/gin_converters/gfx_converter.h" #include "shell/common/gin_converters/gurl_converter.h" #include "shell/common/gin_converters/image_converter.h" #include "shell/common/gin_converters/net_converter.h" #include "shell/common/gin_converters/optional_converter.h" #include "shell/common/gin_converters/value_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/language_util.h" #include "shell/common/node_includes.h" #include "shell/common/options_switches.h" #include "shell/common/process_util.h" #include "shell/common/thread_restrictions.h" #include "shell/common/v8_value_serializer.h" #include "storage/browser/file_system/isolated_context.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/public/common/messaging/transferable_message_mojom_traits.h" #include "third_party/blink/public/common/page/page_zoom.h" #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h" #include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h" #include "third_party/blink/public/mojom/renderer_preferences.mojom.h" #include "ui/base/cursor/cursor.h" #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/display/screen.h" #include "ui/events/base_event_utils.h" #if BUILDFLAG(IS_WIN) #include "shell/browser/native_window_views.h" #endif #if !BUILDFLAG(IS_MAC) #include "ui/aura/window.h" #else #include "ui/base/cocoa/defaults_utils.h" #endif #if BUILDFLAG(IS_LINUX) #include "ui/linux/linux_ui.h" #endif #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) #include "ui/gfx/font_render_params.h" #endif #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #include "extensions/browser/script_executor.h" #include "extensions/browser/view_type_utils.h" #include "extensions/common/mojom/view_type.mojom.h" #include "shell/browser/extensions/electron_extension_web_contents_observer.h" #endif #if BUILDFLAG(ENABLE_PRINTING) #include "chrome/browser/printing/print_view_manager_base.h" #include "components/printing/browser/print_manager_utils.h" #include "components/printing/browser/print_to_pdf/pdf_print_result.h" #include "components/printing/browser/print_to_pdf/pdf_print_utils.h" #include "printing/mojom/print.mojom.h" // nogncheck #include "printing/page_range.h" #include "shell/browser/printing/print_view_manager_electron.h" #include "shell/browser/printing/printing_utils.h" #if BUILDFLAG(IS_WIN) #include "printing/backend/win_helper.h" #endif #endif // BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_PDF_VIEWER) #include "components/pdf/browser/pdf_document_helper.h" // nogncheck #include "shell/browser/electron_pdf_document_helper_client.h" #endif #if BUILDFLAG(ENABLE_PLUGINS) #include "content/public/browser/plugin_service.h" #endif #if !IS_MAS_BUILD() #include "chrome/browser/hang_monitor/hang_crash_dump.h" // nogncheck #endif namespace gin { #if BUILDFLAG(ENABLE_PRINTING) template <> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, printing::mojom::MarginType* out) { using Val = printing::mojom::MarginType; static constexpr auto Lookup = base::MakeFixedFlatMap({ {"custom", Val::kCustomMargins}, {"default", Val::kDefaultMargins}, {"none", Val::kNoMargins}, {"printableArea", Val::kPrintableAreaMargins}, }); return FromV8WithLookup(isolate, val, Lookup, out); } }; template <> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, printing::mojom::DuplexMode* out) { using Val = printing::mojom::DuplexMode; static constexpr auto Lookup = base::MakeFixedFlatMap({ {"longEdge", Val::kLongEdge}, {"shortEdge", Val::kShortEdge}, {"simplex", Val::kSimplex}, }); return FromV8WithLookup(isolate, val, Lookup, out); } }; #endif template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, WindowOpenDisposition val) { std::string disposition = "other"; switch (val) { case WindowOpenDisposition::CURRENT_TAB: disposition = "default"; break; case WindowOpenDisposition::NEW_FOREGROUND_TAB: disposition = "foreground-tab"; break; case WindowOpenDisposition::NEW_BACKGROUND_TAB: disposition = "background-tab"; break; case WindowOpenDisposition::NEW_POPUP: case WindowOpenDisposition::NEW_WINDOW: disposition = "new-window"; break; case WindowOpenDisposition::SAVE_TO_DISK: disposition = "save-to-disk"; break; default: break; } return gin::ConvertToV8(isolate, disposition); } }; template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, content::JavaScriptDialogType val) { switch (val) { case content::JAVASCRIPT_DIALOG_TYPE_ALERT: return gin::ConvertToV8(isolate, "alert"); case content::JAVASCRIPT_DIALOG_TYPE_CONFIRM: return gin::ConvertToV8(isolate, "confirm"); case content::JAVASCRIPT_DIALOG_TYPE_PROMPT: return gin::ConvertToV8(isolate, "prompt"); } } }; template <> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, content::SavePageType* out) { using Val = content::SavePageType; static constexpr auto Lookup = base::MakeFixedFlatMap({ {"htmlcomplete", Val::SAVE_PAGE_TYPE_AS_COMPLETE_HTML}, {"htmlonly", Val::SAVE_PAGE_TYPE_AS_ONLY_HTML}, {"mhtml", Val::SAVE_PAGE_TYPE_AS_MHTML}, }); return FromV8WithLowerLookup(isolate, val, Lookup, out); } }; template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, electron::api::WebContents::Type val) { using Type = electron::api::WebContents::Type; std::string type; switch (val) { case Type::kBackgroundPage: type = "backgroundPage"; break; case Type::kBrowserWindow: type = "window"; break; case Type::kBrowserView: type = "browserView"; break; case Type::kRemote: type = "remote"; break; case Type::kWebView: type = "webview"; break; case Type::kOffScreen: type = "offscreen"; break; default: break; } return gin::ConvertToV8(isolate, type); } static bool FromV8(v8::Isolate* isolate, v8::Local val, electron::api::WebContents::Type* out) { using Val = electron::api::WebContents::Type; static constexpr auto Lookup = base::MakeFixedFlatMap({ {"backgroundPage", Val::kBackgroundPage}, {"browserView", Val::kBrowserView}, {"offscreen", Val::kOffScreen}, {"webview", Val::kWebView}, }); return FromV8WithLookup(isolate, val, Lookup, out); } }; template <> struct Converter> { static v8::Local ToV8( v8::Isolate* isolate, const scoped_refptr& val) { gin_helper::Dictionary dict(isolate, v8::Object::New(isolate)); dict.Set("id", val->GetId()); dict.Set("url", val->GetURL().spec()); return dict.GetHandle(); } }; template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, content::NavigationEntry* entry) { if (!entry) { return v8::Null(isolate); } gin_helper::Dictionary dict(isolate, v8::Object::New(isolate)); dict.Set("url", entry->GetURL().spec()); dict.Set("title", entry->GetTitleForDisplay()); return dict.GetHandle(); } }; } // namespace gin namespace electron::api { namespace { constexpr std::string_view CursorTypeToString( ui::mojom::CursorType cursor_type) { switch (cursor_type) { case ui::mojom::CursorType::kPointer: return "pointer"; case ui::mojom::CursorType::kCross: return "crosshair"; case ui::mojom::CursorType::kHand: return "hand"; case ui::mojom::CursorType::kIBeam: return "text"; case ui::mojom::CursorType::kWait: return "wait"; case ui::mojom::CursorType::kHelp: return "help"; case ui::mojom::CursorType::kEastResize: return "e-resize"; case ui::mojom::CursorType::kNorthResize: return "n-resize"; case ui::mojom::CursorType::kNorthEastResize: return "ne-resize"; case ui::mojom::CursorType::kNorthWestResize: return "nw-resize"; case ui::mojom::CursorType::kSouthResize: return "s-resize"; case ui::mojom::CursorType::kSouthEastResize: return "se-resize"; case ui::mojom::CursorType::kSouthWestResize: return "sw-resize"; case ui::mojom::CursorType::kWestResize: return "w-resize"; case ui::mojom::CursorType::kNorthSouthResize: return "ns-resize"; case ui::mojom::CursorType::kEastWestResize: return "ew-resize"; case ui::mojom::CursorType::kNorthEastSouthWestResize: return "nesw-resize"; case ui::mojom::CursorType::kNorthWestSouthEastResize: return "nwse-resize"; case ui::mojom::CursorType::kColumnResize: return "col-resize"; case ui::mojom::CursorType::kRowResize: return "row-resize"; case ui::mojom::CursorType::kMiddlePanning: return "m-panning"; case ui::mojom::CursorType::kMiddlePanningVertical: return "m-panning-vertical"; case ui::mojom::CursorType::kMiddlePanningHorizontal: return "m-panning-horizontal"; case ui::mojom::CursorType::kEastPanning: return "e-panning"; case ui::mojom::CursorType::kNorthPanning: return "n-panning"; case ui::mojom::CursorType::kNorthEastPanning: return "ne-panning"; case ui::mojom::CursorType::kNorthWestPanning: return "nw-panning"; case ui::mojom::CursorType::kSouthPanning: return "s-panning"; case ui::mojom::CursorType::kSouthEastPanning: return "se-panning"; case ui::mojom::CursorType::kSouthWestPanning: return "sw-panning"; case ui::mojom::CursorType::kWestPanning: return "w-panning"; case ui::mojom::CursorType::kMove: return "move"; case ui::mojom::CursorType::kVerticalText: return "vertical-text"; case ui::mojom::CursorType::kCell: return "cell"; case ui::mojom::CursorType::kContextMenu: return "context-menu"; case ui::mojom::CursorType::kAlias: return "alias"; case ui::mojom::CursorType::kProgress: return "progress"; case ui::mojom::CursorType::kNoDrop: return "nodrop"; case ui::mojom::CursorType::kCopy: return "copy"; case ui::mojom::CursorType::kNone: return "none"; case ui::mojom::CursorType::kNotAllowed: return "not-allowed"; case ui::mojom::CursorType::kZoomIn: return "zoom-in"; case ui::mojom::CursorType::kZoomOut: return "zoom-out"; case ui::mojom::CursorType::kGrab: return "grab"; case ui::mojom::CursorType::kGrabbing: return "grabbing"; case ui::mojom::CursorType::kCustom: return "custom"; case ui::mojom::CursorType::kNull: return "null"; case ui::mojom::CursorType::kDndNone: return "drag-drop-none"; case ui::mojom::CursorType::kDndMove: return "drag-drop-move"; case ui::mojom::CursorType::kDndCopy: return "drag-drop-copy"; case ui::mojom::CursorType::kDndLink: return "drag-drop-link"; case ui::mojom::CursorType::kNorthSouthNoResize: return "ns-no-resize"; case ui::mojom::CursorType::kEastWestNoResize: return "ew-no-resize"; case ui::mojom::CursorType::kNorthEastSouthWestNoResize: return "nesw-no-resize"; case ui::mojom::CursorType::kNorthWestSouthEastNoResize: return "nwse-no-resize"; default: return "default"; } } base::IDMap& GetAllWebContents() { static base::NoDestructor> s_all_web_contents; return *s_all_web_contents; } void OnCapturePageDone(gin_helper::Promise promise, base::ScopedClosureRunner capture_handle, const SkBitmap& bitmap) { auto ui_task_runner = content::GetUIThreadTaskRunner({}); if (!ui_task_runner->RunsTasksInCurrentSequence()) { ui_task_runner->PostTask( FROM_HERE, base::BindOnce(&OnCapturePageDone, std::move(promise), std::move(capture_handle), bitmap)); return; } // Hack to enable transparency in captured image promise.Resolve(gfx::Image::CreateFrom1xBitmap(bitmap)); capture_handle.RunAndReset(); } std::optional GetCursorBlinkInterval() { #if BUILDFLAG(IS_MAC) std::optional system_value( ui::TextInsertionCaretBlinkPeriodFromDefaults()); if (system_value) return *system_value; #elif BUILDFLAG(IS_LINUX) if (auto* linux_ui = ui::LinuxUi::instance()) return linux_ui->GetCursorBlinkInterval(); #elif BUILDFLAG(IS_WIN) const auto system_msec = ::GetCaretBlinkTime(); if (system_msec != 0) { return (system_msec == INFINITE) ? base::TimeDelta() : base::Milliseconds(system_msec); } #endif return std::nullopt; } struct UserDataLink : public base::SupportsUserData::Data { explicit UserDataLink(base::WeakPtr contents) : web_contents(contents) {} base::WeakPtr web_contents; }; const void* kElectronApiWebContentsKey = &kElectronApiWebContentsKey; const char kRootName[] = ""; struct FileSystem { FileSystem() = default; FileSystem(const std::string& type, const std::string& file_system_name, const std::string& root_url, const std::string& file_system_path) : type(type), file_system_name(file_system_name), root_url(root_url), file_system_path(file_system_path) {} std::string type; std::string file_system_name; std::string root_url; std::string file_system_path; }; std::string RegisterFileSystem(content::WebContents* web_contents, const base::FilePath& path) { auto* isolated_context = storage::IsolatedContext::GetInstance(); std::string root_name(kRootName); storage::IsolatedContext::ScopedFSHandle file_system = isolated_context->RegisterFileSystemForPath( storage::kFileSystemTypeLocal, std::string(), path, &root_name); content::ChildProcessSecurityPolicy* policy = content::ChildProcessSecurityPolicy::GetInstance(); content::RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); int renderer_id = render_view_host->GetProcess()->GetID(); policy->GrantReadFileSystem(renderer_id, file_system.id()); policy->GrantWriteFileSystem(renderer_id, file_system.id()); policy->GrantCreateFileForFileSystem(renderer_id, file_system.id()); policy->GrantDeleteFromFileSystem(renderer_id, file_system.id()); if (!policy->CanReadFile(renderer_id, path)) policy->GrantReadFile(renderer_id, path); return file_system.id(); } FileSystem CreateFileSystemStruct(content::WebContents* web_contents, const std::string& file_system_id, const std::string& file_system_path, const std::string& type) { const GURL origin = web_contents->GetURL().DeprecatedGetOriginAsURL(); std::string file_system_name = storage::GetIsolatedFileSystemName(origin, file_system_id); std::string root_url = storage::GetIsolatedFileSystemRootURIString( origin, file_system_id, kRootName); return FileSystem(type, file_system_name, root_url, file_system_path); } base::Value::Dict CreateFileSystemValue(const FileSystem& file_system) { base::Value::Dict value; value.Set("type", file_system.type); value.Set("fileSystemName", file_system.file_system_name); value.Set("rootURL", file_system.root_url); value.Set("fileSystemPath", file_system.file_system_path); return value; } void WriteToFile(const base::FilePath& path, const std::string& content) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::WILL_BLOCK); DCHECK(!path.empty()); base::WriteFile(path, content.data(), content.size()); } void AppendToFile(const base::FilePath& path, const std::string& content) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::WILL_BLOCK); DCHECK(!path.empty()); base::AppendToFile(path, content); } PrefService* GetPrefService(content::WebContents* web_contents) { auto* context = web_contents->GetBrowserContext(); return static_cast(context)->prefs(); } std::map GetAddedFileSystemPaths( content::WebContents* web_contents) { auto* pref_service = GetPrefService(web_contents); const base::Value::Dict& file_system_paths = pref_service->GetDict(prefs::kDevToolsFileSystemPaths); std::map result; for (auto it : file_system_paths) { std::string type = it.second.is_string() ? it.second.GetString() : std::string(); result[it.first] = type; } return result; } bool IsDevToolsFileSystemAdded(content::WebContents* web_contents, const std::string& file_system_path) { return base::Contains(GetAddedFileSystemPaths(web_contents), file_system_path); } content::RenderFrameHost* GetRenderFrameHost( content::NavigationHandle* navigation_handle) { int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId(); content::FrameTreeNode* frame_tree_node = content::FrameTreeNode::GloballyFindByID(frame_tree_node_id); content::RenderFrameHostManager* render_manager = frame_tree_node->render_manager(); content::RenderFrameHost* frame_host = nullptr; if (render_manager) { frame_host = render_manager->speculative_frame_host(); if (!frame_host) frame_host = render_manager->current_frame_host(); } return frame_host; } } // namespace #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) WebContents::Type GetTypeFromViewType(extensions::mojom::ViewType view_type) { switch (view_type) { case extensions::mojom::ViewType::kExtensionBackgroundPage: return WebContents::Type::kBackgroundPage; case extensions::mojom::ViewType::kAppWindow: case extensions::mojom::ViewType::kComponent: case extensions::mojom::ViewType::kExtensionPopup: case extensions::mojom::ViewType::kBackgroundContents: case extensions::mojom::ViewType::kExtensionGuest: case extensions::mojom::ViewType::kTabContents: case extensions::mojom::ViewType::kOffscreenDocument: case extensions::mojom::ViewType::kExtensionSidePanel: case extensions::mojom::ViewType::kInvalid: return WebContents::Type::kRemote; } } #endif WebContents::WebContents(v8::Isolate* isolate, content::WebContents* web_contents) : content::WebContentsObserver(web_contents), type_(Type::kRemote), id_(GetAllWebContents().Add(this)) #if BUILDFLAG(ENABLE_PRINTING) , print_task_runner_(CreatePrinterHandlerTaskRunner()) #endif { #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) // WebContents created by extension host will have valid ViewType set. extensions::mojom::ViewType view_type = extensions::GetViewType(web_contents); if (view_type != extensions::mojom::ViewType::kInvalid) { InitWithExtensionView(isolate, web_contents, view_type); } extensions::ElectronExtensionWebContentsObserver::CreateForWebContents( web_contents); script_executor_ = std::make_unique(web_contents); #endif auto session = Session::CreateFrom(isolate, GetBrowserContext()); session_.Reset(isolate, session.ToV8()); SetUserAgent(GetBrowserContext()->GetUserAgent()); web_contents->SetUserData(kElectronApiWebContentsKey, std::make_unique(GetWeakPtr())); InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate)); } WebContents::WebContents(v8::Isolate* isolate, std::unique_ptr web_contents, Type type) : content::WebContentsObserver(web_contents.get()), type_(type), id_(GetAllWebContents().Add(this)) #if BUILDFLAG(ENABLE_PRINTING) , print_task_runner_(CreatePrinterHandlerTaskRunner()) #endif { DCHECK(type != Type::kRemote) << "Can't take ownership of a remote WebContents"; auto session = Session::CreateFrom(isolate, GetBrowserContext()); session_.Reset(isolate, session.ToV8()); InitWithSessionAndOptions(isolate, std::move(web_contents), session, gin::Dictionary::CreateEmpty(isolate)); } WebContents::WebContents(v8::Isolate* isolate, const gin_helper::Dictionary& options) : id_(GetAllWebContents().Add(this)) #if BUILDFLAG(ENABLE_PRINTING) , print_task_runner_(CreatePrinterHandlerTaskRunner()) #endif { // Read options. options.Get("backgroundThrottling", &background_throttling_); // Get type options.Get("type", &type_); // Get transparent for guest view options.Get("transparent", &guest_transparent_); bool b = false; if (options.Get(options::kOffscreen, &b) && b) type_ = Type::kOffScreen; // Init embedder earlier options.Get("embedder", &embedder_); // Whether to enable DevTools. options.Get("devTools", &enable_devtools_); bool initially_shown = true; options.Get(options::kShow, &initially_shown); // Obtain the session. std::string partition; gin::Handle session; if (options.Get("session", &session) && !session.IsEmpty()) { } else if (options.Get("partition", &partition)) { session = Session::FromPartition(isolate, partition); } else { // Use the default session if not specified. session = Session::FromPartition(isolate, ""); } session_.Reset(isolate, session.ToV8()); std::unique_ptr web_contents; if (is_guest()) { scoped_refptr site_instance = content::SiteInstance::CreateForURL(session->browser_context(), GURL("chrome-guest://fake-host")); content::WebContents::CreateParams params(session->browser_context(), site_instance); guest_delegate_ = std::make_unique(embedder_->web_contents(), this); params.guest_delegate = guest_delegate_.get(); if (embedder_ && embedder_->IsOffScreen()) { auto* view = new OffScreenWebContentsView( false, base::BindRepeating(&WebContents::OnPaint, base::Unretained(this))); params.view = view; params.delegate_view = view; web_contents = content::WebContents::Create(params); view->SetWebContents(web_contents.get()); } else { web_contents = content::WebContents::Create(params); } } else if (IsOffScreen()) { // webPreferences does not have a transparent option, so if the window needs // to be transparent, that will be set at electron_api_browser_window.cc#L57 // and we then need to pull it back out and check it here. std::string background_color; options.GetHidden(options::kBackgroundColor, &background_color); bool transparent = ParseCSSColor(background_color) == SK_ColorTRANSPARENT; content::WebContents::CreateParams params(session->browser_context()); auto* view = new OffScreenWebContentsView( transparent, base::BindRepeating(&WebContents::OnPaint, base::Unretained(this))); params.view = view; params.delegate_view = view; web_contents = content::WebContents::Create(params); view->SetWebContents(web_contents.get()); } else { content::WebContents::CreateParams params(session->browser_context()); params.initially_hidden = !initially_shown; web_contents = content::WebContents::Create(params); } InitWithSessionAndOptions(isolate, std::move(web_contents), session, options); } void WebContents::InitZoomController(content::WebContents* web_contents, const gin_helper::Dictionary& options) { WebContentsZoomController::CreateForWebContents(web_contents); zoom_controller_ = WebContentsZoomController::FromWebContents(web_contents); double zoom_factor; if (options.Get(options::kZoomFactor, &zoom_factor)) zoom_controller_->SetDefaultZoomFactor(zoom_factor); // Nothing to do with ZoomController, but this function gets called in all // init cases! content::RenderViewHost* host = web_contents->GetRenderViewHost(); if (host) host->GetWidget()->AddInputEventObserver(this); } void WebContents::InitWithSessionAndOptions( v8::Isolate* isolate, std::unique_ptr owned_web_contents, gin::Handle session, const gin_helper::Dictionary& options) { Observe(owned_web_contents.get()); InitWithWebContents(std::move(owned_web_contents), session->browser_context(), is_guest()); inspectable_web_contents_->GetView()->SetDelegate(this); auto* prefs = web_contents()->GetMutableRendererPrefs(); // Collect preferred languages from OS and browser process. accept_languages // effects HTTP header, navigator.languages, and CJK fallback font selection. // // Note that an application locale set to the browser process might be // different with the one set to the preference list. // (e.g. overridden with --lang) std::string accept_languages = g_browser_process->GetApplicationLocale() + ","; for (auto const& language : electron::GetPreferredLanguages()) { if (language == g_browser_process->GetApplicationLocale()) continue; accept_languages += language + ","; } accept_languages.pop_back(); prefs->accept_languages = accept_languages; #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) // Update font settings. static const gfx::FontRenderParams params( gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr)); prefs->should_antialias_text = params.antialiasing; prefs->use_subpixel_positioning = params.subpixel_positioning; prefs->hinting = params.hinting; prefs->use_autohinter = params.autohinter; prefs->use_bitmaps = params.use_bitmaps; prefs->subpixel_rendering = params.subpixel_rendering; #endif // Honor the system's cursor blink rate settings if (auto interval = GetCursorBlinkInterval()) prefs->caret_blink_interval = *interval; // Save the preferences in C++. // If there's already a WebContentsPreferences object, we created it as part // of the webContents.setWindowOpenHandler path, so don't overwrite it. if (!WebContentsPreferences::From(web_contents())) { new WebContentsPreferences(web_contents(), options); } // Trigger re-calculation of webkit prefs. web_contents()->NotifyPreferencesChanged(); WebContentsPermissionHelper::CreateForWebContents(web_contents()); InitZoomController(web_contents(), options); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) extensions::ElectronExtensionWebContentsObserver::CreateForWebContents( web_contents()); script_executor_ = std::make_unique(web_contents()); #endif AutofillDriverFactory::CreateForWebContents(web_contents()); SetUserAgent(GetBrowserContext()->GetUserAgent()); if (is_guest()) { NativeWindow* owner_window = nullptr; if (embedder_) { // New WebContents's owner_window is the embedder's owner_window. auto* relay = NativeWindowRelay::FromWebContents(embedder_->web_contents()); if (relay) owner_window = relay->GetNativeWindow(); } if (owner_window) SetOwnerWindow(owner_window); } web_contents()->SetUserData(kElectronApiWebContentsKey, std::make_unique(GetWeakPtr())); } #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) void WebContents::InitWithExtensionView(v8::Isolate* isolate, content::WebContents* web_contents, extensions::mojom::ViewType view_type) { // Must reassign type prior to calling `Init`. type_ = GetTypeFromViewType(view_type); if (type_ == Type::kRemote) return; if (type_ == Type::kBackgroundPage) // non-background-page WebContents are retained by other classes. We need // to pin here to prevent background-page WebContents from being GC'd. // The background page api::WebContents will live until the underlying // content::WebContents is destroyed. Pin(isolate); // Allow toggling DevTools for background pages Observe(web_contents); InitWithWebContents(std::unique_ptr(web_contents), GetBrowserContext(), is_guest()); inspectable_web_contents_->GetView()->SetDelegate(this); } #endif void WebContents::InitWithWebContents( std::unique_ptr web_contents, ElectronBrowserContext* browser_context, bool is_guest) { browser_context_ = browser_context; web_contents->SetDelegate(this); #if BUILDFLAG(ENABLE_PRINTING) PrintViewManagerElectron::CreateForWebContents(web_contents.get()); #endif // Determine whether the WebContents is offscreen. auto* web_preferences = WebContentsPreferences::From(web_contents.get()); offscreen_ = web_preferences && web_preferences->IsOffscreen(); // Create InspectableWebContents. inspectable_web_contents_ = std::make_unique( std::move(web_contents), browser_context->prefs(), is_guest); inspectable_web_contents_->SetDelegate(this); } WebContents::~WebContents() { if (owner_window_) { owner_window_->RemoveBackgroundThrottlingSource(this); } if (web_contents()) { content::RenderViewHost* host = web_contents()->GetRenderViewHost(); if (host) host->GetWidget()->RemoveInputEventObserver(this); } if (!inspectable_web_contents_) { WebContentsDestroyed(); return; } inspectable_web_contents_->GetView()->SetDelegate(nullptr); // This event is only for internal use, which is emitted when WebContents is // being destroyed. Emit("will-destroy"); // For guest view based on OOPIF, the WebContents is released by the embedder // frame, and we need to clear the reference to the memory. bool not_owned_by_this = is_guest() && attached_; #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) // And background pages are owned by extensions::ExtensionHost. if (type_ == Type::kBackgroundPage) not_owned_by_this = true; #endif if (not_owned_by_this) { inspectable_web_contents_->ReleaseWebContents(); WebContentsDestroyed(); } // InspectableWebContents will be automatically destroyed. } void WebContents::DeleteThisIfAlive() { // It is possible that the FirstWeakCallback has been called but the // SecondWeakCallback has not, in this case the garbage collection of // WebContents has already started and we should not |delete this|. // Calling |GetWrapper| can detect this corner case. auto* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); v8::Local wrapper; if (!GetWrapper(isolate).ToLocal(&wrapper)) return; delete this; } void WebContents::Destroy() { // The content::WebContents should be destroyed asynchronously when possible // as user may choose to destroy WebContents during an event of it. if (Browser::Get()->is_shutting_down() || is_guest()) { DeleteThisIfAlive(); } else { content::GetUIThreadTaskRunner({})->PostTask( FROM_HERE, base::BindOnce(&WebContents::DeleteThisIfAlive, GetWeakPtr())); } } void WebContents::Close(std::optional options) { bool dispatch_beforeunload = false; if (options) options->Get("waitForBeforeUnload", &dispatch_beforeunload); if (dispatch_beforeunload && web_contents()->NeedToFireBeforeUnloadOrUnloadEvents()) { NotifyUserActivation(); web_contents()->DispatchBeforeUnload(false /* auto_cancel */); } else { web_contents()->Close(); } } bool WebContents::DidAddMessageToConsole( content::WebContents* source, blink::mojom::ConsoleMessageLevel level, const std::u16string& message, int32_t line_no, const std::u16string& source_id) { return Emit("console-message", static_cast(level), message, line_no, source_id); } void WebContents::OnCreateWindow( const GURL& target_url, const content::Referrer& referrer, const std::string& frame_name, WindowOpenDisposition disposition, const std::string& features, const scoped_refptr& body) { Emit("-new-window", target_url, frame_name, disposition, features, referrer, body); } void WebContents::WebContentsCreatedWithFullParams( content::WebContents* source_contents, int opener_render_process_id, int opener_render_frame_id, const content::mojom::CreateNewWindowParams& params, content::WebContents* new_contents) { ChildWebContentsTracker::CreateForWebContents(new_contents); auto* tracker = ChildWebContentsTracker::FromWebContents(new_contents); tracker->url = params.target_url; tracker->frame_name = params.frame_name; tracker->referrer = params.referrer.To(); tracker->raw_features = params.raw_features; tracker->body = params.body; v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); gin_helper::Dictionary dict; gin::ConvertFromV8(isolate, pending_child_web_preferences_.Get(isolate), &dict); pending_child_web_preferences_.Reset(); // Associate the preferences passed in via `setWindowOpenHandler` with the // content::WebContents that was just created for the child window. These // preferences will be picked up by the RenderWidgetHost via its call to the // delegate's OverrideWebkitPrefs. new WebContentsPreferences(new_contents, dict); } bool WebContents::IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, const content::mojom::CreateNewWindowParams& params) { bool default_prevented = Emit( "-will-add-new-contents", params.target_url, params.frame_name, params.raw_features, params.disposition, *params.referrer, params.body); // If the app prevented the default, redirect to CreateCustomWebContents, // which always returns nullptr, which will result in the window open being // prevented (window.open() will return null in the renderer). return default_prevented; } void WebContents::SetNextChildWebPreferences( const gin_helper::Dictionary preferences) { v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); // Store these prefs for when Chrome calls WebContentsCreatedWithFullParams // with the new child contents. pending_child_web_preferences_.Reset(isolate, preferences.GetHandle()); } content::WebContents* WebContents::CreateCustomWebContents( content::RenderFrameHost* opener, content::SiteInstance* source_site_instance, bool is_new_browsing_instance, const GURL& opener_url, const std::string& frame_name, const GURL& target_url, const content::StoragePartitionConfig& partition_config, content::SessionStorageNamespace* session_storage_namespace) { return nullptr; } void WebContents::AddNewContents( content::WebContents* source, std::unique_ptr new_contents, const GURL& target_url, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& window_features, bool user_gesture, bool* was_blocked) { auto* tracker = ChildWebContentsTracker::FromWebContents(new_contents.get()); DCHECK(tracker); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); auto api_web_contents = CreateAndTake(isolate, std::move(new_contents), Type::kBrowserWindow); // We call RenderFrameCreated here as at this point the empty "about:blank" // render frame has already been created. If the window never navigates again // RenderFrameCreated won't be called and certain prefs like // "kBackgroundColor" will not be applied. auto* frame = api_web_contents->MainFrame(); if (frame) { api_web_contents->HandleNewRenderFrame(frame); } if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture, window_features.bounds.x(), window_features.bounds.y(), window_features.bounds.width(), window_features.bounds.height(), tracker->url, tracker->frame_name, tracker->referrer, tracker->raw_features, tracker->body)) { api_web_contents->Destroy(); } } content::WebContents* WebContents::OpenURLFromTab( content::WebContents* source, const content::OpenURLParams& params, base::OnceCallback navigation_handle_callback) { auto weak_this = GetWeakPtr(); if (params.disposition != WindowOpenDisposition::CURRENT_TAB) { Emit("-new-window", params.url, "", params.disposition, "", params.referrer, params.post_data); return nullptr; } if (!weak_this || !web_contents()) return nullptr; content::NavigationController::LoadURLParams load_url_params(params.url); load_url_params.referrer = params.referrer; load_url_params.transition_type = params.transition; load_url_params.extra_headers = params.extra_headers; load_url_params.should_replace_current_entry = params.should_replace_current_entry; load_url_params.is_renderer_initiated = params.is_renderer_initiated; load_url_params.started_from_context_menu = params.started_from_context_menu; load_url_params.initiator_origin = params.initiator_origin; load_url_params.source_site_instance = params.source_site_instance; load_url_params.frame_tree_node_id = params.frame_tree_node_id; load_url_params.redirect_chain = params.redirect_chain; load_url_params.has_user_gesture = params.user_gesture; load_url_params.blob_url_loader_factory = params.blob_url_loader_factory; load_url_params.href_translate = params.href_translate; load_url_params.reload_type = params.reload_type; if (params.post_data) { load_url_params.load_type = content::NavigationController::LOAD_TYPE_HTTP_POST; load_url_params.post_data = params.post_data; } source->GetController().LoadURLWithParams(load_url_params); return source; } void WebContents::BeforeUnloadFired(content::WebContents* tab, bool proceed, bool* proceed_to_fire_unload) { // Note that Chromium does not emit this for navigations. // Emit returns true if preventDefault() was called, so !Emit will be true if // the event should proceed. *proceed_to_fire_unload = !Emit("-before-unload-fired", proceed); } void WebContents::SetContentsBounds(content::WebContents* source, const gfx::Rect& rect) { if (!Emit("content-bounds-updated", rect)) for (ExtendedWebContentsObserver& observer : observers_) observer.OnSetContentBounds(rect); } void WebContents::CloseContents(content::WebContents* source) { Emit("close"); auto* autofill_driver_factory = AutofillDriverFactory::FromWebContents(web_contents()); if (autofill_driver_factory) { autofill_driver_factory->CloseAllPopups(); } Destroy(); } void WebContents::ActivateContents(content::WebContents* source) { for (ExtendedWebContentsObserver& observer : observers_) observer.OnActivateContents(); } void WebContents::UpdateTargetURL(content::WebContents* source, const GURL& url) { Emit("update-target-url", url); } bool WebContents::HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { if (type_ == Type::kWebView && embedder_) { // Send the unhandled keyboard events back to the embedder. return embedder_->HandleKeyboardEvent(source, event); } else { return PlatformHandleKeyboardEvent(source, event); } } #if !BUILDFLAG(IS_MAC) // NOTE: The macOS version of this function is found in // electron_api_web_contents_mac.mm, as it requires calling into objective-C // code. bool WebContents::PlatformHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { // Check if the webContents has preferences and to ignore shortcuts auto* web_preferences = WebContentsPreferences::From(source); if (web_preferences && web_preferences->ShouldIgnoreMenuShortcuts()) return false; // Let the NativeWindow handle other parts. if (owner_window()) { owner_window()->HandleKeyboardEvent(source, event); return true; } return false; } #endif content::KeyboardEventProcessingResult WebContents::PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { if (exclusive_access_manager_.HandleUserKeyEvent(event)) return content::KeyboardEventProcessingResult::HANDLED; if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown || event.GetType() == blink::WebInputEvent::Type::kKeyUp) { // For backwards compatibility, pretend that `kRawKeyDown` events are // actually `kKeyDown`. content::NativeWebKeyboardEvent tweaked_event(event); if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown) tweaked_event.SetType(blink::WebInputEvent::Type::kKeyDown); bool prevent_default = Emit("before-input-event", tweaked_event); if (prevent_default) { return content::KeyboardEventProcessingResult::HANDLED; } } return content::KeyboardEventProcessingResult::NOT_HANDLED; } void WebContents::ContentsZoomChange(bool zoom_in) { Emit("zoom-changed", zoom_in ? "in" : "out"); } Profile* WebContents::GetProfile() { return nullptr; } bool WebContents::IsFullscreen() const { if (!owner_window()) return false; return owner_window()->IsFullscreen() || is_html_fullscreen(); } void WebContents::EnterFullscreen(const GURL& url, ExclusiveAccessBubbleType bubble_type, const int64_t display_id) {} void WebContents::ExitFullscreen() {} void WebContents::UpdateExclusiveAccessExitBubbleContent( const GURL& url, ExclusiveAccessBubbleType bubble_type, ExclusiveAccessBubbleHideCallback bubble_first_hide_callback, bool notify_download, bool force_update) {} void WebContents::OnExclusiveAccessUserInput() {} content::WebContents* WebContents::GetActiveWebContents() { return web_contents(); } bool WebContents::CanUserExitFullscreen() const { return true; } bool WebContents::IsExclusiveAccessBubbleDisplayed() const { return false; } void WebContents::EnterFullscreenModeForTab( content::RenderFrameHost* requesting_frame, const blink::mojom::FullscreenOptions& options) { auto* source = content::WebContents::FromRenderFrameHost(requesting_frame); auto* permission_helper = WebContentsPermissionHelper::FromWebContents(source); auto callback = base::BindRepeating(&WebContents::OnEnterFullscreenModeForTab, base::Unretained(this), requesting_frame, options); permission_helper->RequestFullscreenPermission(requesting_frame, callback); } void WebContents::OnEnterFullscreenModeForTab( content::RenderFrameHost* requesting_frame, const blink::mojom::FullscreenOptions& options, bool allowed) { if (!allowed || !owner_window()) return; auto* source = content::WebContents::FromRenderFrameHost(requesting_frame); if (IsFullscreenForTabOrPending(source)) { DCHECK_EQ(fullscreen_frame_, source->GetFocusedFrame()); return; } owner_window()->set_fullscreen_transition_type( NativeWindow::FullScreenTransitionType::kHTML); exclusive_access_manager_.fullscreen_controller()->EnterFullscreenModeForTab( requesting_frame, options.display_id); SetHtmlApiFullscreen(true); if (native_fullscreen_) { // Explicitly trigger a view resize, as the size is not actually changing if // the browser is fullscreened, too. source->GetRenderViewHost()->GetWidget()->SynchronizeVisualProperties(); } } void WebContents::ExitFullscreenModeForTab(content::WebContents* source) { if (!owner_window()) return; // This needs to be called before we exit fullscreen on the native window, // or the controller will incorrectly think we weren't fullscreen and bail. exclusive_access_manager_.fullscreen_controller()->ExitFullscreenModeForTab( source); SetHtmlApiFullscreen(false); if (native_fullscreen_) { // Explicitly trigger a view resize, as the size is not actually changing if // the browser is fullscreened, too. Chrome does this indirectly from // `chrome/browser/ui/exclusive_access/fullscreen_controller.cc`. source->GetRenderViewHost()->GetWidget()->SynchronizeVisualProperties(); } } void WebContents::RendererUnresponsive( content::WebContents* source, content::RenderWidgetHost* render_widget_host, base::RepeatingClosure hang_monitor_restarter) { Emit("unresponsive"); } void WebContents::RendererResponsive( content::WebContents* source, content::RenderWidgetHost* render_widget_host) { Emit("responsive"); } bool WebContents::HandleContextMenu(content::RenderFrameHost& render_frame_host, const content::ContextMenuParams& params) { Emit("context-menu", std::make_pair(params, &render_frame_host)); return true; } void WebContents::FindReply(content::WebContents* web_contents, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) { if (!final_update) return; v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); auto result = gin_helper::Dictionary::CreateEmpty(isolate); result.Set("requestId", request_id); result.Set("matches", number_of_matches); result.Set("selectionArea", selection_rect); result.Set("activeMatchOrdinal", active_match_ordinal); result.Set("finalUpdate", final_update); // Deprecate after 2.0 Emit("found-in-page", result.GetHandle()); } void WebContents::OnRequestPointerLock(content::WebContents* web_contents, bool user_gesture, bool last_unlocked_by_target, bool allowed) { if (allowed) { exclusive_access_manager_.pointer_lock_controller()->RequestToLockPointer( web_contents, user_gesture, last_unlocked_by_target); } else { web_contents->GotResponseToPointerLockRequest( blink::mojom::PointerLockResult::kPermissionDenied); } } void WebContents::RequestPointerLock(content::WebContents* web_contents, bool user_gesture, bool last_unlocked_by_target) { auto* permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); permission_helper->RequestPointerLockPermission( user_gesture, last_unlocked_by_target, base::BindOnce(&WebContents::OnRequestPointerLock, base::Unretained(this))); } void WebContents::LostPointerLock() { exclusive_access_manager_.pointer_lock_controller() ->ExitExclusiveAccessToPreviousState(); } void WebContents::OnRequestKeyboardLock(content::WebContents* web_contents, bool esc_key_locked, bool allowed) { if (allowed) { exclusive_access_manager_.keyboard_lock_controller()->RequestKeyboardLock( web_contents, esc_key_locked); } else { web_contents->GotResponseToKeyboardLockRequest(false); } } void WebContents::RequestKeyboardLock(content::WebContents* web_contents, bool esc_key_locked) { auto* permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); permission_helper->RequestKeyboardLockPermission( esc_key_locked, base::BindOnce(&WebContents::OnRequestKeyboardLock, base::Unretained(this))); } void WebContents::CancelKeyboardLockRequest( content::WebContents* web_contents) { exclusive_access_manager_.keyboard_lock_controller() ->CancelKeyboardLockRequest(web_contents); } bool WebContents::CheckMediaAccessPermission( content::RenderFrameHost* render_frame_host, const url::Origin& security_origin, blink::mojom::MediaStreamType type) { auto* web_contents = content::WebContents::FromRenderFrameHost(render_frame_host); auto* permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); return permission_helper->CheckMediaAccessPermission(security_origin, type); } void WebContents::RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback) { auto* permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); permission_helper->RequestMediaAccessPermission(request, std::move(callback)); } const void* const kJavaScriptDialogManagerKey = &kJavaScriptDialogManagerKey; content::JavaScriptDialogManager* WebContents::GetJavaScriptDialogManager( content::WebContents* source) { // Indirect these delegate methods through a helper object whose lifetime is // bound to that of the content::WebContents. This prevents the // content::WebContents from calling methods on the Electron WebContents in // the event that the Electron one is destroyed before the content one, as // happens sometimes during shutdown or when webviews are involved. class JSDialogManagerHelper : public content::JavaScriptDialogManager, public base::SupportsUserData::Data { public: void RunJavaScriptDialog(content::WebContents* web_contents, content::RenderFrameHost* rfh, content::JavaScriptDialogType dialog_type, const std::u16string& message_text, const std::u16string& default_prompt_text, DialogClosedCallback callback, bool* did_suppress_message) override { auto* wc = WebContents::From(web_contents); if (wc) wc->RunJavaScriptDialog(web_contents, rfh, dialog_type, message_text, default_prompt_text, std::move(callback), did_suppress_message); } void RunBeforeUnloadDialog(content::WebContents* web_contents, content::RenderFrameHost* rfh, bool is_reload, DialogClosedCallback callback) override { auto* wc = WebContents::From(web_contents); if (wc) wc->RunBeforeUnloadDialog(web_contents, rfh, is_reload, std::move(callback)); } void CancelDialogs(content::WebContents* web_contents, bool reset_state) override { auto* wc = WebContents::From(web_contents); if (wc) wc->CancelDialogs(web_contents, reset_state); } }; if (!source->GetUserData(kJavaScriptDialogManagerKey)) source->SetUserData(kJavaScriptDialogManagerKey, std::make_unique()); return static_cast( source->GetUserData(kJavaScriptDialogManagerKey)); } void WebContents::OnAudioStateChanged(bool audible) { v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); gin::Handle event = gin_helper::internal::Event::New(isolate); v8::Local event_object = event.ToV8().As(); gin::Dictionary dict(isolate, event_object); dict.Set("audible", audible); EmitWithoutEvent("audio-state-changed", event); } void WebContents::BeforeUnloadFired(bool proceed) { // Do nothing, we override this method just to avoid compilation error since // there are two virtual functions named BeforeUnloadFired. } void WebContents::HandleNewRenderFrame( content::RenderFrameHost* render_frame_host) { auto* rwhv = render_frame_host->GetView(); if (!rwhv) return; // Set the background color of RenderWidgetHostView. auto* web_preferences = WebContentsPreferences::From(web_contents()); if (web_preferences) SetBackgroundColor(web_preferences->GetBackgroundColor()); if (!background_throttling_) render_frame_host->GetRenderViewHost()->SetSchedulerThrottling(false); auto* rwh_impl = static_cast(rwhv->GetRenderWidgetHost()); if (rwh_impl) rwh_impl->disable_hidden_ = !background_throttling_; auto* web_frame = WebFrameMain::FromRenderFrameHost(render_frame_host); if (web_frame) web_frame->MaybeSetupMojoConnection(); } void WebContents::OnBackgroundColorChanged() { std::optional color = web_contents()->GetBackgroundColor(); if (color.has_value()) { auto* const view = web_contents()->GetRenderWidgetHostView(); static_cast(view) ->SetContentBackgroundColor(color.value()); } } void WebContents::RenderFrameCreated( content::RenderFrameHost* render_frame_host) { HandleNewRenderFrame(render_frame_host); // RenderFrameCreated is called for speculative frames which may not be // used in certain cross-origin navigations. Invoking // RenderFrameHost::GetLifecycleState currently crashes when called for // speculative frames so we need to filter it out for now. Check // https://crbug.com/1183639 for details on when this can be removed. auto* rfh_impl = static_cast(render_frame_host); if (rfh_impl->lifecycle_state() == content::RenderFrameHostImpl::LifecycleStateImpl::kSpeculative) { return; } content::RenderFrameHost::LifecycleState lifecycle_state = render_frame_host->GetLifecycleState(); if (lifecycle_state == content::RenderFrameHost::LifecycleState::kActive) { v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); auto details = gin_helper::Dictionary::CreateEmpty(isolate); details.SetGetter("frame", render_frame_host); Emit("frame-created", details); } } void WebContents::RenderFrameDeleted( content::RenderFrameHost* render_frame_host) { // A RenderFrameHost can be deleted when: // - A WebContents is removed and its containing frames are disposed. // - An