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.
5 #ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
6 #define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
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 #include "ui/gfx/insets.h"
25 #include "ui/gfx/size.h"
29 class DevToolsControllerTest
;
37 class DevToolsAgentHost
;
38 class DevToolsClientHost
;
39 struct FileChooserParams
;
48 namespace user_prefs
{
49 class PrefRegistrySyncable
;
52 class DevToolsWindow
: private content::NotificationObserver
,
53 private content::WebContentsDelegate
,
54 private content::DevToolsFrontendHostDelegate
,
55 private DevToolsEmbedderMessageDispatcher::Delegate
{
57 typedef base::Callback
<void(bool)> InfoBarCallback
;
59 static const char kDevToolsApp
[];
61 virtual ~DevToolsWindow();
63 static std::string
GetDevToolsWindowPlacementPrefKey();
64 static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable
* registry
);
66 // Return the DevToolsWindow for the given RenderViewHost if one exists,
68 static DevToolsWindow
* GetInstanceForInspectedRenderViewHost(
69 content::RenderViewHost
* inspected_rvh
);
71 // Return the DevToolsWindow for the given WebContents if one exists and is
72 // docked, otherwise NULL. This method will return only fully initialized
73 // window ready to be presented in UI.
74 // For immediately-ready-to-use but maybe not yet fully initialized DevTools
75 // use |GetInstanceForInspectedRenderViewHost| instead.
76 static DevToolsWindow
* GetDockedInstanceForInspectedTab(
77 content::WebContents
* inspected_tab
);
79 static bool IsDevToolsWindow(content::RenderViewHost
* window_rvh
);
81 // Open or reveal DevTools window, and perform the specified action.
82 static DevToolsWindow
* OpenDevToolsWindow(
83 content::RenderViewHost
* inspected_rvh
,
84 const DevToolsToggleAction
& action
);
86 // Open or reveal DevTools window, with no special action.
87 static DevToolsWindow
* OpenDevToolsWindow(
88 content::RenderViewHost
* inspected_rvh
);
90 static DevToolsWindow
* OpenDevToolsWindowForTest(
91 content::RenderViewHost
* inspected_rvh
, bool is_docked
);
92 static DevToolsWindow
* OpenDevToolsWindowForTest(
93 Browser
* browser
, bool is_docked
);
95 // Perform specified action for current WebContents inside a |browser|.
96 // This may close currently open DevTools window.
97 static DevToolsWindow
* ToggleDevToolsWindow(
99 const DevToolsToggleAction
& action
);
101 // External frontend is always undocked.
102 static void OpenExternalFrontend(
104 const std::string
& frontend_uri
,
105 content::DevToolsAgentHost
* agent_host
);
107 // Worker frontend is always undocked.
108 static DevToolsWindow
* OpenDevToolsWindowForWorker(
110 content::DevToolsAgentHost
* worker_agent
);
112 static void InspectElement(
113 content::RenderViewHost
* inspected_rvh
, int x
, int y
);
115 static int GetMinimizedHeight();
117 // content::DevToolsFrontendHostDelegate:
118 virtual void InspectedContentsClosing() OVERRIDE
;
120 content::WebContents
* web_contents() { return web_contents_
; }
121 Browser
* browser() { return browser_
; } // For tests.
123 content::RenderViewHost
* GetRenderViewHost();
125 // Inspected WebContents is placed over DevTools WebContents in docked mode.
126 // The following methods return the insets of inspected WebContents
127 // relative to DevTools WebContents.
128 gfx::Insets
GetContentsInsets() const;
130 // Minimum size of the docked DevTools WebContents. This includes
131 // the overlaying inspected WebContents size.
132 gfx::Size
GetMinimumSize() const;
134 // BeforeUnload interception ////////////////////////////////////////////////
136 // In order to preserve any edits the user may have made in devtools, the
137 // beforeunload event of the inspected page is hooked - devtools gets the
138 // first shot at handling beforeunload and presents a dialog to the user. If
139 // the user accepts the dialog then the script is given a chance to handle
140 // it. This way 2 dialogs may be displayed: one from the devtools asking the
141 // user to confirm that they're ok with their devtools edits going away and
142 // another from the webpage as the result of its beforeunload handler.
143 // The following set of methods handle beforeunload event flow through
144 // devtools window. When the |contents| with devtools opened on them are
145 // getting closed, the following sequence of calls takes place:
146 // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates
147 // whether devtools intercept the beforeunload event.
148 // If InterceptPageBeforeUnload() returns true then the following steps
149 // will take place; otherwise only step 4 will be reached and none of the
150 // corresponding functions in steps 2 & 3 will get called.
151 // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event
152 // for devtools frontend, which will asynchronously call
153 // |WebContentsDelegate::BeforeUnloadFired| method.
154 // In case of docked devtools window, devtools are set as a delegate for
155 // its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be
157 // If devtools window is undocked it's not set as the delegate so the call
158 // to BeforeUnloadFired is proxied through HandleBeforeUnload() rather
159 // than getting called directly.
160 // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false
161 // it calls throught to the content's BeforeUnloadFired(), which from the
162 // WebContents perspective looks the same as the |content|'s own
163 // beforeunload dialog having had it's 'stay on this page' button clicked.
164 // 3b. If |proceed| = true, then it fires beforeunload event on |contents|
165 // and everything proceeds as it normally would without the Devtools
167 // 4. If the user cancels the dialog put up by either the WebContents or
168 // devtools frontend, then |contents|'s |BeforeUnloadFired| callback is
169 // called with the proceed argument set to false, this causes
170 // |DevToolsWindow::OnPageCloseCancelled| to be called.
172 // Devtools window in undocked state is not set as a delegate of
173 // its frontend. Instead, an instance of browser is set as the delegate, and
174 // thus beforeunload event callback from devtools frontend is not delivered
175 // to the instance of devtools window, which is solely responsible for
176 // managing custom beforeunload event flow.
177 // This is a helper method to route callback from
178 // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|.
179 // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog,
181 // * |proceed_to_fire_unload| - output parameter, whether we should continue
182 // to fire the unload event or stop things here.
183 // Returns true if devtools window is in a state of intercepting beforeunload
184 // event and if it will manage unload process on its own.
185 static bool HandleBeforeUnload(content::WebContents
* contents
,
187 bool* proceed_to_fire_unload
);
189 // Returns true if this contents beforeunload event was intercepted by
190 // devtools and false otherwise. If the event was intercepted, caller should
191 // not fire beforeunlaod event on |contents| itself as devtools window will
192 // take care of it, otherwise caller should continue handling the event as
194 static bool InterceptPageBeforeUnload(content::WebContents
* contents
);
196 // Returns true if devtools browser has already fired its beforeunload event
197 // as a result of beforeunload event interception.
198 static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser
* browser
);
200 // Returns true if devtools window would like to hook beforeunload event
201 // of this |contents|.
202 static bool NeedsToInterceptBeforeUnload(content::WebContents
* contents
);
204 // Notify devtools window that closing of |contents| was cancelled
206 static void OnPageCloseCanceled(content::WebContents
* contents
);
209 friend class DevToolsControllerTest
;
210 friend class DevToolsSanityTest
;
211 friend class BrowserWindowControllerTest
;
213 // DevTools initialization typically follows this way:
214 // - Toggle/Open: client call;
216 // - ScheduleShow: setup window to be functional, but not yet show;
217 // - DocumentOnLoadCompletedInMainFrame: frontend loaded;
218 // - SetIsDocked: frontend decided on docking state;
219 // - OnLoadCompleted: ready to present frontend;
220 // - Show: actually placing frontend WebContents to a Browser or docked place;
221 // - DoAction: perform action passed in Toggle/Open.
224 kOnLoadFired
, // Implies SetIsDocked was not yet called.
225 kIsDockedSet
, // Implies DocumentOnLoadCompleted was not yet called.
229 DevToolsWindow(Profile
* profile
,
230 const GURL
& frontend_url
,
231 content::RenderViewHost
* inspected_rvh
,
234 static DevToolsWindow
* Create(Profile
* profile
,
235 const GURL
& frontend_url
,
236 content::RenderViewHost
* inspected_rvh
,
237 bool shared_worker_frontend
,
238 bool external_frontend
,
240 static GURL
GetDevToolsURL(Profile
* profile
,
241 const GURL
& base_url
,
242 bool shared_worker_frontend
,
243 bool external_frontend
,
245 static DevToolsWindow
* FindDevToolsWindow(content::DevToolsAgentHost
*);
246 static DevToolsWindow
* AsDevToolsWindow(content::RenderViewHost
*);
247 static DevToolsWindow
* CreateDevToolsWindowForWorker(Profile
* profile
);
248 static bool IsDockedFromString(const std::string
& dock_side
);
249 static bool FindInspectedBrowserAndTabIndex(
250 content::WebContents
* inspected_web_contents
, Browser
**, int* tab
);
251 static DevToolsWindow
* ToggleDevToolsWindow(
252 content::RenderViewHost
* inspected_rvh
,
254 const DevToolsToggleAction
& action
);
256 // content::NotificationObserver:
257 virtual void Observe(int type
,
258 const content::NotificationSource
& source
,
259 const content::NotificationDetails
& details
) OVERRIDE
;
261 // content::WebContentsDelegate:
262 virtual content::WebContents
* OpenURLFromTab(
263 content::WebContents
* source
,
264 const content::OpenURLParams
& params
) OVERRIDE
;
265 virtual void AddNewContents(content::WebContents
* source
,
266 content::WebContents
* new_contents
,
267 WindowOpenDisposition disposition
,
268 const gfx::Rect
& initial_pos
,
270 bool* was_blocked
) OVERRIDE
;
271 virtual void CloseContents(content::WebContents
* source
) OVERRIDE
;
272 virtual void BeforeUnloadFired(content::WebContents
* tab
,
274 bool* proceed_to_fire_unload
) OVERRIDE
;
275 virtual bool PreHandleKeyboardEvent(
276 content::WebContents
* source
,
277 const content::NativeWebKeyboardEvent
& event
,
278 bool* is_keyboard_shortcut
) OVERRIDE
;
279 virtual void HandleKeyboardEvent(
280 content::WebContents
* source
,
281 const content::NativeWebKeyboardEvent
& event
) OVERRIDE
;
282 virtual content::JavaScriptDialogManager
*
283 GetJavaScriptDialogManager() OVERRIDE
;
284 virtual content::ColorChooser
* OpenColorChooser(
285 content::WebContents
* web_contents
,
287 const std::vector
<content::ColorSuggestion
>& suggestions
) OVERRIDE
;
288 virtual void RunFileChooser(
289 content::WebContents
* web_contents
,
290 const content::FileChooserParams
& params
) OVERRIDE
;
291 virtual void WebContentsFocused(content::WebContents
* contents
) OVERRIDE
;
293 // content::DevToolsFrontendHostDelegate override:
294 virtual void DispatchOnEmbedder(const std::string
& message
) OVERRIDE
;
296 // DevToolsEmbedderMessageDispatcher::Delegate overrides:
297 virtual void ActivateWindow() OVERRIDE
;
298 virtual void ActivateContents(content::WebContents
* contents
) OVERRIDE
;
299 virtual void CloseWindow() OVERRIDE
;
300 virtual void SetContentsInsets(
301 int left
, int top
, int right
, int bottom
) OVERRIDE
;
302 virtual void MoveWindow(int x
, int y
) OVERRIDE
;
303 virtual void SetDockSide(const std::string
& side
) OVERRIDE
;
304 virtual void SetIsDocked(bool is_docked
) OVERRIDE
;
305 virtual void OpenInNewTab(const std::string
& url
) OVERRIDE
;
306 virtual void SaveToFile(const std::string
& url
,
307 const std::string
& content
,
308 bool save_as
) OVERRIDE
;
309 virtual void AppendToFile(const std::string
& url
,
310 const std::string
& content
) OVERRIDE
;
311 virtual void RequestFileSystems() OVERRIDE
;
312 virtual void AddFileSystem() OVERRIDE
;
313 virtual void RemoveFileSystem(const std::string
& file_system_path
) OVERRIDE
;
314 virtual void UpgradeDraggedFileSystemPermissions(
315 const std::string
& file_system_url
) OVERRIDE
;
316 virtual void IndexPath(int request_id
,
317 const std::string
& file_system_path
) OVERRIDE
;
318 virtual void StopIndexing(int request_id
) OVERRIDE
;
319 virtual void SearchInPath(int request_id
,
320 const std::string
& file_system_path
,
321 const std::string
& query
) OVERRIDE
;
323 // DevToolsFileHelper callbacks.
324 void FileSavedAs(const std::string
& url
);
325 void CanceledFileSaveAs(const std::string
& url
);
326 void AppendedTo(const std::string
& url
);
327 void FileSystemsLoaded(
328 const std::vector
<DevToolsFileHelper::FileSystem
>& file_systems
);
329 void FileSystemAdded(const DevToolsFileHelper::FileSystem
& file_system
);
330 void IndexingTotalWorkCalculated(int request_id
,
331 const std::string
& file_system_path
,
333 void IndexingWorked(int request_id
,
334 const std::string
& file_system_path
,
336 void IndexingDone(int request_id
, const std::string
& file_system_path
);
337 void SearchCompleted(int request_id
,
338 const std::string
& file_system_path
,
339 const std::vector
<std::string
>& file_paths
);
340 void ShowDevToolsConfirmInfoBar(const base::string16
& message
,
341 const InfoBarCallback
& callback
);
343 void CreateDevToolsBrowser();
344 BrowserWindow
* GetInspectedBrowserWindow();
345 void ScheduleShow(const DevToolsToggleAction
& action
);
346 void Show(const DevToolsToggleAction
& action
);
347 void DoAction(const DevToolsToggleAction
& action
);
348 void LoadCompleted();
349 void SetIsDockedAndShowImmediatelyForTest(bool is_docked
);
351 void AddDevToolsExtensionsToClient();
352 void CallClientFunction(const std::string
& function_name
,
353 const base::Value
* arg1
,
354 const base::Value
* arg2
,
355 const base::Value
* arg3
);
356 void UpdateBrowserToolbar();
357 content::WebContents
* GetInspectedWebContents();
358 void DocumentOnLoadCompletedInMainFrame();
360 class InspectedWebContentsObserver
;
361 scoped_ptr
<InspectedWebContentsObserver
> inspected_contents_observer_
;
362 class FrontendWebContentsObserver
;
363 friend class FrontendWebContentsObserver
;
364 scoped_ptr
<FrontendWebContentsObserver
> frontend_contents_observer_
;
367 content::WebContents
* web_contents_
;
371 LoadState load_state_
;
372 DevToolsToggleAction action_on_load_
;
373 bool ignore_set_is_docked_for_test_
;
374 content::NotificationRegistrar registrar_
;
375 scoped_ptr
<content::DevToolsClientHost
> frontend_host_
;
376 scoped_ptr
<DevToolsFileHelper
> file_helper_
;
377 scoped_refptr
<DevToolsFileSystemIndexer
> file_system_indexer_
;
380 scoped_refptr
<DevToolsFileSystemIndexer::FileSystemIndexingJob
> >
382 IndexingJobsMap indexing_jobs_
;
383 gfx::Insets contents_insets_
;
384 // True if we're in the process of handling a beforeunload event originating
385 // from the inspected webcontents, see InterceptPageBeforeUnload for details.
386 bool intercepted_page_beforeunload_
;
388 scoped_ptr
<DevToolsEmbedderMessageDispatcher
> embedder_message_dispatcher_
;
389 base::WeakPtrFactory
<DevToolsWindow
> weak_factory_
;
390 DISALLOW_COPY_AND_ASSIGN(DevToolsWindow
);
393 #endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_