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_
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 #include "content/public/browser/web_contents_observer.h"
16 class DevToolsWindowTesting
;
17 class DevToolsEventForwarder
;
20 class DevToolsAgentHost
;
21 struct NativeWebKeyboardEvent
;
25 namespace user_prefs
{
26 class PrefRegistrySyncable
;
29 class DevToolsWindow
: public DevToolsUIBindings::Delegate
,
30 public content::WebContentsDelegate
{
32 class ObserverWithAccessor
: public content::WebContentsObserver
{
34 explicit ObserverWithAccessor(content::WebContents
* web_contents
);
35 virtual ~ObserverWithAccessor();
36 content::WebContents
* GetWebContents();
39 DISALLOW_COPY_AND_ASSIGN(ObserverWithAccessor
);
42 static const char kDevToolsApp
[];
44 virtual ~DevToolsWindow();
46 static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable
* registry
);
48 // Return the DevToolsWindow for the given WebContents if one exists,
50 static DevToolsWindow
* GetInstanceForInspectedWebContents(
51 content::WebContents
* inspected_web_contents
);
53 // Return the docked DevTools WebContents for the given inspected WebContents
54 // if one exists and should be shown in browser window, otherwise NULL.
55 // This method will return only fully initialized window ready to be
57 // If |out_strategy| is not NULL, it will contain resizing strategy.
58 // For immediately-ready-to-use but maybe not yet fully initialized DevTools
59 // use |GetInstanceForInspectedRenderViewHost| instead.
60 static content::WebContents
* GetInTabWebContents(
61 content::WebContents
* inspected_tab
,
62 DevToolsContentsResizingStrategy
* out_strategy
);
64 static bool IsDevToolsWindow(content::WebContents
* web_contents
);
66 // Open or reveal DevTools window, and perform the specified action.
67 static DevToolsWindow
* OpenDevToolsWindow(
68 content::WebContents
* inspected_web_contents
,
69 const DevToolsToggleAction
& action
);
71 // Open or reveal DevTools window, with no special action.
72 static DevToolsWindow
* OpenDevToolsWindow(
73 content::WebContents
* inspected_web_contents
);
75 // Perform specified action for current WebContents inside a |browser|.
76 // This may close currently open DevTools window.
77 static DevToolsWindow
* ToggleDevToolsWindow(
79 const DevToolsToggleAction
& action
);
81 // External frontend is always undocked.
82 static void OpenExternalFrontend(
84 const std::string
& frontend_uri
,
85 const scoped_refptr
<content::DevToolsAgentHost
>& agent_host
,
88 // Worker frontend is always undocked.
89 static DevToolsWindow
* OpenDevToolsWindowForWorker(
91 const scoped_refptr
<content::DevToolsAgentHost
>& worker_agent
);
93 static void InspectElement(content::WebContents
* inspected_web_contents
,
97 // Sets closure to be called after load is done. If already loaded, calls
98 // closure immediately.
99 void SetLoadCompletedCallback(const base::Closure
& closure
);
101 // Forwards an unhandled keyboard event to the DevTools frontend.
102 bool ForwardKeyboardEvent(const content::NativeWebKeyboardEvent
& event
);
104 // BeforeUnload interception ////////////////////////////////////////////////
106 // In order to preserve any edits the user may have made in devtools, the
107 // beforeunload event of the inspected page is hooked - devtools gets the
108 // first shot at handling beforeunload and presents a dialog to the user. If
109 // the user accepts the dialog then the script is given a chance to handle
110 // it. This way 2 dialogs may be displayed: one from the devtools asking the
111 // user to confirm that they're ok with their devtools edits going away and
112 // another from the webpage as the result of its beforeunload handler.
113 // The following set of methods handle beforeunload event flow through
114 // devtools window. When the |contents| with devtools opened on them are
115 // getting closed, the following sequence of calls takes place:
116 // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates
117 // whether devtools intercept the beforeunload event.
118 // If InterceptPageBeforeUnload() returns true then the following steps
119 // will take place; otherwise only step 4 will be reached and none of the
120 // corresponding functions in steps 2 & 3 will get called.
121 // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event
122 // for devtools frontend, which will asynchronously call
123 // |WebContentsDelegate::BeforeUnloadFired| method.
124 // In case of docked devtools window, devtools are set as a delegate for
125 // its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be
127 // If devtools window is undocked it's not set as the delegate so the call
128 // to BeforeUnloadFired is proxied through HandleBeforeUnload() rather
129 // than getting called directly.
130 // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false
131 // it calls throught to the content's BeforeUnloadFired(), which from the
132 // WebContents perspective looks the same as the |content|'s own
133 // beforeunload dialog having had it's 'stay on this page' button clicked.
134 // 3b. If |proceed| = true, then it fires beforeunload event on |contents|
135 // and everything proceeds as it normally would without the Devtools
137 // 4. If the user cancels the dialog put up by either the WebContents or
138 // devtools frontend, then |contents|'s |BeforeUnloadFired| callback is
139 // called with the proceed argument set to false, this causes
140 // |DevToolsWindow::OnPageCloseCancelled| to be called.
142 // Devtools window in undocked state is not set as a delegate of
143 // its frontend. Instead, an instance of browser is set as the delegate, and
144 // thus beforeunload event callback from devtools frontend is not delivered
145 // to the instance of devtools window, which is solely responsible for
146 // managing custom beforeunload event flow.
147 // This is a helper method to route callback from
148 // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|.
149 // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog,
151 // * |proceed_to_fire_unload| - output parameter, whether we should continue
152 // to fire the unload event or stop things here.
153 // Returns true if devtools window is in a state of intercepting beforeunload
154 // event and if it will manage unload process on its own.
155 static bool HandleBeforeUnload(content::WebContents
* contents
,
157 bool* proceed_to_fire_unload
);
159 // Returns true if this contents beforeunload event was intercepted by
160 // devtools and false otherwise. If the event was intercepted, caller should
161 // not fire beforeunlaod event on |contents| itself as devtools window will
162 // take care of it, otherwise caller should continue handling the event as
164 static bool InterceptPageBeforeUnload(content::WebContents
* contents
);
166 // Returns true if devtools browser has already fired its beforeunload event
167 // as a result of beforeunload event interception.
168 static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser
* browser
);
170 // Returns true if devtools window would like to hook beforeunload event
171 // of this |contents|.
172 static bool NeedsToInterceptBeforeUnload(content::WebContents
* contents
);
174 // Notify devtools window that closing of |contents| was cancelled
176 static void OnPageCloseCanceled(content::WebContents
* contents
);
179 friend class DevToolsWindowTesting
;
181 // DevTools lifecycle typically follows this way:
182 // - Toggle/Open: client call;
184 // - ScheduleShow: setup window to be functional, but not yet show;
185 // - DocumentOnLoadCompletedInMainFrame: frontend loaded;
186 // - SetIsDocked: frontend decided on docking state;
187 // - OnLoadCompleted: ready to present frontend;
188 // - Show: actually placing frontend WebContents to a Browser or docked place;
189 // - DoAction: perform action passed in Toggle/Open;
191 // - CloseWindow: initiates before unload handling;
192 // - CloseContents: destroys frontend;
193 // - DevToolsWindow is dead once it's main_web_contents dies.
196 kOnLoadFired
, // Implies SetIsDocked was not yet called.
197 kIsDockedSet
, // Implies DocumentOnLoadCompleted was not yet called.
202 DevToolsWindow(Profile
* profile
,
203 const GURL
& frontend_url
,
204 content::WebContents
* inspected_web_contents
,
207 static DevToolsWindow
* Create(Profile
* profile
,
208 const GURL
& frontend_url
,
209 content::WebContents
* inspected_web_contents
,
210 bool shared_worker_frontend
,
211 bool external_frontend
,
213 const std::string
& settings
);
214 static GURL
GetDevToolsURL(Profile
* profile
,
215 const GURL
& base_url
,
216 bool shared_worker_frontend
,
217 bool external_frontend
,
219 const std::string
& settings
);
220 static DevToolsWindow
* FindDevToolsWindow(content::DevToolsAgentHost
*);
221 static DevToolsWindow
* AsDevToolsWindow(content::WebContents
*);
222 static DevToolsWindow
* CreateDevToolsWindowForWorker(Profile
* profile
);
223 static DevToolsWindow
* ToggleDevToolsWindow(
224 content::WebContents
* web_contents
,
226 const DevToolsToggleAction
& action
,
227 const std::string
& settings
);
229 static std::string
GetDevToolsWindowPlacementPrefKey();
231 // content::WebContentsDelegate:
232 virtual content::WebContents
* OpenURLFromTab(
233 content::WebContents
* source
,
234 const content::OpenURLParams
& params
) OVERRIDE
;
235 virtual void ActivateContents(content::WebContents
* contents
) OVERRIDE
;
236 virtual void AddNewContents(content::WebContents
* source
,
237 content::WebContents
* new_contents
,
238 WindowOpenDisposition disposition
,
239 const gfx::Rect
& initial_pos
,
241 bool* was_blocked
) OVERRIDE
;
242 virtual void WebContentsCreated(content::WebContents
* source_contents
,
243 int opener_render_frame_id
,
244 const base::string16
& frame_name
,
245 const GURL
& target_url
,
246 content::WebContents
* new_contents
) OVERRIDE
;
247 virtual void CloseContents(content::WebContents
* source
) OVERRIDE
;
248 virtual void ContentsZoomChange(bool zoom_in
) OVERRIDE
;
249 virtual void BeforeUnloadFired(content::WebContents
* tab
,
251 bool* proceed_to_fire_unload
) OVERRIDE
;
252 virtual bool PreHandleKeyboardEvent(
253 content::WebContents
* source
,
254 const content::NativeWebKeyboardEvent
& event
,
255 bool* is_keyboard_shortcut
) OVERRIDE
;
256 virtual void HandleKeyboardEvent(
257 content::WebContents
* source
,
258 const content::NativeWebKeyboardEvent
& event
) OVERRIDE
;
259 virtual content::JavaScriptDialogManager
*
260 GetJavaScriptDialogManager() OVERRIDE
;
261 virtual content::ColorChooser
* OpenColorChooser(
262 content::WebContents
* web_contents
,
264 const std::vector
<content::ColorSuggestion
>& suggestions
) OVERRIDE
;
265 virtual void RunFileChooser(
266 content::WebContents
* web_contents
,
267 const content::FileChooserParams
& params
) OVERRIDE
;
268 virtual void WebContentsFocused(content::WebContents
* contents
) OVERRIDE
;
269 virtual bool PreHandleGestureEvent(
270 content::WebContents
* source
,
271 const blink::WebGestureEvent
& event
) OVERRIDE
;
273 // content::DevToolsUIBindings::Delegate overrides
274 virtual void ActivateWindow() OVERRIDE
;
275 virtual void CloseWindow() OVERRIDE
;
276 virtual void SetInspectedPageBounds(const gfx::Rect
& rect
) OVERRIDE
;
277 virtual void InspectElementCompleted() OVERRIDE
;
278 virtual void MoveWindow(int x
, int y
) OVERRIDE
;
279 virtual void SetIsDocked(bool is_docked
) OVERRIDE
;
280 virtual void OpenInNewTab(const std::string
& url
) OVERRIDE
;
281 virtual void SetWhitelistedShortcuts(const std::string
& message
) OVERRIDE
;
282 virtual void InspectedContentsClosing() OVERRIDE
;
283 virtual void OnLoadCompleted() OVERRIDE
;
284 virtual InfoBarService
* GetInfoBarService() OVERRIDE
;
285 virtual void RenderProcessGone() OVERRIDE
;
287 void CreateDevToolsBrowser();
288 BrowserWindow
* GetInspectedBrowserWindow();
289 void ScheduleShow(const DevToolsToggleAction
& action
);
290 void Show(const DevToolsToggleAction
& action
);
291 void DoAction(const DevToolsToggleAction
& action
);
292 void LoadCompleted();
293 void UpdateBrowserToolbar();
294 void UpdateBrowserWindow();
295 content::WebContents
* GetInspectedWebContents();
297 scoped_ptr
<ObserverWithAccessor
> inspected_contents_observer_
;
300 content::WebContents
* main_web_contents_
;
301 content::WebContents
* toolbox_web_contents_
;
302 DevToolsUIBindings
* bindings_
;
305 const bool can_dock_
;
306 LifeStage life_stage_
;
307 DevToolsToggleAction action_on_load_
;
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 base::Closure close_callback_
;
315 base::TimeTicks inspect_element_start_time_
;
316 scoped_ptr
<DevToolsEventForwarder
> event_forwarder_
;
318 friend class DevToolsEventForwarder
;
319 DISALLOW_COPY_AND_ASSIGN(DevToolsWindow
);
322 #endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_