1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ 6 #define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ 7 8 #include "chrome/browser/devtools/devtools_contents_resizing_strategy.h" 9 #include "chrome/browser/devtools/devtools_toggle_action.h" 10 #include "chrome/browser/devtools/devtools_ui_bindings.h" 11 #include "content/public/browser/web_contents_delegate.h" 12 13 class Browser; 14 class BrowserWindow; 15 class DevToolsControllerTest; 16 class DevToolsEventForwarder; 17 18 namespace content { 19 class DevToolsAgentHost; 20 struct NativeWebKeyboardEvent; 21 class RenderViewHost; 22 } 23 24 namespace user_prefs { 25 class PrefRegistrySyncable; 26 } 27 28 class DevToolsWindow : public DevToolsUIBindings::Delegate, 29 public content::WebContentsDelegate { 30 public: 31 static const char kDevToolsApp[]; 32 33 virtual ~DevToolsWindow(); 34 35 static std::string GetDevToolsWindowPlacementPrefKey(); 36 static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); 37 38 // Return the DevToolsWindow for the given RenderViewHost if one exists, 39 // otherwise NULL. 40 static DevToolsWindow* GetInstanceForInspectedRenderViewHost( 41 content::RenderViewHost* inspected_rvh); 42 43 // Return the DevToolsWindow for the given WebContents if one exists, 44 // otherwise NULL. 45 static DevToolsWindow* GetInstanceForInspectedWebContents( 46 content::WebContents* inspected_web_contents); 47 48 // Return the docked DevTools WebContents for the given inspected WebContents 49 // if one exists and should be shown in browser window, otherwise NULL. 50 // This method will return only fully initialized window ready to be 51 // presented in UI. 52 // If |out_strategy| is not NULL, it will contain resizing strategy. 53 // For immediately-ready-to-use but maybe not yet fully initialized DevTools 54 // use |GetInstanceForInspectedRenderViewHost| instead. 55 static content::WebContents* GetInTabWebContents( 56 content::WebContents* inspected_tab, 57 DevToolsContentsResizingStrategy* out_strategy); 58 59 static bool IsDevToolsWindow(content::RenderViewHost* window_rvh); 60 61 // Open or reveal DevTools window, and perform the specified action. 62 static DevToolsWindow* OpenDevToolsWindow( 63 content::RenderViewHost* inspected_rvh, 64 const DevToolsToggleAction& action); 65 66 // Open or reveal DevTools window, with no special action. 67 static DevToolsWindow* OpenDevToolsWindow( 68 content::RenderViewHost* inspected_rvh); 69 70 static DevToolsWindow* OpenDevToolsWindowForTest( 71 content::RenderViewHost* inspected_rvh, bool is_docked); 72 static DevToolsWindow* OpenDevToolsWindowForTest( 73 Browser* browser, bool is_docked); 74 75 // Perform specified action for current WebContents inside a |browser|. 76 // This may close currently open DevTools window. 77 static DevToolsWindow* ToggleDevToolsWindow( 78 Browser* browser, 79 const DevToolsToggleAction& action); 80 81 // External frontend is always undocked. 82 static void OpenExternalFrontend( 83 Profile* profile, 84 const std::string& frontend_uri, 85 content::DevToolsAgentHost* agent_host); 86 87 // Worker frontend is always undocked. 88 static DevToolsWindow* OpenDevToolsWindowForWorker( 89 Profile* profile, 90 content::DevToolsAgentHost* worker_agent); 91 92 static void InspectElement( 93 content::RenderViewHost* inspected_rvh, int x, int y); 94 95 Browser* browser_for_test() { return browser_; } 96 content::WebContents* web_contents_for_test() { return main_web_contents_; } 97 98 // Sets closure to be called after load is done. If already loaded, calls 99 // closure immediately. 100 void SetLoadCompletedCallback(const base::Closure& closure); 101 102 // Forwards an unhandled keyboard event to the DevTools frontend. 103 bool ForwardKeyboardEvent(const content::NativeWebKeyboardEvent& event); 104 105 // BeforeUnload interception //////////////////////////////////////////////// 106 107 // In order to preserve any edits the user may have made in devtools, the 108 // beforeunload event of the inspected page is hooked - devtools gets the 109 // first shot at handling beforeunload and presents a dialog to the user. If 110 // the user accepts the dialog then the script is given a chance to handle 111 // it. This way 2 dialogs may be displayed: one from the devtools asking the 112 // user to confirm that they're ok with their devtools edits going away and 113 // another from the webpage as the result of its beforeunload handler. 114 // The following set of methods handle beforeunload event flow through 115 // devtools window. When the |contents| with devtools opened on them are 116 // getting closed, the following sequence of calls takes place: 117 // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates 118 // whether devtools intercept the beforeunload event. 119 // If InterceptPageBeforeUnload() returns true then the following steps 120 // will take place; otherwise only step 4 will be reached and none of the 121 // corresponding functions in steps 2 & 3 will get called. 122 // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event 123 // for devtools frontend, which will asynchronously call 124 // |WebContentsDelegate::BeforeUnloadFired| method. 125 // In case of docked devtools window, devtools are set as a delegate for 126 // its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be 127 // called directly. 128 // If devtools window is undocked it's not set as the delegate so the call 129 // to BeforeUnloadFired is proxied through HandleBeforeUnload() rather 130 // than getting called directly. 131 // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false 132 // it calls throught to the content's BeforeUnloadFired(), which from the 133 // WebContents perspective looks the same as the |content|'s own 134 // beforeunload dialog having had it's 'stay on this page' button clicked. 135 // 3b. If |proceed| = true, then it fires beforeunload event on |contents| 136 // and everything proceeds as it normally would without the Devtools 137 // interception. 138 // 4. If the user cancels the dialog put up by either the WebContents or 139 // devtools frontend, then |contents|'s |BeforeUnloadFired| callback is 140 // called with the proceed argument set to false, this causes 141 // |DevToolsWindow::OnPageCloseCancelled| to be called. 142 143 // Devtools window in undocked state is not set as a delegate of 144 // its frontend. Instead, an instance of browser is set as the delegate, and 145 // thus beforeunload event callback from devtools frontend is not delivered 146 // to the instance of devtools window, which is solely responsible for 147 // managing custom beforeunload event flow. 148 // This is a helper method to route callback from 149 // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|. 150 // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog, 151 // false otherwise. 152 // * |proceed_to_fire_unload| - output parameter, whether we should continue 153 // to fire the unload event or stop things here. 154 // Returns true if devtools window is in a state of intercepting beforeunload 155 // event and if it will manage unload process on its own. 156 static bool HandleBeforeUnload(content::WebContents* contents, 157 bool proceed, 158 bool* proceed_to_fire_unload); 159 160 // Returns true if this contents beforeunload event was intercepted by 161 // devtools and false otherwise. If the event was intercepted, caller should 162 // not fire beforeunlaod event on |contents| itself as devtools window will 163 // take care of it, otherwise caller should continue handling the event as 164 // usual. 165 static bool InterceptPageBeforeUnload(content::WebContents* contents); 166 167 // Returns true if devtools browser has already fired its beforeunload event 168 // as a result of beforeunload event interception. 169 static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser* browser); 170 171 // Returns true if devtools window would like to hook beforeunload event 172 // of this |contents|. 173 static bool NeedsToInterceptBeforeUnload(content::WebContents* contents); 174 175 // Notify devtools window that closing of |contents| was cancelled 176 // by user. 177 static void OnPageCloseCanceled(content::WebContents* contents); 178 179 private: 180 friend class DevToolsControllerTest; 181 friend class DevToolsSanityTest; 182 friend class BrowserWindowControllerTest; 183 184 // DevTools initialization typically follows this way: 185 // - Toggle/Open: client call; 186 // - Create; 187 // - ScheduleShow: setup window to be functional, but not yet show; 188 // - DocumentOnLoadCompletedInMainFrame: frontend loaded; 189 // - SetIsDocked: frontend decided on docking state; 190 // - OnLoadCompleted: ready to present frontend; 191 // - Show: actually placing frontend WebContents to a Browser or docked place; 192 // - DoAction: perform action passed in Toggle/Open. 193 enum LoadState { 194 kNotLoaded, 195 kOnLoadFired, // Implies SetIsDocked was not yet called. 196 kIsDockedSet, // Implies DocumentOnLoadCompleted was not yet called. 197 kLoadCompleted 198 }; 199 200 DevToolsWindow(Profile* profile, 201 const GURL& frontend_url, 202 content::RenderViewHost* inspected_rvh, 203 bool can_dock); 204 205 static DevToolsWindow* Create(Profile* profile, 206 const GURL& frontend_url, 207 content::RenderViewHost* inspected_rvh, 208 bool shared_worker_frontend, 209 bool external_frontend, 210 bool can_dock); 211 static GURL GetDevToolsURL(Profile* profile, 212 const GURL& base_url, 213 bool shared_worker_frontend, 214 bool external_frontend, 215 bool can_dock); 216 static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*); 217 static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*); 218 static DevToolsWindow* CreateDevToolsWindowForWorker(Profile* profile); 219 static bool FindInspectedBrowserAndTabIndex( 220 content::WebContents* inspected_web_contents, Browser**, int* tab); 221 static DevToolsWindow* ToggleDevToolsWindow( 222 content::RenderViewHost* inspected_rvh, 223 bool force_open, 224 const DevToolsToggleAction& action); 225 226 // content::WebContentsDelegate: 227 virtual content::WebContents* OpenURLFromTab( 228 content::WebContents* source, 229 const content::OpenURLParams& params) OVERRIDE; 230 virtual void ActivateContents(content::WebContents* contents) OVERRIDE; 231 virtual void AddNewContents(content::WebContents* source, 232 content::WebContents* new_contents, 233 WindowOpenDisposition disposition, 234 const gfx::Rect& initial_pos, 235 bool user_gesture, 236 bool* was_blocked) OVERRIDE; 237 virtual void WebContentsCreated(content::WebContents* source_contents, 238 int opener_render_frame_id, 239 const base::string16& frame_name, 240 const GURL& target_url, 241 content::WebContents* new_contents) OVERRIDE; 242 virtual void CloseContents(content::WebContents* source) OVERRIDE; 243 virtual void ContentsZoomChange(bool zoom_in) OVERRIDE; 244 virtual void BeforeUnloadFired(content::WebContents* tab, 245 bool proceed, 246 bool* proceed_to_fire_unload) OVERRIDE; 247 virtual bool PreHandleKeyboardEvent( 248 content::WebContents* source, 249 const content::NativeWebKeyboardEvent& event, 250 bool* is_keyboard_shortcut) OVERRIDE; 251 virtual void HandleKeyboardEvent( 252 content::WebContents* source, 253 const content::NativeWebKeyboardEvent& event) OVERRIDE; 254 virtual content::JavaScriptDialogManager* 255 GetJavaScriptDialogManager() OVERRIDE; 256 virtual content::ColorChooser* OpenColorChooser( 257 content::WebContents* web_contents, 258 SkColor color, 259 const std::vector<content::ColorSuggestion>& suggestions) OVERRIDE; 260 virtual void RunFileChooser( 261 content::WebContents* web_contents, 262 const content::FileChooserParams& params) OVERRIDE; 263 virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE; 264 virtual bool PreHandleGestureEvent( 265 content::WebContents* source, 266 const blink::WebGestureEvent& event) OVERRIDE; 267 268 // content::DevToolsUIBindings::Delegate overrides 269 virtual void ActivateWindow() OVERRIDE; 270 virtual void CloseWindow() OVERRIDE; 271 virtual void SetInspectedPageBounds(const gfx::Rect& rect) OVERRIDE; 272 virtual void SetContentsResizingStrategy( 273 const gfx::Insets& insets, const gfx::Size& min_size) OVERRIDE; 274 virtual void InspectElementCompleted() OVERRIDE; 275 virtual void MoveWindow(int x, int y) OVERRIDE; 276 virtual void SetIsDocked(bool is_docked) OVERRIDE; 277 virtual void OpenInNewTab(const std::string& url) OVERRIDE; 278 virtual void SetWhitelistedShortcuts(const std::string& message) OVERRIDE; 279 virtual void InspectedContentsClosing() OVERRIDE; 280 virtual void OnLoadCompleted() OVERRIDE; 281 virtual InfoBarService* GetInfoBarService() OVERRIDE; 282 virtual void RenderProcessGone() OVERRIDE; 283 284 void CreateDevToolsBrowser(); 285 BrowserWindow* GetInspectedBrowserWindow(); 286 void ScheduleShow(const DevToolsToggleAction& action); 287 void Show(const DevToolsToggleAction& action); 288 void DoAction(const DevToolsToggleAction& action); 289 void LoadCompleted(); 290 void SetIsDockedAndShowImmediatelyForTest(bool is_docked); 291 void UpdateBrowserToolbar(); 292 void UpdateBrowserWindow(); 293 content::WebContents* GetInspectedWebContents(); 294 295 class InspectedWebContentsObserver; 296 scoped_ptr<InspectedWebContentsObserver> inspected_contents_observer_; 297 298 Profile* profile_; 299 content::WebContents* main_web_contents_; 300 content::WebContents* toolbox_web_contents_; 301 DevToolsUIBindings* bindings_; 302 Browser* browser_; 303 bool is_docked_; 304 const bool can_dock_; 305 LoadState load_state_; 306 DevToolsToggleAction action_on_load_; 307 bool ignore_set_is_docked_; 308 DevToolsContentsResizingStrategy contents_resizing_strategy_; 309 // True if we're in the process of handling a beforeunload event originating 310 // from the inspected webcontents, see InterceptPageBeforeUnload for details. 311 bool intercepted_page_beforeunload_; 312 base::Closure load_completed_callback_; 313 314 base::TimeTicks inspect_element_start_time_; 315 scoped_ptr<DevToolsEventForwarder> event_forwarder_; 316 317 friend class DevToolsEventForwarder; 318 DISALLOW_COPY_AND_ASSIGN(DevToolsWindow); 319 }; 320 321 #endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ 322