• 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 <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 
web_contents()106   content::WebContents* web_contents() { return web_contents_; }
browser()107   Browser* browser() { return browser_; }  // For tests.
dock_side()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