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 ~ObserverWithAccessor() override
;
38 DISALLOW_COPY_AND_ASSIGN(ObserverWithAccessor
);
41 static const char kDevToolsApp
[];
43 ~DevToolsWindow() override
;
45 static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable
* registry
);
47 // Return the DevToolsWindow for the given WebContents if one exists,
49 static DevToolsWindow
* GetInstanceForInspectedWebContents(
50 content::WebContents
* inspected_web_contents
);
52 // Return the docked DevTools WebContents for the given inspected WebContents
53 // if one exists and should be shown in browser window, otherwise NULL.
54 // This method will return only fully initialized window ready to be
56 // If |out_strategy| is not NULL, it will contain resizing strategy.
57 // For immediately-ready-to-use but maybe not yet fully initialized DevTools
58 // use |GetInstanceForInspectedRenderViewHost| instead.
59 static content::WebContents
* GetInTabWebContents(
60 content::WebContents
* inspected_tab
,
61 DevToolsContentsResizingStrategy
* out_strategy
);
63 static bool IsDevToolsWindow(content::WebContents
* web_contents
);
64 static DevToolsWindow
* AsDevToolsWindow(content::WebContents
* web_contents
);
65 static DevToolsWindow
* FindDevToolsWindow(content::DevToolsAgentHost
*);
67 // Open or reveal DevTools window, and perform the specified action.
68 // How to get pointer to the created window see comments for
69 // ToggleDevToolsWindow().
70 static void OpenDevToolsWindow(content::WebContents
* inspected_web_contents
,
71 const DevToolsToggleAction
& action
);
73 // Open or reveal DevTools window, with no special action.
74 // How to get pointer to the created window see comments for
75 // ToggleDevToolsWindow().
76 static void OpenDevToolsWindow(content::WebContents
* inspected_web_contents
);
78 // Open or reveal DevTools window. This window will be undocked.
79 static void OpenDevToolsWindow(
81 const scoped_refptr
<content::DevToolsAgentHost
>& agent_host
);
83 // Perform specified action for current WebContents inside a |browser|.
84 // This may close currently open DevTools window.
85 // If DeveloperToolsDisabled policy is set, no DevTools window created.
86 // In case if needed pointer to the created window one should use
87 // DevToolsAgentHost and DevToolsWindow::FindDevToolsWindow(). E.g.:
89 // scoped_refptr<content::DevToolsAgentHost> agent(
90 // content::DevToolsAgentHost::GetOrCreateFor(inspected_web_contents));
91 // DevToolsWindow::ToggleDevToolsWindow(
92 // inspected_web_contents, DevToolsToggleAction::Show());
93 // DevToolsWindow* window = DevToolsWindow::FindDevToolsWindow(agent.get());
95 static void ToggleDevToolsWindow(
97 const DevToolsToggleAction
& action
);
99 // External frontend is always undocked.
100 static void OpenExternalFrontend(
102 const std::string
& frontend_uri
,
103 const scoped_refptr
<content::DevToolsAgentHost
>& agent_host
,
106 // Worker frontend is always undocked.
107 static void OpenDevToolsWindowForWorker(
109 const scoped_refptr
<content::DevToolsAgentHost
>& worker_agent
);
111 static void InspectElement(content::WebContents
* inspected_web_contents
,
115 // Sets closure to be called after load is done. If already loaded, calls
116 // closure immediately.
117 void SetLoadCompletedCallback(const base::Closure
& closure
);
119 // Forwards an unhandled keyboard event to the DevTools frontend.
120 bool ForwardKeyboardEvent(const content::NativeWebKeyboardEvent
& event
);
122 // Reloads inspected web contents as if it was triggered from DevTools.
123 void ReloadInspectedWebContents(bool ignore_cache
);
125 // content::WebContentsDelegate overrides.
126 content::WebContents
* OpenURLFromTab(
127 content::WebContents
* source
,
128 const content::OpenURLParams
& params
) override
;
130 void ShowCertificateViewer(int certificate_id
);
132 // BeforeUnload interception ////////////////////////////////////////////////
134 // In order to preserve any edits the user may have made in devtools, the
135 // beforeunload event of the inspected page is hooked - devtools gets the
136 // first shot at handling beforeunload and presents a dialog to the user. If
137 // the user accepts the dialog then the script is given a chance to handle
138 // it. This way 2 dialogs may be displayed: one from the devtools asking the
139 // user to confirm that they're ok with their devtools edits going away and
140 // another from the webpage as the result of its beforeunload handler.
141 // The following set of methods handle beforeunload event flow through
142 // devtools window. When the |contents| with devtools opened on them are
143 // getting closed, the following sequence of calls takes place:
144 // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates
145 // whether devtools intercept the beforeunload event.
146 // If InterceptPageBeforeUnload() returns true then the following steps
147 // will take place; otherwise only step 4 will be reached and none of the
148 // corresponding functions in steps 2 & 3 will get called.
149 // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event
150 // for devtools frontend, which will asynchronously call
151 // |WebContentsDelegate::BeforeUnloadFired| method.
152 // In case of docked devtools window, devtools are set as a delegate for
153 // its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be
155 // If devtools window is undocked it's not set as the delegate so the call
156 // to BeforeUnloadFired is proxied through HandleBeforeUnload() rather
157 // than getting called directly.
158 // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false
159 // it calls throught to the content's BeforeUnloadFired(), which from the
160 // WebContents perspective looks the same as the |content|'s own
161 // beforeunload dialog having had it's 'stay on this page' button clicked.
162 // 3b. If |proceed| = true, then it fires beforeunload event on |contents|
163 // and everything proceeds as it normally would without the Devtools
165 // 4. If the user cancels the dialog put up by either the WebContents or
166 // devtools frontend, then |contents|'s |BeforeUnloadFired| callback is
167 // called with the proceed argument set to false, this causes
168 // |DevToolsWindow::OnPageCloseCancelled| to be called.
170 // Devtools window in undocked state is not set as a delegate of
171 // its frontend. Instead, an instance of browser is set as the delegate, and
172 // thus beforeunload event callback from devtools frontend is not delivered
173 // to the instance of devtools window, which is solely responsible for
174 // managing custom beforeunload event flow.
175 // This is a helper method to route callback from
176 // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|.
177 // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog,
179 // * |proceed_to_fire_unload| - output parameter, whether we should continue
180 // to fire the unload event or stop things here.
181 // Returns true if devtools window is in a state of intercepting beforeunload
182 // event and if it will manage unload process on its own.
183 static bool HandleBeforeUnload(content::WebContents
* contents
,
185 bool* proceed_to_fire_unload
);
187 // Returns true if this contents beforeunload event was intercepted by
188 // devtools and false otherwise. If the event was intercepted, caller should
189 // not fire beforeunlaod event on |contents| itself as devtools window will
190 // take care of it, otherwise caller should continue handling the event as
192 static bool InterceptPageBeforeUnload(content::WebContents
* contents
);
194 // Returns true if devtools browser has already fired its beforeunload event
195 // as a result of beforeunload event interception.
196 static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser
* browser
);
198 // Returns true if devtools window would like to hook beforeunload event
199 // of this |contents|.
200 static bool NeedsToInterceptBeforeUnload(content::WebContents
* contents
);
202 // Notify devtools window that closing of |contents| was cancelled
204 static void OnPageCloseCanceled(content::WebContents
* contents
);
207 friend class DevToolsWindowTesting
;
209 // DevTools lifecycle typically follows this way:
210 // - Toggle/Open: client call;
212 // - ScheduleShow: setup window to be functional, but not yet show;
213 // - DocumentOnLoadCompletedInMainFrame: frontend loaded;
214 // - SetIsDocked: frontend decided on docking state;
215 // - OnLoadCompleted: ready to present frontend;
216 // - Show: actually placing frontend WebContents to a Browser or docked place;
217 // - DoAction: perform action passed in Toggle/Open;
219 // - CloseWindow: initiates before unload handling;
220 // - CloseContents: destroys frontend;
221 // - DevToolsWindow is dead once it's main_web_contents dies.
224 kOnLoadFired
, // Implies SetIsDocked was not yet called.
225 kIsDockedSet
, // Implies DocumentOnLoadCompleted was not yet called.
230 DevToolsWindow(Profile
* profile
,
231 content::WebContents
* main_web_contents
,
232 DevToolsUIBindings
* bindings
,
233 content::WebContents
* inspected_web_contents
,
236 static DevToolsWindow
* Create(Profile
* profile
,
237 const GURL
& frontend_url
,
238 content::WebContents
* inspected_web_contents
,
239 bool shared_worker_frontend
,
240 const std::string
& remote_frontend
,
242 const std::string
& settings
);
243 static GURL
GetDevToolsURL(Profile
* profile
,
244 const GURL
& base_url
,
245 bool shared_worker_frontend
,
246 const std::string
& remote_frontend
,
248 const std::string
& settings
);
250 static DevToolsWindow
* CreateDevToolsWindowForWorker(Profile
* profile
);
251 static void ToggleDevToolsWindow(
252 content::WebContents
* web_contents
,
254 const DevToolsToggleAction
& action
,
255 const std::string
& settings
);
257 // content::WebContentsDelegate:
258 void ActivateContents(content::WebContents
* contents
) override
;
259 void AddNewContents(content::WebContents
* source
,
260 content::WebContents
* new_contents
,
261 WindowOpenDisposition disposition
,
262 const gfx::Rect
& initial_rect
,
264 bool* was_blocked
) override
;
265 void WebContentsCreated(content::WebContents
* source_contents
,
266 int opener_render_frame_id
,
267 const std::string
& frame_name
,
268 const GURL
& target_url
,
269 content::WebContents
* new_contents
) override
;
270 void CloseContents(content::WebContents
* source
) override
;
271 void ContentsZoomChange(bool zoom_in
) override
;
272 void BeforeUnloadFired(content::WebContents
* tab
,
274 bool* proceed_to_fire_unload
) override
;
275 bool PreHandleKeyboardEvent(content::WebContents
* source
,
276 const content::NativeWebKeyboardEvent
& event
,
277 bool* is_keyboard_shortcut
) override
;
278 void HandleKeyboardEvent(
279 content::WebContents
* source
,
280 const content::NativeWebKeyboardEvent
& event
) override
;
281 content::JavaScriptDialogManager
* GetJavaScriptDialogManager(
282 content::WebContents
* source
) override
;
283 content::ColorChooser
* OpenColorChooser(
284 content::WebContents
* web_contents
,
286 const std::vector
<content::ColorSuggestion
>& suggestions
) override
;
287 void RunFileChooser(content::WebContents
* web_contents
,
288 const content::FileChooserParams
& params
) override
;
289 bool PreHandleGestureEvent(content::WebContents
* source
,
290 const blink::WebGestureEvent
& event
) override
;
292 // content::DevToolsUIBindings::Delegate overrides
293 void ActivateWindow() override
;
294 void CloseWindow() override
;
295 void SetInspectedPageBounds(const gfx::Rect
& rect
) override
;
296 void InspectElementCompleted() override
;
297 void SetIsDocked(bool is_docked
) override
;
298 void OpenInNewTab(const std::string
& url
) override
;
299 void SetWhitelistedShortcuts(const std::string
& message
) override
;
300 void InspectedContentsClosing() override
;
301 void OnLoadCompleted() override
;
302 InfoBarService
* GetInfoBarService() override
;
303 void RenderProcessGone(bool crashed
) override
;
305 void CreateDevToolsBrowser();
306 BrowserWindow
* GetInspectedBrowserWindow();
307 void ScheduleShow(const DevToolsToggleAction
& action
);
308 void Show(const DevToolsToggleAction
& action
);
309 void DoAction(const DevToolsToggleAction
& action
);
310 void LoadCompleted();
311 void UpdateBrowserToolbar();
312 void UpdateBrowserWindow();
313 content::WebContents
* GetInspectedWebContents();
315 scoped_ptr
<ObserverWithAccessor
> inspected_contents_observer_
;
318 content::WebContents
* main_web_contents_
;
319 content::WebContents
* toolbox_web_contents_
;
320 DevToolsUIBindings
* bindings_
;
323 const bool can_dock_
;
324 LifeStage life_stage_
;
325 DevToolsToggleAction action_on_load_
;
326 DevToolsContentsResizingStrategy contents_resizing_strategy_
;
327 // True if we're in the process of handling a beforeunload event originating
328 // from the inspected webcontents, see InterceptPageBeforeUnload for details.
329 bool intercepted_page_beforeunload_
;
330 base::Closure load_completed_callback_
;
331 base::Closure close_callback_
;
333 base::TimeTicks inspect_element_start_time_
;
334 scoped_ptr
<DevToolsEventForwarder
> event_forwarder_
;
336 friend class DevToolsEventForwarder
;
337 DISALLOW_COPY_AND_ASSIGN(DevToolsWindow
);
340 #endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_