#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
#include <memory>
#include <string>
#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
#include "chrome/browser/devtools/devtools_toggle_action.h"
#include "chrome/browser/devtools/devtools_ui_bindings.h"
#include "components/policy/core/common/policy_service.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/devtools_manager_delegate.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_list_observer.h"
#endif
class Browser;
class BrowserWindowInterface;
class BrowserList;
class BrowserWindow;
class DevToolsWindowTesting;
class DevToolsEventForwarder;
class DevToolsEyeDropper;
namespace content {
class DevToolsAgentHost;
struct NativeWebKeyboardEvent;
class NavigationHandle;
class NavigationThrottleRegistry;
class RenderFrameHost;
}
namespace user_prefs {
class PrefRegistrySyncable;
}
enum class DevToolsOpenedByAction {
kUnknown = 0,
kMainMenuOrMainShortcut = 1,
kConsoleShortcut = 2,
kContextMenuInspect = 3,
kInspectorModeShortcut = 4,
kToggleShortcut = 5,
kInspectLink = 6,
kAutomaticForNewTarget = 7,
kTargetReload = 8,
kOpenForNodeFromAnotherTarget = 9,
kPinnedToolbarButton = 10,
kMaxValue = kPinnedToolbarButton,
};
enum class DevToolsClosedByAction {
kUnknown = 0,
kMainMenuOrMainShortcut = 1,
kToggleShortcut = 2,
kCloseButton = 3,
kTargetDetach = 4,
kPinnedToolbarButton = 5,
kMaxValue = kPinnedToolbarButton,
};
class DevToolsWindow : public DevToolsUIBindings::Delegate,
public content::WebContentsDelegate,
public content::WebContentsObserver,
#if !BUILDFLAG(IS_ANDROID)
public BrowserListObserver,
#endif
public infobars::InfoBarManager::Observer,
public policy::PolicyService::Observer {
public:
static const char kDevToolsApp[];
DevToolsWindow(const DevToolsWindow&) = delete;
DevToolsWindow& operator=(const DevToolsWindow&) = delete;
~DevToolsWindow() override;
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
static bool AllowDevToolsFor(Profile* profile,
content::WebContents* web_contents);
static DevToolsWindow* GetInstanceForInspectedWebContents(
content::WebContents* inspected_web_contents);
static content::WebContents* GetInTabWebContents(
content::WebContents* inspected_tab,
DevToolsContentsResizingStrategy* out_strategy);
static bool IsDevToolsWindow(content::WebContents* web_contents);
static DevToolsWindow* AsDevToolsWindow(content::WebContents* web_contents);
static DevToolsWindow* AsDevToolsWindow(BrowserWindowInterface* browser);
static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*);
static void OpenDevToolsWindow(content::WebContents* inspected_web_contents,
const DevToolsToggleAction& action,
DevToolsOpenedByAction opened_by);
static void OpenDevToolsWindow(content::WebContents* inspected_web_contents,
DevToolsOpenedByAction opened_by);
static void OpenDevToolsWindow(
content::WebContents* inspected_web_contents,
Profile* profile,
DevToolsOpenedByAction opened_by,
const content::DevToolsManagerDelegate::DevToolsOptions&
devtools_options);
static void OpenDevToolsWindow(
scoped_refptr<content::DevToolsAgentHost> host,
Profile* profile,
DevToolsOpenedByAction opened_by,
const content::DevToolsManagerDelegate::DevToolsOptions&
devtools_options =
content::DevToolsManagerDelegate::DevToolsOptions());
static void OpenDevToolsWindowWithBundledFrontend(
scoped_refptr<content::DevToolsAgentHost> host,
Profile* profile,
DevToolsOpenedByAction opened_by);
static void ToggleDevToolsWindow(
BrowserWindowInterface* browser,
const DevToolsToggleAction& action,
DevToolsOpenedByAction opened_by = DevToolsOpenedByAction::kUnknown);
static DevToolsWindow* OpenNodeFrontendWindow(
Profile* profile,
DevToolsOpenedByAction opened_by);
static void InspectElement(content::RenderFrameHost* inspected_frame_host,
int x,
int y);
static void LogDevToolsOpenedByAction(DevToolsOpenedByAction opened_by);
static void LogDevToolsOpenedUKM(content::WebContents* web_contents);
static void MaybeCreateAndAddNavigationThrottle(
content::NavigationThrottleRegistry& registry);
void SetLoadCompletedCallback(base::OnceClosure closure);
bool ForwardKeyboardEvent(const input::NativeWebKeyboardEvent& event);
bool ReloadInspectedWebContents(bool bypass_cache);
content::WebContents* OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params,
base::OnceCallback<void(content::NavigationHandle&)>
navigation_handle_callback) override;
content::WebContents* OpenURLFromInspectedTab(
const content::OpenURLParams& params);
static bool HandleBeforeUnload(content::WebContents* contents,
bool proceed,
bool* proceed_to_fire_unload);
static bool InterceptPageBeforeUnload(content::WebContents* contents);
static bool HasFiredBeforeUnloadEventForDevToolsBrowser(
BrowserWindowInterface* browser);
static bool NeedsToInterceptBeforeUnload(content::WebContents* contents);
static void OnPageCloseCanceled(content::WebContents* contents);
content::WebContents* GetInspectedWebContents() override;
void ActivateWindow() override;
void MainWebContentRenderFrameHostChanged(
content::RenderFrameHost* old_frame,
content::RenderFrameHost* new_frame);
raw_ptr<content::WebContents> GetDevToolsWebContents();
bool IsDocked() { return is_docked_; }
private:
friend class DevToolsWindowTesting;
friend class DevToolsWindowCreationObserver;
friend class HatsNextWebDialogBrowserTest;
using CreationCallback = base::RepeatingCallback<void(DevToolsWindow*)>;
static void AddCreationCallbackForTest(const CreationCallback& callback);
static void RemoveCreationCallbackForTest(const CreationCallback& callback);
static void OpenDevToolsWindowForFrame(
Profile* profile,
const scoped_refptr<content::DevToolsAgentHost>& agent_host,
DevToolsOpenedByAction opened_by);
static void OpenDevToolsWindowForWorker(
Profile* profile,
const scoped_refptr<content::DevToolsAgentHost>& worker_agent,
DevToolsOpenedByAction opened_by);
enum LifeStage {
kNotLoaded,
kOnLoadFired,
kIsDockedSet,
kLoadCompleted,
kClosing
};
enum FrontendType {
kFrontendDefault,
kFrontendWorker,
kFrontendV8,
kFrontendNode,
kFrontendRemote,
kFrontendRemoteWorker,
kFrontendRemoteTab,
};
DevToolsWindow(FrontendType frontend_type,
Profile* profile,
std::unique_ptr<content::WebContents> main_web_contents,
DevToolsUIBindings* bindings,
content::WebContents* inspected_web_contents,
bool can_dock,
DevToolsOpenedByAction opened_by);
static void OpenExternalFrontend(
Profile* profile,
const std::string& frontend_uri,
const scoped_refptr<content::DevToolsAgentHost>& agent_host,
bool use_bundled_frontend,
DevToolsOpenedByAction opened_by);
static void OpenDevToolsWindow(
scoped_refptr<content::DevToolsAgentHost> host,
Profile* profile,
bool use_bundled_frontend,
DevToolsOpenedByAction opened_by,
const content::DevToolsManagerDelegate::DevToolsOptions&
devtools_options =
content::DevToolsManagerDelegate::DevToolsOptions());
static DevToolsWindow* Create(Profile* profile,
content::WebContents* inspected_web_contents,
FrontendType frontend_type,
const std::string& frontend_url,
bool can_dock,
const std::string& settings,
const std::string& panel,
bool has_other_clients,
bool browser_connection,
DevToolsOpenedByAction opened_by);
static GURL GetDevToolsURL(Profile* profile,
FrontendType frontend_type,
const std::string& frontend_url,
bool can_dock,
const std::string& panel,
bool has_other_clients,
bool browser_connection);
static void ToggleDevToolsWindow(
content::WebContents* web_contents,
Profile* profile,
bool force_open,
const DevToolsToggleAction& action,
const std::string& settings,
DevToolsOpenedByAction opened_by = DevToolsOpenedByAction::kUnknown,
const content::DevToolsManagerDelegate::DevToolsOptions&
devtools_options =
content::DevToolsManagerDelegate::DevToolsOptions());
static Profile* GetProfileForDevToolsWindow(
content::WebContents* web_contents);
void ActivateContents(content::WebContents* contents) override;
content::WebContents* AddNewContents(
content::WebContents* source,
std::unique_ptr<content::WebContents> new_contents,
const GURL& target_url,
WindowOpenDisposition disposition,
const blink::mojom::WindowFeatures& window_features,
bool user_gesture,
bool* was_blocked) override;
void WebContentsCreated(content::WebContents* source_contents,
int opener_render_process_id,
int opener_render_frame_id,
const std::string& frame_name,
const GURL& target_url,
content::WebContents* new_contents) override;
void CloseContents(content::WebContents* source) override;
void ContentsZoomChange(bool zoom_in) override;
void BeforeUnloadFired(content::WebContents* tab,
bool proceed,
bool* proceed_to_fire_unload) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source,
const input::NativeWebKeyboardEvent& event) override;
bool HandleKeyboardEvent(content::WebContents* source,
const input::NativeWebKeyboardEvent& event) override;
content::JavaScriptDialogManager* GetJavaScriptDialogManager(
content::WebContents* source) override;
std::unique_ptr<content::EyeDropper> OpenEyeDropper(
content::RenderFrameHost* render_frame_host,
content::EyeDropperListener* listener) override;
void RunFileChooser(content::RenderFrameHost* render_frame_host,
scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) override;
bool PreHandleGestureEvent(content::WebContents* source,
const blink::WebGestureEvent& event) override;
void Close(DevToolsClosedByAction closed_by);
void CloseWindow() override;
void Inspect(scoped_refptr<content::DevToolsAgentHost> host) override;
void SetInspectedPageBounds(const gfx::Rect& rect) override;
void InspectElementCompleted() override;
void SetIsDocked(bool is_docked) override;
void OpenInNewTab(const std::string& url) override;
void OpenSearchResultsInNewTab(const std::string& url) override;
void SetWhitelistedShortcuts(const std::string& message) override;
void SetEyeDropperActive(bool active) override;
void OpenNodeFrontend() override;
void InspectedContentsClosing() override;
void OnLoadCompleted() override;
void ReadyForTest() override;
void ConnectionReady() override;
void SetOpenNewWindowForPopups(bool value) override;
infobars::ContentInfoBarManager* GetInfoBarManager() override;
void RenderProcessGone(bool crashed) override;
void ShowCertificateViewer(const std::string& cert_viewer) override;
int GetDockStateForLogging() override;
int GetOpenedByForLogging() override;
int GetClosedByForLogging() override;
void OpenInNewTab(const GURL& url);
void ColorPickedInEyeDropper(int r, int g, int b, int a);
using content::WebContentsObserver::BeforeUnloadFired;
void PrimaryPageChanged(content::Page& page) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
#if !BUILDFLAG(IS_ANDROID)
void OnBrowserRemoved(Browser* browser) override;
#endif
void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override;
void CreateDevToolsBrowser();
BrowserWindow* GetInspectedBrowserWindow();
void ScheduleShow(const DevToolsToggleAction& action);
void Show(const DevToolsToggleAction& action);
void DoAction(const DevToolsToggleAction& action);
void LoadCompleted();
void UpdateBrowserToolbar();
void UpdateBrowserWindow();
void RegisterModalDialogManager(BrowserWindowInterface* browser);
void OnLocaleChanged();
void OverrideAndSyncDevToolsRendererPrefs();
void OnDevToolsPolicyChanged();
void OnPolicyUpdated(const policy::PolicyNamespace& ns,
const policy::PolicyMap& previous,
const policy::PolicyMap& current) override;
void MaybeShowSharedProcessInfobar();
#if !BUILDFLAG(IS_ANDROID)
void ActivateInspectedTab();
#endif
FrontendType frontend_type_;
raw_ptr<Profile> profile_;
raw_ptr<content::WebContents> main_web_contents_;
class MainWebContentsObserver : public content::WebContentsObserver {
public:
MainWebContentsObserver(content::WebContents& web_contents,
DevToolsWindow& window)
: WebContentsObserver(&web_contents), window_(window) {}
~MainWebContentsObserver() override;
private:
void RenderFrameHostChanged(content::RenderFrameHost* old_frame,
content::RenderFrameHost* new_frame) override;
raw_ref<DevToolsWindow> window_;
};
MainWebContentsObserver main_web_contents_observer_;
raw_ptr<content::WebContents, DanglingUntriaged> toolbox_web_contents_;
std::unique_ptr<content::WebContents> owned_toolbox_web_contents_;
raw_ptr<DevToolsUIBindings> bindings_;
raw_ptr<BrowserWindowInterface> browser_;
bool is_docked_;
class OwnedMainWebContents;
std::unique_ptr<OwnedMainWebContents> owned_main_web_contents_;
const bool can_dock_;
bool close_on_detach_;
LifeStage life_stage_;
DevToolsToggleAction action_on_load_;
DevToolsContentsResizingStrategy contents_resizing_strategy_;
bool intercepted_page_beforeunload_;
base::OnceClosure load_completed_callback_;
base::OnceClosure close_callback_;
bool ready_for_test_;
base::OnceClosure ready_for_test_callback_;
base::TimeTicks inspect_element_start_time_;
std::unique_ptr<DevToolsEventForwarder> event_forwarder_;
std::unique_ptr<DevToolsEyeDropper> eye_dropper_;
const DevToolsOpenedByAction opened_by_;
DevToolsClosedByAction closed_by_;
const base::UnguessableToken session_id_for_logging_;
class Throttle;
raw_ptr<Throttle> throttle_ = nullptr;
bool open_new_window_for_popups_ = false;
raw_ptr<infobars::InfoBar> sharing_infobar_ = nullptr;
int checked_sharing_process_id_ = content::ChildProcessHost::kInvalidUniqueID;
#if !BUILDFLAG(IS_ANDROID)
base::ScopedObservation<BrowserList, BrowserListObserver>
browser_list_observation_{this};
#endif
PrefChangeRegistrar pref_change_registrar_;
base::ScopedClosureRunner capture_handle_;
base::CallbackListSubscription policy_checker_callback_subscription_;
friend class DevToolsEventForwarder;
base::WeakPtrFactory<DevToolsWindow> weak_factory_{this};
};
#endif