// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ #define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ #include #include #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" #include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h" #include "chrome/browser/devtools/devtools_file_helper.h" #include "chrome/browser/devtools/devtools_file_system_indexer.h" #include "chrome/browser/devtools/devtools_toggle_action.h" #include "content/public/browser/devtools_client_host.h" #include "content/public/browser/devtools_frontend_host_delegate.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_delegate.h" class Browser; class BrowserWindow; class DevToolsControllerTest; class Profile; namespace base { class Value; } namespace content { class DevToolsAgentHost; class DevToolsClientHost; struct FileChooserParams; class RenderViewHost; class WebContents; } namespace IPC { class Message; } namespace user_prefs { class PrefRegistrySyncable; } enum DevToolsDockSide { DEVTOOLS_DOCK_SIDE_UNDOCKED = 0, DEVTOOLS_DOCK_SIDE_BOTTOM, DEVTOOLS_DOCK_SIDE_RIGHT, DEVTOOLS_DOCK_SIDE_MINIMIZED }; class DevToolsWindow : private content::NotificationObserver, private content::WebContentsDelegate, private content::DevToolsFrontendHostDelegate, private DevToolsEmbedderMessageDispatcher::Delegate { public: typedef base::Callback InfoBarCallback; static const char kDevToolsApp[]; virtual ~DevToolsWindow(); static std::string GetDevToolsWindowPlacementPrefKey(); static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); // Return the DevToolsWindow for the given RenderViewHost if one exists, // otherwise NULL. static DevToolsWindow* GetInstanceForInspectedRenderViewHost( content::RenderViewHost* inspected_rvh); static DevToolsWindow* GetDockedInstanceForInspectedTab( content::WebContents* inspected_tab); static bool IsDevToolsWindow(content::RenderViewHost* window_rvh); static DevToolsWindow* OpenDevToolsWindowForWorker( Profile* profile, content::DevToolsAgentHost* worker_agent); static DevToolsWindow* CreateDevToolsWindowForWorker(Profile* profile); static DevToolsWindow* OpenDevToolsWindow( content::RenderViewHost* inspected_rvh); static DevToolsWindow* ToggleDevToolsWindow( Browser* browser, const DevToolsToggleAction& action); static void OpenExternalFrontend(Profile* profile, const std::string& frontend_uri, content::DevToolsAgentHost* agent_host); // Exposed for testing, normal clients should not use this method. static DevToolsWindow* ToggleDevToolsWindow( content::RenderViewHost* inspected_rvh, bool force_open, const DevToolsToggleAction& action); static void InspectElement( content::RenderViewHost* inspected_rvh, int x, int y); static int GetMinimumWidth(); static int GetMinimumHeight(); static int GetMinimizedHeight(); // content::DevToolsFrontendHostDelegate: virtual void InspectedContentsClosing() OVERRIDE; content::WebContents* web_contents() { return web_contents_; } Browser* browser() { return browser_; } // For tests. DevToolsDockSide dock_side() const { return dock_side_; } content::RenderViewHost* GetRenderViewHost(); content::DevToolsClientHost* GetDevToolsClientHostForTest(); // Returns preferred devtools window width for given |container_width|. It // tries to use the saved window width, or, if none exists, 1/3 of the // container width, then clamps to try and ensure both devtools and content // are at least somewhat visible. // Called only for the case when devtools window is docked to the side. int GetWidth(int container_width); // Returns preferred devtools window height for given |container_height|. // Uses the same logic as GetWidth. // Called only for the case when devtools window is docked to bottom. int GetHeight(int container_height); // Stores preferred devtools window width for this instance. void SetWidth(int width); // Stores preferred devtools window height for this instance. void SetHeight(int height); void Show(const DevToolsToggleAction& action); // BeforeUnload interception //////////////////////////////////////////////// // In order to preserve any edits the user may have made in devtools, the // beforeunload event of the inspected page is hooked - devtools gets the // first shot at handling beforeunload and presents a dialog to the user. If // the user accepts the dialog then the script is given a chance to handle // it. This way 2 dialogs may be displayed: one from the devtools asking the // user to confirm that they're ok with their devtools edits going away and // another from the webpage as the result of its beforeunload handler. // The following set of methods handle beforeunload event flow through // devtools window. When the |contents| with devtools opened on them are // getting closed, the following sequence of calls takes place: // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates // whether devtools intercept the beforeunload event. // If InterceptPageBeforeUnload() returns true then the following steps // will take place; otherwise only step 4 will be reached and none of the // corresponding functions in steps 2 & 3 will get called. // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event // for devtools frontend, which will asynchronously call // |WebContentsDelegate::BeforeUnloadFired| method. // In case of docked devtools window, devtools are set as a delegate for // its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be // called directly. // If devtools window is undocked it's not set as the delegate so the call // to BeforeUnloadFired is proxied through HandleBeforeUnload() rather // than getting called directly. // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false // it calls throught to the content's BeforeUnloadFired(), which from the // WebContents perspective looks the same as the |content|'s own // beforeunload dialog having had it's 'stay on this page' button clicked. // 3b. If |proceed| = true, then it fires beforeunload event on |contents| // and everything proceeds as it normally would without the Devtools // interception. // 4. If the user cancels the dialog put up by either the WebContents or // devtools frontend, then |contents|'s |BeforeUnloadFired| callback is // called with the proceed argument set to false, this causes // |DevToolsWindow::OnPageCloseCancelled| to be called. // Devtools window in undocked state is not set as a delegate of // its frontend. Instead, an instance of browser is set as the delegate, and // thus beforeunload event callback from devtools frontend is not delivered // to the instance of devtools window, which is solely responsible for // managing custom beforeunload event flow. // This is a helper method to route callback from // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|. // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog, // false otherwise. // * |proceed_to_fire_unload| - output parameter, whether we should continue // to fire the unload event or stop things here. // Returns true if devtools window is in a state of intercepting beforeunload // event and if it will manage unload process on its own. static bool HandleBeforeUnload(content::WebContents* contents, bool proceed, bool* proceed_to_fire_unload); // Returns true if this contents beforeunload event was intercepted by // devtools and false otherwise. If the event was intercepted, caller should // not fire beforeunlaod event on |contents| itself as devtools window will // take care of it, otherwise caller should continue handling the event as // usual. static bool InterceptPageBeforeUnload(content::WebContents* contents); // Returns true if devtools browser has already fired its beforeunload event // as a result of beforeunload event interception. static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser* browser); // Returns true if devtools window would like to hook beforeunload event // of this |contents|. static bool NeedsToInterceptBeforeUnload(content::WebContents* contents); // Notify devtools window that closing of |contents| was cancelled // by user. static void OnPageCloseCanceled(content::WebContents* contents); void SetDockSideForTest(DevToolsDockSide dock_side); private: friend class DevToolsControllerTest; DevToolsWindow(Profile* profile, const GURL& frontend_url, content::RenderViewHost* inspected_rvh, DevToolsDockSide dock_side); static DevToolsWindow* Create(Profile* profile, const GURL& frontend_url, content::RenderViewHost* inspected_rvh, DevToolsDockSide dock_side, bool shared_worker_frontend, bool external_frontend); static GURL GetDevToolsURL(Profile* profile, const GURL& base_url, DevToolsDockSide dock_side, bool shared_worker_frontend, bool external_frontend); static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*); static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*); static DevToolsDockSide GetDockSideFromPrefs(Profile* profile); static std::string SideToString(DevToolsDockSide dock_side); static DevToolsDockSide SideFromString(const std::string& dock_side); // content::NotificationObserver: virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE; // content::WebContentsDelegate: virtual content::WebContents* OpenURLFromTab( content::WebContents* source, const content::OpenURLParams& params) OVERRIDE; virtual void AddNewContents(content::WebContents* source, content::WebContents* new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture, bool* was_blocked) OVERRIDE; virtual void CloseContents(content::WebContents* source) OVERRIDE; virtual void BeforeUnloadFired(content::WebContents* tab, bool proceed, bool* proceed_to_fire_unload) OVERRIDE; virtual bool PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) OVERRIDE; virtual void HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) OVERRIDE; virtual content::JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE; virtual content::ColorChooser* OpenColorChooser( content::WebContents* web_contents, SkColor color, const std::vector& suggestions) OVERRIDE; virtual void RunFileChooser( content::WebContents* web_contents, const content::FileChooserParams& params) OVERRIDE; virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE; // content::DevToolsFrontendHostDelegate override: virtual void DispatchOnEmbedder(const std::string& message) OVERRIDE; // DevToolsEmbedderMessageDispatcher::Delegate overrides: virtual void ActivateWindow() OVERRIDE; virtual void ActivateContents(content::WebContents* contents) OVERRIDE; virtual void CloseWindow() OVERRIDE; virtual void SetWindowBounds(int x, int y, int width, int height) OVERRIDE; virtual void MoveWindow(int x, int y) OVERRIDE; virtual void SetDockSide(const std::string& side) OVERRIDE; virtual void OpenInNewTab(const std::string& url) OVERRIDE; virtual void SaveToFile(const std::string& url, const std::string& content, bool save_as) OVERRIDE; virtual void AppendToFile(const std::string& url, const std::string& content) OVERRIDE; virtual void RequestFileSystems() OVERRIDE; virtual void AddFileSystem() OVERRIDE; virtual void RemoveFileSystem(const std::string& file_system_path) OVERRIDE; virtual void UpgradeDraggedFileSystemPermissions( const std::string& file_system_url) OVERRIDE; virtual void IndexPath(int request_id, const std::string& file_system_path) OVERRIDE; virtual void StopIndexing(int request_id) OVERRIDE; virtual void SearchInPath(int request_id, const std::string& file_system_path, const std::string& query) OVERRIDE; // DevToolsFileHelper callbacks. void FileSavedAs(const std::string& url); void CanceledFileSaveAs(const std::string& url); void AppendedTo(const std::string& url); void FileSystemsLoaded( const std::vector& file_systems); void FileSystemAdded(const DevToolsFileHelper::FileSystem& file_system); void IndexingTotalWorkCalculated(int request_id, const std::string& file_system_path, int total_work); void IndexingWorked(int request_id, const std::string& file_system_path, int worked); void IndexingDone(int request_id, const std::string& file_system_path); void SearchCompleted(int request_id, const std::string& file_system_path, const std::vector& file_paths); void ShowDevToolsConfirmInfoBar(const base::string16& message, const InfoBarCallback& callback); void CreateDevToolsBrowser(); bool FindInspectedBrowserAndTabIndex(Browser**, int* tab); BrowserWindow* GetInspectedBrowserWindow(); bool IsInspectedBrowserPopup(); void UpdateFrontendDockSide(); void ScheduleAction(const DevToolsToggleAction& action); void DoAction(); void UpdateTheme(); void AddDevToolsExtensionsToClient(); void CallClientFunction(const std::string& function_name, const base::Value* arg1, const base::Value* arg2, const base::Value* arg3); void UpdateBrowserToolbar(); bool IsDocked(); void Restore(); content::WebContents* GetInspectedWebContents(); void DocumentOnLoadCompletedInMainFrame(); class InspectedWebContentsObserver; scoped_ptr inspected_contents_observer_; class FrontendWebContentsObserver; friend class FrontendWebContentsObserver; scoped_ptr frontend_contents_observer_; Profile* profile_; content::WebContents* web_contents_; Browser* browser_; DevToolsDockSide dock_side_; bool is_loaded_; DevToolsToggleAction action_on_load_; content::NotificationRegistrar registrar_; scoped_ptr frontend_host_; scoped_ptr file_helper_; scoped_refptr file_system_indexer_; typedef std::map< int, scoped_refptr > IndexingJobsMap; IndexingJobsMap indexing_jobs_; int width_; int height_; DevToolsDockSide dock_side_before_minimized_; // True if we're in the process of handling a beforeunload event originating // from the inspected webcontents, see InterceptPageBeforeUnload for details. bool intercepted_page_beforeunload_; scoped_ptr embedder_message_dispatcher_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(DevToolsWindow); }; #endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_