• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
browser_for_test()95   Browser* browser_for_test() { return browser_; }
web_contents_for_test()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