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 <string> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/memory/weak_ptr.h" 14 #include "base/strings/string16.h" 15 #include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h" 16 #include "chrome/browser/devtools/devtools_file_helper.h" 17 #include "chrome/browser/devtools/devtools_file_system_indexer.h" 18 #include "chrome/browser/devtools/devtools_toggle_action.h" 19 #include "content/public/browser/devtools_client_host.h" 20 #include "content/public/browser/devtools_frontend_host_delegate.h" 21 #include "content/public/browser/notification_observer.h" 22 #include "content/public/browser/notification_registrar.h" 23 #include "content/public/browser/web_contents_delegate.h" 24 25 class Browser; 26 class BrowserWindow; 27 class DevToolsControllerTest; 28 class Profile; 29 30 namespace base { 31 class Value; 32 } 33 34 namespace content { 35 class DevToolsAgentHost; 36 class DevToolsClientHost; 37 struct FileChooserParams; 38 class RenderViewHost; 39 class WebContents; 40 } 41 42 namespace IPC { 43 class Message; 44 } 45 46 namespace user_prefs { 47 class PrefRegistrySyncable; 48 } 49 50 enum DevToolsDockSide { 51 DEVTOOLS_DOCK_SIDE_UNDOCKED = 0, 52 DEVTOOLS_DOCK_SIDE_BOTTOM, 53 DEVTOOLS_DOCK_SIDE_RIGHT, 54 DEVTOOLS_DOCK_SIDE_MINIMIZED 55 }; 56 57 class DevToolsWindow : private content::NotificationObserver, 58 private content::WebContentsDelegate, 59 private content::DevToolsFrontendHostDelegate, 60 private DevToolsEmbedderMessageDispatcher::Delegate { 61 public: 62 typedef base::Callback<void(bool)> InfoBarCallback; 63 64 static const char kDevToolsApp[]; 65 66 virtual ~DevToolsWindow(); 67 68 static std::string GetDevToolsWindowPlacementPrefKey(); 69 static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); 70 // Return the DevToolsWindow for the given RenderViewHost if one exists, 71 // otherwise NULL. 72 static DevToolsWindow* GetInstanceForInspectedRenderViewHost( 73 content::RenderViewHost* inspected_rvh); 74 static DevToolsWindow* GetDockedInstanceForInspectedTab( 75 content::WebContents* inspected_tab); 76 static bool IsDevToolsWindow(content::RenderViewHost* window_rvh); 77 static DevToolsWindow* OpenDevToolsWindowForWorker( 78 Profile* profile, 79 content::DevToolsAgentHost* worker_agent); 80 static DevToolsWindow* CreateDevToolsWindowForWorker(Profile* profile); 81 static DevToolsWindow* OpenDevToolsWindow( 82 content::RenderViewHost* inspected_rvh); 83 static DevToolsWindow* ToggleDevToolsWindow( 84 Browser* browser, 85 const DevToolsToggleAction& action); 86 static void OpenExternalFrontend(Profile* profile, 87 const std::string& frontend_uri, 88 content::DevToolsAgentHost* agent_host); 89 90 // Exposed for testing, normal clients should not use this method. 91 static DevToolsWindow* ToggleDevToolsWindow( 92 content::RenderViewHost* inspected_rvh, 93 bool force_open, 94 const DevToolsToggleAction& action); 95 96 static void InspectElement( 97 content::RenderViewHost* inspected_rvh, int x, int y); 98 99 static int GetMinimumWidth(); 100 static int GetMinimumHeight(); 101 static int GetMinimizedHeight(); 102 103 // content::DevToolsFrontendHostDelegate: 104 virtual void InspectedContentsClosing() OVERRIDE; 105 106 content::WebContents* web_contents() { return web_contents_; } 107 Browser* browser() { return browser_; } // For tests. 108 DevToolsDockSide dock_side() const { return dock_side_; } 109 110 content::RenderViewHost* GetRenderViewHost(); 111 content::DevToolsClientHost* GetDevToolsClientHostForTest(); 112 113 // Returns preferred devtools window width for given |container_width|. It 114 // tries to use the saved window width, or, if none exists, 1/3 of the 115 // container width, then clamps to try and ensure both devtools and content 116 // are at least somewhat visible. 117 // Called only for the case when devtools window is docked to the side. 118 int GetWidth(int container_width); 119 120 // Returns preferred devtools window height for given |container_height|. 121 // Uses the same logic as GetWidth. 122 // Called only for the case when devtools window is docked to bottom. 123 int GetHeight(int container_height); 124 125 // Stores preferred devtools window width for this instance. 126 void SetWidth(int width); 127 128 // Stores preferred devtools window height for this instance. 129 void SetHeight(int height); 130 131 void Show(const DevToolsToggleAction& action); 132 133 // BeforeUnload interception //////////////////////////////////////////////// 134 135 // In order to preserve any edits the user may have made in devtools, the 136 // beforeunload event of the inspected page is hooked - devtools gets the 137 // first shot at handling beforeunload and presents a dialog to the user. If 138 // the user accepts the dialog then the script is given a chance to handle 139 // it. This way 2 dialogs may be displayed: one from the devtools asking the 140 // user to confirm that they're ok with their devtools edits going away and 141 // another from the webpage as the result of its beforeunload handler. 142 // The following set of methods handle beforeunload event flow through 143 // devtools window. When the |contents| with devtools opened on them are 144 // getting closed, the following sequence of calls takes place: 145 // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates 146 // whether devtools intercept the beforeunload event. 147 // If InterceptPageBeforeUnload() returns true then the following steps 148 // will take place; otherwise only step 4 will be reached and none of the 149 // corresponding functions in steps 2 & 3 will get called. 150 // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event 151 // for devtools frontend, which will asynchronously call 152 // |WebContentsDelegate::BeforeUnloadFired| method. 153 // In case of docked devtools window, devtools are set as a delegate for 154 // its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be 155 // called directly. 156 // If devtools window is undocked it's not set as the delegate so the call 157 // to BeforeUnloadFired is proxied through HandleBeforeUnload() rather 158 // than getting called directly. 159 // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false 160 // it calls throught to the content's BeforeUnloadFired(), which from the 161 // WebContents perspective looks the same as the |content|'s own 162 // beforeunload dialog having had it's 'stay on this page' button clicked. 163 // 3b. If |proceed| = true, then it fires beforeunload event on |contents| 164 // and everything proceeds as it normally would without the Devtools 165 // interception. 166 // 4. If the user cancels the dialog put up by either the WebContents or 167 // devtools frontend, then |contents|'s |BeforeUnloadFired| callback is 168 // called with the proceed argument set to false, this causes 169 // |DevToolsWindow::OnPageCloseCancelled| to be called. 170 171 // Devtools window in undocked state is not set as a delegate of 172 // its frontend. Instead, an instance of browser is set as the delegate, and 173 // thus beforeunload event callback from devtools frontend is not delivered 174 // to the instance of devtools window, which is solely responsible for 175 // managing custom beforeunload event flow. 176 // This is a helper method to route callback from 177 // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|. 178 // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog, 179 // false otherwise. 180 // * |proceed_to_fire_unload| - output parameter, whether we should continue 181 // to fire the unload event or stop things here. 182 // Returns true if devtools window is in a state of intercepting beforeunload 183 // event and if it will manage unload process on its own. 184 static bool HandleBeforeUnload(content::WebContents* contents, 185 bool proceed, 186 bool* proceed_to_fire_unload); 187 188 // Returns true if this contents beforeunload event was intercepted by 189 // devtools and false otherwise. If the event was intercepted, caller should 190 // not fire beforeunlaod event on |contents| itself as devtools window will 191 // take care of it, otherwise caller should continue handling the event as 192 // usual. 193 static bool InterceptPageBeforeUnload(content::WebContents* contents); 194 195 // Returns true if devtools browser has already fired its beforeunload event 196 // as a result of beforeunload event interception. 197 static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser* browser); 198 199 // Returns true if devtools window would like to hook beforeunload event 200 // of this |contents|. 201 static bool NeedsToInterceptBeforeUnload(content::WebContents* contents); 202 203 // Notify devtools window that closing of |contents| was cancelled 204 // by user. 205 static void OnPageCloseCanceled(content::WebContents* contents); 206 207 void SetDockSideForTest(DevToolsDockSide dock_side); 208 209 private: 210 friend class DevToolsControllerTest; 211 212 DevToolsWindow(Profile* profile, 213 const GURL& frontend_url, 214 content::RenderViewHost* inspected_rvh, 215 DevToolsDockSide dock_side); 216 217 static DevToolsWindow* Create(Profile* profile, 218 const GURL& frontend_url, 219 content::RenderViewHost* inspected_rvh, 220 DevToolsDockSide dock_side, 221 bool shared_worker_frontend, 222 bool external_frontend, 223 bool can_dock); 224 static GURL GetDevToolsURL(Profile* profile, 225 const GURL& base_url, 226 DevToolsDockSide dock_side, 227 bool shared_worker_frontend, 228 bool external_frontend, 229 bool can_dock); 230 static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*); 231 static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*); 232 static DevToolsDockSide GetDockSideFromPrefs(Profile* profile); 233 static std::string SideToString(DevToolsDockSide dock_side); 234 static DevToolsDockSide SideFromString(const std::string& dock_side); 235 static bool FindInspectedBrowserAndTabIndex( 236 content::WebContents* inspected_web_contents, Browser**, int* tab); 237 238 // content::NotificationObserver: 239 virtual void Observe(int type, 240 const content::NotificationSource& source, 241 const content::NotificationDetails& details) OVERRIDE; 242 243 // content::WebContentsDelegate: 244 virtual content::WebContents* OpenURLFromTab( 245 content::WebContents* source, 246 const content::OpenURLParams& params) OVERRIDE; 247 virtual void AddNewContents(content::WebContents* source, 248 content::WebContents* new_contents, 249 WindowOpenDisposition disposition, 250 const gfx::Rect& initial_pos, 251 bool user_gesture, 252 bool* was_blocked) OVERRIDE; 253 virtual void CloseContents(content::WebContents* source) OVERRIDE; 254 virtual void BeforeUnloadFired(content::WebContents* tab, 255 bool proceed, 256 bool* proceed_to_fire_unload) OVERRIDE; 257 virtual bool PreHandleKeyboardEvent( 258 content::WebContents* source, 259 const content::NativeWebKeyboardEvent& event, 260 bool* is_keyboard_shortcut) OVERRIDE; 261 virtual void HandleKeyboardEvent( 262 content::WebContents* source, 263 const content::NativeWebKeyboardEvent& event) OVERRIDE; 264 virtual content::JavaScriptDialogManager* 265 GetJavaScriptDialogManager() OVERRIDE; 266 virtual content::ColorChooser* OpenColorChooser( 267 content::WebContents* web_contents, 268 SkColor color, 269 const std::vector<content::ColorSuggestion>& suggestions) OVERRIDE; 270 virtual void RunFileChooser( 271 content::WebContents* web_contents, 272 const content::FileChooserParams& params) OVERRIDE; 273 virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE; 274 275 // content::DevToolsFrontendHostDelegate override: 276 virtual void DispatchOnEmbedder(const std::string& message) OVERRIDE; 277 278 // DevToolsEmbedderMessageDispatcher::Delegate overrides: 279 virtual void ActivateWindow() OVERRIDE; 280 virtual void ActivateContents(content::WebContents* contents) OVERRIDE; 281 virtual void CloseWindow() OVERRIDE; 282 virtual void SetWindowBounds(int x, int y, int width, int height) OVERRIDE; 283 virtual void MoveWindow(int x, int y) OVERRIDE; 284 virtual void SetDockSide(const std::string& side) OVERRIDE; 285 virtual void OpenInNewTab(const std::string& url) OVERRIDE; 286 virtual void SaveToFile(const std::string& url, 287 const std::string& content, 288 bool save_as) OVERRIDE; 289 virtual void AppendToFile(const std::string& url, 290 const std::string& content) OVERRIDE; 291 virtual void RequestFileSystems() OVERRIDE; 292 virtual void AddFileSystem() OVERRIDE; 293 virtual void RemoveFileSystem(const std::string& file_system_path) OVERRIDE; 294 virtual void UpgradeDraggedFileSystemPermissions( 295 const std::string& file_system_url) OVERRIDE; 296 virtual void IndexPath(int request_id, 297 const std::string& file_system_path) OVERRIDE; 298 virtual void StopIndexing(int request_id) OVERRIDE; 299 virtual void SearchInPath(int request_id, 300 const std::string& file_system_path, 301 const std::string& query) OVERRIDE; 302 303 // DevToolsFileHelper callbacks. 304 void FileSavedAs(const std::string& url); 305 void CanceledFileSaveAs(const std::string& url); 306 void AppendedTo(const std::string& url); 307 void FileSystemsLoaded( 308 const std::vector<DevToolsFileHelper::FileSystem>& file_systems); 309 void FileSystemAdded(const DevToolsFileHelper::FileSystem& file_system); 310 void IndexingTotalWorkCalculated(int request_id, 311 const std::string& file_system_path, 312 int total_work); 313 void IndexingWorked(int request_id, 314 const std::string& file_system_path, 315 int worked); 316 void IndexingDone(int request_id, const std::string& file_system_path); 317 void SearchCompleted(int request_id, 318 const std::string& file_system_path, 319 const std::vector<std::string>& file_paths); 320 void ShowDevToolsConfirmInfoBar(const base::string16& message, 321 const InfoBarCallback& callback); 322 323 void CreateDevToolsBrowser(); 324 BrowserWindow* GetInspectedBrowserWindow(); 325 bool IsInspectedBrowserPopup(); 326 void UpdateFrontendDockSide(); 327 void ScheduleAction(const DevToolsToggleAction& action); 328 void DoAction(); 329 void UpdateTheme(); 330 void AddDevToolsExtensionsToClient(); 331 void CallClientFunction(const std::string& function_name, 332 const base::Value* arg1, 333 const base::Value* arg2, 334 const base::Value* arg3); 335 void UpdateBrowserToolbar(); 336 bool IsDocked(); 337 void Restore(); 338 content::WebContents* GetInspectedWebContents(); 339 void DocumentOnLoadCompletedInMainFrame(); 340 341 class InspectedWebContentsObserver; 342 scoped_ptr<InspectedWebContentsObserver> inspected_contents_observer_; 343 class FrontendWebContentsObserver; 344 friend class FrontendWebContentsObserver; 345 scoped_ptr<FrontendWebContentsObserver> frontend_contents_observer_; 346 347 Profile* profile_; 348 content::WebContents* web_contents_; 349 Browser* browser_; 350 DevToolsDockSide dock_side_; 351 bool is_loaded_; 352 DevToolsToggleAction action_on_load_; 353 content::NotificationRegistrar registrar_; 354 scoped_ptr<content::DevToolsClientHost> frontend_host_; 355 scoped_ptr<DevToolsFileHelper> file_helper_; 356 scoped_refptr<DevToolsFileSystemIndexer> file_system_indexer_; 357 typedef std::map< 358 int, 359 scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob> > 360 IndexingJobsMap; 361 IndexingJobsMap indexing_jobs_; 362 int width_; 363 int height_; 364 DevToolsDockSide dock_side_before_minimized_; 365 // True if we're in the process of handling a beforeunload event originating 366 // from the inspected webcontents, see InterceptPageBeforeUnload for details. 367 bool intercepted_page_beforeunload_; 368 369 scoped_ptr<DevToolsEmbedderMessageDispatcher> embedder_message_dispatcher_; 370 base::WeakPtrFactory<DevToolsWindow> weak_factory_; 371 DISALLOW_COPY_AND_ASSIGN(DevToolsWindow); 372 }; 373 374 #endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ 375