From 282639a79aba67c3b51aa14041776b1cffce7186 Mon Sep 17 00:00:00 2001 From: Patrick Reynolds Date: Fri, 15 Nov 2013 14:23:52 -0600 Subject: [PATCH] implement dev tools --- .../inspectable_web_contents_view_linux.cc | 216 +++++++++++++++++- .../inspectable_web_contents_view_linux.h | 21 ++ 2 files changed, 225 insertions(+), 12 deletions(-) diff --git a/brightray/browser/linux/inspectable_web_contents_view_linux.cc b/brightray/browser/linux/inspectable_web_contents_view_linux.cc index 2ef27e4fb09..3a8428217c4 100644 --- a/brightray/browser/linux/inspectable_web_contents_view_linux.cc +++ b/brightray/browser/linux/inspectable_web_contents_view_linux.cc @@ -1,5 +1,8 @@ #include "inspectable_web_contents_view_linux.h" +#include +#include +#include "base/strings/stringprintf.h" #include "browser/browser_client.h" #include "browser/inspectable_web_contents_impl.h" @@ -12,34 +15,223 @@ InspectableWebContentsView* CreateInspectableContentsView(InspectableWebContents } InspectableWebContentsViewLinux::InspectableWebContentsViewLinux(InspectableWebContentsImpl* inspectable_web_contents) - : inspectable_web_contents_(inspectable_web_contents) { - // TODO - fprintf(stderr, "InspectableWebContentsViewLinux::InspectableWebContentsViewLinux\n"); + : inspectable_web_contents_(inspectable_web_contents), devtools_window(NULL) { } InspectableWebContentsViewLinux::~InspectableWebContentsViewLinux() { - // TODO - fprintf(stderr, "InspectableWebContentsViewLinux::~InspectableWebContentsViewLinux\n"); + if (devtools_window) gtk_widget_destroy(devtools_window); } +#if 0 // some utility functions to debug GTK window hierarchies +static void dump_one(GtkWidget *wat, int indent) { + GtkAllocation alloc; + gtk_widget_get_allocation(wat, &alloc); + fprintf(stderr, "%*s[%p] %s @%d,%d %dx%d", + indent, "", wat, + g_type_name_from_instance((GTypeInstance*)wat), + alloc.x, alloc.y, alloc.width, alloc.height); + if (GTK_IS_WINDOW(wat)) fprintf(stderr, " - \"%s\"", gtk_window_get_title(GTK_WINDOW(wat))); + fputc('\n', stderr); +} + +static void dump_the_whole_tree(GtkWidget *wat, int indent) { + if (!wat) { + fprintf(stderr, "(nil)\n"); + return; + } + dump_one(wat, indent); + GList *kids = gtk_container_get_children(GTK_CONTAINER(wat)); + for (GList *p=kids; p; p=p->next) { + dump_the_whole_tree(GTK_WIDGET(p->data), indent+2); + } +} + +static void dump_parents(GtkWidget *wat) { + fprintf(stderr, "Parents:\n"); + for (GtkWidget *p=gtk_widget_get_parent(wat); p; p=gtk_widget_get_parent(p)) { + dump_one(p, 2); + } +} +#endif + gfx::NativeView InspectableWebContentsViewLinux::GetNativeView() const { return inspectable_web_contents_->GetWebContents()->GetView()->GetNativeView(); } + +/* This code is a little bit hairy. + The dev tools can be in any one of five places: + 1. Unassigned and invisible. This is the default state until someone asks + to 'inspect element' for the first time. In this case, devtools->parent is + NULL. + 2. In an onscreen window, visible. + 3. In the bottom half of a GtkVPaned. + 4. In the right half of a GtkHPaned. + 5. In an offscreen window, invisible. This is where they go once they have + been displayed and the user asks to "close" them. They can't be put back + into the unassigned state. + ShowDevTools() and is responsible for transitioning from any one of these + states to the three visible states, 2-4, as indicated by the contents of the + 'dockside' variable. The helper functions ShowDevToolsInWindow and + ShowDevToolsInPane focus on transitioning to states 2 and 3+4, respectively. + These helper functions are responsible for the entire transition, including + cleaning up any extraneous containers from the old state. + + Hiding the dev tools is taken care of by CloseDevTools (from paned states + 3+4 to invisible state 5) or by the "delete-event" signal on the + devtools_window (from window state 2 to 5). + + Remember that GTK does reference counting, so a view with no refs and no + parent will be freed. Views that have a ref but no parents will lose their + dimensions. So it's best to move the devtools view from place to place with + gtk_widget_reparent whenever possible. Unfortunately, one cannot reparent + things into a GtkPaned, so fairly brittle use of g_object_[un]ref and + gtk_container_remove happens. +*/ + void InspectableWebContentsViewLinux::ShowDevTools() { - // TODO - fprintf(stderr, "InspectableWebContentsViewLinux::ShowDevTools\n"); + GtkWidget *devtools = inspectable_web_contents()->devtools_web_contents()->GetView()->GetNativeView(); + GtkWidget *parent = gtk_widget_get_parent(devtools); + + DLOG(INFO) << base::StringPrintf("InspectableWebContentsViewLinux::ShowDevTools - parent=%s@%p window=%p dockside=\"%s\"", + g_type_name_from_instance((GTypeInstance*)parent), parent, devtools_window, dockside.c_str()); + + if (!parent || GTK_IS_PANED(parent)) { + if (dockside == "undocked") ShowDevToolsInWindow(); + else if (dockside == "bottom") ShowDevToolsInPane(true); + else if (dockside == "right") ShowDevToolsInPane(false); + } + else { + DCHECK(parent == devtools_window); + if (dockside == "undocked") gtk_widget_show_all(parent); + else if (dockside == "bottom") ShowDevToolsInPane(true); + else if (dockside == "right") ShowDevToolsInPane(false); + } } void InspectableWebContentsViewLinux::CloseDevTools() { - // TODO - fprintf(stderr, "InspectableWebContentsViewLinux::CloseDevTools\n"); + GtkWidget *devtools = inspectable_web_contents()->devtools_web_contents()->GetView()->GetNativeView(); + GtkWidget *parent = gtk_widget_get_parent(devtools); + + DLOG(INFO) << base::StringPrintf("InspectableWebContentsViewLinux::CloseDevTools - parent=%s@%p window=%p dockside=\"%s\"", + g_type_name_from_instance((GTypeInstance*)parent), parent, devtools_window, dockside.c_str()); + + if (!parent) { + return; // Not visible -> nothing to do + } + else if (GTK_IS_PANED(parent)) { + GtkWidget *browser = GetBrowserWindow(); + GtkWidget *view = GetNativeView(); + + if (!devtools_window) MakeDevToolsWindow(); + gtk_widget_reparent(devtools, devtools_window); + g_object_ref(parent); + gtk_container_remove(GTK_CONTAINER(browser), parent); + gtk_widget_reparent(view, browser); + g_object_unref(parent); + } + else { + DCHECK(parent == devtools_window); + gtk_widget_hide(parent); + } } bool InspectableWebContentsViewLinux::SetDockSide(const std::string& side) { - // TODO - fprintf(stderr, "InspectableWebContentsViewLinux::SetDockSide\n"); - return false; + DLOG(INFO) << "InspectableWebContentsViewLinux::SetDockSide: \"" << side << "\""; + if (side != "undocked" && side != "bottom" && side != "right") return false; // unsupported display location + if (dockside == side) return true; // no change from current location + + dockside = side; + + // If devtools already has a parent, then we're being asked to move it. + GtkWidget *devtools = inspectable_web_contents()->devtools_web_contents()->GetView()->GetNativeView(); + if (gtk_widget_get_parent(devtools)) { + ShowDevTools(); + } + + return true; +} + +void InspectableWebContentsViewLinux::ShowDevToolsInWindow() { + GtkWidget *devtools = inspectable_web_contents()->devtools_web_contents()->GetView()->GetNativeView(); + GtkWidget *parent = gtk_widget_get_parent(devtools); + + if (!devtools_window) MakeDevToolsWindow(); + if (!parent) { + gtk_container_add(GTK_CONTAINER(devtools_window), devtools); + } + else if (parent != devtools_window) { + DCHECK(GTK_IS_PANED(parent)); + gtk_widget_reparent(devtools, devtools_window); + + // Remove the pane. + GtkWidget *view = GetNativeView(); + GtkWidget *browser = GetBrowserWindow(); + g_object_ref(view); + gtk_container_remove(GTK_CONTAINER(parent), view); + gtk_container_remove(GTK_CONTAINER(browser), parent); + gtk_container_add(GTK_CONTAINER(browser), view); + g_object_unref(view); + } + gtk_widget_show_all(devtools_window); +} + +void InspectableWebContentsViewLinux::MakeDevToolsWindow() { + DCHECK(!devtools_window); + devtools_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(devtools_window), "Developer Tools"); + gtk_window_set_default_size(GTK_WINDOW(devtools_window), 800, 600); + g_signal_connect(GTK_OBJECT(devtools_window), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), this); +} + +void InspectableWebContentsViewLinux::ShowDevToolsInPane(bool on_bottom) { + GtkWidget *devtools = inspectable_web_contents()->devtools_web_contents()->GetView()->GetNativeView(); + GtkWidget *parent = gtk_widget_get_parent(devtools); + GtkWidget *pane = on_bottom ? gtk_vpaned_new() : gtk_hpaned_new(); + GtkWidget *view = GetNativeView(); + GtkWidget *browser = GetBrowserWindow(); + + GtkAllocation alloc; + gtk_widget_get_allocation(browser, &alloc); + gtk_paned_set_position(GTK_PANED(pane), on_bottom ? alloc.height*2/3 : alloc.width/2); + if (!parent) { + g_object_ref(view); + gtk_container_remove(GTK_CONTAINER(browser), view); + gtk_paned_add1(GTK_PANED(pane), view); + gtk_paned_add2(GTK_PANED(pane), devtools); + g_object_unref(view); + } + else if (GTK_IS_PANED(parent)) { + g_object_ref(view); + g_object_ref(devtools); + gtk_container_remove(GTK_CONTAINER(parent), view); + gtk_container_remove(GTK_CONTAINER(parent), devtools); + gtk_paned_add1(GTK_PANED(pane), view); + gtk_paned_add2(GTK_PANED(pane), devtools); + g_object_unref(view); + g_object_unref(devtools); + gtk_container_remove(GTK_CONTAINER(browser), parent); + } + else { + DCHECK(parent == devtools_window); + g_object_ref(view); + gtk_container_remove(GTK_CONTAINER(devtools_window), devtools); + gtk_container_remove(GTK_CONTAINER(browser), view); + gtk_paned_add1(GTK_PANED(pane), view); + gtk_paned_add2(GTK_PANED(pane), devtools); + g_object_unref(view); + gtk_widget_hide(devtools_window); + } + gtk_container_add(GTK_CONTAINER(browser), pane); + gtk_widget_show_all(pane); +} + +GtkWidget *InspectableWebContentsViewLinux::GetBrowserWindow() { + GtkWidget *view = GetNativeView(); + GtkWidget *parent = gtk_widget_get_parent(view); + GtkWidget *browser = GTK_IS_PANED(parent) ? gtk_widget_get_parent(parent) : parent; + DCHECK(GTK_IS_WINDOW(browser)); + return browser; } } diff --git a/brightray/browser/linux/inspectable_web_contents_view_linux.h b/brightray/browser/linux/inspectable_web_contents_view_linux.h index 832481a0572..f0b89929804 100644 --- a/brightray/browser/linux/inspectable_web_contents_view_linux.h +++ b/brightray/browser/linux/inspectable_web_contents_view_linux.h @@ -25,6 +25,27 @@ private: // Owns us. InspectableWebContentsImpl* inspectable_web_contents_; + std::string dockside; + GtkWidget *devtools_window; + + // Show the dev tools in their own window. If they're already shown + // somewhere else, remove them cleanly and take any GtkPaned out of the + // window. + void ShowDevToolsInWindow(); + + // Show the dev tools in a vpaned (on the bottom) or hpaned (on the + // right). If they're already shown in a pane, move them and remove the + // old pane. If they're already shown in a window, hide (don't delete) + // that window. + void ShowDevToolsInPane(bool on_bottom); + + // Create a new window for dev tools. This function doesn't actually + // put the dev tools in the window or show the window. + void MakeDevToolsWindow(); + + // Get the GtkWindow* that contains this object. + GtkWidget *GetBrowserWindow(); + DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsViewLinux); };