diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 22:30:26 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 22:30:26 +0000 |
commit | 2e417c8257e0007ef3fb6929a507268d0450bd4a (patch) | |
tree | 756cbb26e6225ef36abddb2cf59a73800587dcbd /chrome | |
parent | 0cac83aa1536b90dd25db93808c4371c675e4cf2 (diff) | |
download | chromium_src-2e417c8257e0007ef3fb6929a507268d0450bd4a.zip chromium_src-2e417c8257e0007ef3fb6929a507268d0450bd4a.tar.gz chromium_src-2e417c8257e0007ef3fb6929a507268d0450bd4a.tar.bz2 |
Copy selection to x clipboard.
Review URL: http://codereview.chromium.org/55052
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13044 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/renderer_host/render_view_host.cc | 24 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_view_host.h | 5 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view.h | 6 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_gtk.cc | 105 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_gtk.h | 27 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 13 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 22 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 10 |
8 files changed, 190 insertions, 22 deletions
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc index 84994bb..83c55a7 100644 --- a/chrome/browser/renderer_host/render_view_host.cc +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -772,8 +772,10 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { OnRemoveAutofillEntry) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFeedList, OnMsgUpdateFeedList) IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionRequest, OnExtensionRequest) + IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionChanged, OnMsgSelectionChanged) + IPC_MESSAGE_HANDLER(ViewHostMsg_SetSelectionText, OnMsgSetSelectionText) IPC_MESSAGE_HANDLER(ViewHostMsg_PasteFromSelectionClipboard, - OnPasteFromSelectionClipboard) + OnMsgPasteFromSelectionClipboard) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(RenderWidgetHost::OnMessageReceived(msg)) IPC_END_MESSAGE_MAP_EX() @@ -1094,6 +1096,21 @@ void RenderViewHost::OnMsgSetTooltipText(const std::wstring& tooltip_text) { view()->SetTooltipText(tooltip_text); } +void RenderViewHost::OnMsgSelectionChanged() { + if (view()) + view()->SelectionChanged(); +} + +void RenderViewHost::OnMsgSetSelectionText(const std::string& text) { + if (view()) + view()->SetSelectionText(text); +} + +void RenderViewHost::OnMsgPasteFromSelectionClipboard() { + if (view()) + view()->PasteFromSelectionClipboard(); +} + void RenderViewHost::OnMsgRunFileChooser(bool multiple_files, const std::wstring& title, const std::wstring& default_file, @@ -1366,8 +1383,3 @@ void RenderViewHost::SendExtensionResponse(int callback_id, const std::string& response) { Send(new ViewMsg_ExtensionResponse(routing_id(), callback_id, response)); } - -void RenderViewHost::OnPasteFromSelectionClipboard() { - if (view()) - view()->PasteFromSelectionClipboard(); -} diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h index c150259..f7a63a5 100644 --- a/chrome/browser/renderer_host/render_view_host.h +++ b/chrome/browser/renderer_host/render_view_host.h @@ -506,6 +506,9 @@ class RenderViewHost : public RenderWidgetHost { #endif void OnMsgGoToEntryAtOffset(int offset); void OnMsgSetTooltipText(const std::wstring& tooltip_text); + void OnMsgSelectionChanged(); + void OnMsgSetSelectionText(const std::string& text); + void OnMsgPasteFromSelectionClipboard(); void OnMsgRunFileChooser(bool multiple_files, const std::wstring& title, const std::wstring& default_file, @@ -565,8 +568,6 @@ class RenderViewHost : public RenderWidgetHost { void OnExtensionRequest(const std::string& name, const std::string& args, int callback_id); - void OnPasteFromSelectionClipboard(); - // Helper function to send a navigation message. If a cross-site request is // in progress, we may be suspended while waiting for the onbeforeunload // handler, so this function might buffer the message rather than sending it. diff --git a/chrome/browser/renderer_host/render_widget_host_view.h b/chrome/browser/renderer_host/render_widget_host_view.h index 255f6644..1a9dead 100644 --- a/chrome/browser/renderer_host/render_widget_host_view.h +++ b/chrome/browser/renderer_host/render_widget_host_view.h @@ -112,6 +112,12 @@ class RenderWidgetHostView { // the page has changed. virtual void SetTooltipText(const std::wstring& tooltip_text) = 0; + // Notifies the View that the renderer text selection has changed. + virtual void SelectionChanged() { }; + + // Notifies the View what the current selection text is. + virtual void SetSelectionText(const std::string& text) { }; + // Tells the View to get the text from the selection clipboard and send it // back to the renderer asynchronously. virtual void PasteFromSelectionClipboard() { } diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc index 21b45d1..74eaf57 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc @@ -6,6 +6,7 @@ #include <gtk/gtk.h> #include <gdk/gdk.h> +#include <gdk/gdkx.h> #include <cairo/cairo.h> #include "base/logging.h" @@ -16,13 +17,10 @@ #include "chrome/browser/renderer_host/backing_store.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "third_party/WebKit/WebKit/chromium/public/gtk/WebInputEventFactory.h" +#include "webkit/glue/webcursor_gtk_data.h" using WebKit::WebInputEventFactory; -namespace { - -#include "webkit/glue/webcursor_gtk_data.h" - // This class is a simple convenience wrapper for Gtk functions. It has only // static methods. class RenderWidgetHostViewGtkWidget { @@ -48,9 +46,9 @@ class RenderWidgetHostViewGtkWidget { g_signal_connect(widget, "key-release-event", G_CALLBACK(KeyPressReleaseEvent), host_view); g_signal_connect(widget, "focus-in-event", - G_CALLBACK(FocusIn), host_view); + G_CALLBACK(OnFocusIn), host_view); g_signal_connect(widget, "focus-out-event", - G_CALLBACK(FocusOut), host_view); + G_CALLBACK(OnFocusOut), host_view); g_signal_connect(widget, "button-press-event", G_CALLBACK(ButtonPressReleaseEvent), host_view); g_signal_connect(widget, "button-release-event", @@ -60,6 +58,34 @@ class RenderWidgetHostViewGtkWidget { g_signal_connect(widget, "scroll-event", G_CALLBACK(MouseScrollEvent), host_view); + GtkTargetList* target_list = gtk_target_list_new(NULL, 0); + gtk_target_list_add_text_targets(target_list, 0); + gint num_targets = 0; + GtkTargetEntry* targets = gtk_target_table_new_from_list(target_list, + &num_targets); + gtk_selection_clear_targets(widget, GDK_SELECTION_PRIMARY); + gtk_selection_add_targets(widget, GDK_SELECTION_PRIMARY, targets, + num_targets); + gtk_target_table_free(targets, num_targets); + + // When X requests the contents of the clipboard, GTK will emit the + // selection_request_event signal. The default handler would then + // synchronously emit the selection_get signal. However, we want to + // respond to the selection_request_event asynchronously, so we intercept + // the signal in OnSelectionRequest, request the selection text from the + // render view, and return TRUE so the default handler won't be called. Then + // when we get the selection text back from the renderer in + // SetSelectionText() we will call manually the selection_request_event + // default handler. + g_signal_connect(widget, "selection_request_event", + G_CALLBACK(OnSelectionRequest), host_view); + g_signal_connect(widget, "selection_get", + G_CALLBACK(OnSelectionGet), host_view); + + // In OnSelectionGet, we need to access |host_view| to get the selection + // text. + g_object_set_data(G_OBJECT(widget), "render-widget-host-view-gtk", + host_view); return widget; } @@ -87,13 +113,13 @@ class RenderWidgetHostViewGtkWidget { return TRUE; } - static gboolean FocusIn(GtkWidget* widget, GdkEventFocus* focus, + static gboolean OnFocusIn(GtkWidget* widget, GdkEventFocus* focus, RenderWidgetHostViewGtk* host_view) { host_view->GetRenderWidgetHost()->Focus(); return FALSE; } - static gboolean FocusOut(GtkWidget* widget, GdkEventFocus* focus, + static gboolean OnFocusOut(GtkWidget* widget, GdkEventFocus* focus, RenderWidgetHostViewGtk* host_view) { // Whenever we lose focus, set the cursor back to that of our parent window, // which should be the default arrow. @@ -130,17 +156,49 @@ class RenderWidgetHostViewGtkWidget { return FALSE; } + + static gboolean OnSelectionRequest(GtkWidget* widget, + GdkEventSelection* event) { + RenderWidgetHostViewGtk* host_view = + reinterpret_cast<RenderWidgetHostViewGtk*>( + g_object_get_data(G_OBJECT(widget), "render-widget-host-view-gtk")); + + // If we already know the selection text, return FALSE to let the default + // handler run. Also, don't try to handle two events simultaneously, + // because we might end up sending the wrong |event_selection_| back to GTK. + if (!host_view->selection_text_.empty() || + host_view->event_selection_active_) + return FALSE; + + host_view->event_selection_ = *event; + host_view->event_selection_active_ = true; + if (host_view->selection_text_.empty()) + host_view->RequestSelectionText(); + + return TRUE; + } + + static void OnSelectionGet(GtkWidget* widget, + GtkSelectionData* data, + guint info, guint time, + RenderWidgetHostViewGtk* host_view) { + DCHECK(!host_view->selection_text_.empty() || + host_view->event_selection_active_); + + gtk_selection_data_set(data, data->target, 8, + reinterpret_cast<const guchar*>(host_view->selection_text_.c_str()), + host_view->selection_text_.length()); + } + DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget); }; -gboolean OnPopupParentFocusOut(GtkWidget* parent, GdkEventFocus* focus, - RenderWidgetHost* host) { +static gboolean OnPopupParentFocusOut(GtkWidget* parent, GdkEventFocus* focus, + RenderWidgetHost* host) { host->Shutdown(); return FALSE; } -} // namespace - // static RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( RenderWidgetHost* widget) { @@ -153,7 +211,8 @@ RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) parent_(NULL), popup_signal_id_(0), activatable_(true), - is_loading_(false) { + is_loading_(false), + event_selection_active_(false) { host_->set_view(this); } @@ -308,6 +367,22 @@ void RenderWidgetHostViewGtk::SetTooltipText(const std::wstring& tooltip_text) { } } +void RenderWidgetHostViewGtk::SelectionChanged() { + selection_text_.clear(); + + guint32 timestamp = gdk_x11_get_server_time(view_.get()->window); + gtk_selection_owner_set(view_.get(), GDK_SELECTION_PRIMARY, timestamp); +} + +void RenderWidgetHostViewGtk::SetSelectionText(const std::string& text) { + selection_text_ = text; + DCHECK(event_selection_active_); + event_selection_active_ = false; + // Resume normal handling of the active selection_request_event. + GtkWidgetClass* klass = GTK_WIDGET_CLASS(gtk_type_class(GTK_TYPE_WIDGET)); + klass->selection_request_event(view_.get(), &event_selection_); +} + BackingStore* RenderWidgetHostViewGtk::AllocBackingStore( const gfx::Size& size) { Display* display = x11_util::GetXDisplay(); @@ -387,6 +462,10 @@ void RenderWidgetHostViewGtk::ShowCurrentCursor() { gdk_cursor_unref(gdk_cursor); } +void RenderWidgetHostViewGtk::RequestSelectionText() { + host_->Send(new ViewMsg_RequestSelectionText(host_->routing_id())); +} + void RenderWidgetHostViewGtk::ReceivedSelectionText(GtkClipboard* clipboard, const gchar* text, gpointer userdata) { RenderWidgetHostViewGtk* host_view = diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.h b/chrome/browser/renderer_host/render_widget_host_view_gtk.h index f3fc7ba..5d260a1 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.h +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.h @@ -5,16 +5,19 @@ #ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GTK_H_ #define CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GTK_H_ +#include <gdk/gdk.h> #include <vector> #include "base/gfx/native_widget_types.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/common/owned_widget_gtk.h" +#include "chrome/common/render_messages.h" #include "webkit/glue/webcursor.h" class RenderWidgetHost; typedef struct _GtkClipboard GtkClipboard; +typedef struct _GtkSelectionData GtkSelectionData; // ----------------------------------------------------------------------------- // See comments in render_widget_host_view.h about this class and its members. @@ -58,6 +61,8 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView { void RenderViewGone(); void Destroy(); void SetTooltipText(const std::wstring& tooltip_text); + void SelectionChanged(); + void SetSelectionText(const std::string& text); void PasteFromSelectionClipboard(); BackingStore* AllocBackingStore(const gfx::Size& size); // --------------------------------------------------------------------------- @@ -67,9 +72,20 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView { void Paint(const gfx::Rect&); private: + friend class RenderWidgetHostViewGtkWidget; + + void set_event_selection(GdkEventSelection* event_selection) { + event_selection_ = *event_selection; + event_selection_active_ = true; + } + // Update the display cursor for the render view. void ShowCurrentCursor(); + void RequestSelectionText(); + + // When we've requested the text from the X clipboard, GTK returns it to us + // through this callback. static void ReceivedSelectionText(GtkClipboard* clipboard, const gchar* text, gpointer userdata); @@ -99,6 +115,17 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView { // The cursor for the page. This is passed up from the renderer. WebCursor current_cursor_; + + // We cache the text that is selected on the page. This is used for copying to + // the X clipboard. We update |selection_text_| whenever X asks us for it and + // the cache is empty. We invalidate it (set it to empty) whenever the + // renderer sends a SelectionChanged message. + std::string selection_text_; + + // A struct that keeps state for the XSelectionEvent we are handling (if any). + GdkEventSelection event_selection_; + // Tracks whether we are currently handling an XSelectionEvent. + bool event_selection_active_; }; #endif // CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GTK_H_ diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 5e664c3..cc296a6 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -108,6 +108,10 @@ IPC_BEGIN_MESSAGES(View) // node. IPC_MESSAGE_ROUTED1(ViewMsg_SetInitialFocus, bool /* reverse */) + // Ask the renderer to send us the selection text via the SetSelectionText + // message. + IPC_MESSAGE_ROUTED0(ViewMsg_RequestSelectionText) + // Tells the renderer to perform the specified navigation, interrupting any // existing navigation. IPC_MESSAGE_ROUTED1(ViewMsg_Navigate, ViewMsg_Navigate_Params) @@ -900,6 +904,13 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED1(ViewHostMsg_SetTooltipText, std::wstring /* tooltip text string */) + // Notification that the text selection has changed. + IPC_MESSAGE_ROUTED0(ViewHostMsg_SelectionChanged) + + // Send the current text selection. + IPC_MESSAGE_ROUTED1(ViewHostMsg_SetSelectionText, + std::string /* currently selected text */) + // Asks the browser to display the file chooser. The result is returned in a // ViewHost_RunFileChooserResponse message. IPC_MESSAGE_ROUTED4(ViewHostMsg_RunFileChooser, @@ -1063,7 +1074,7 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED1(ViewHostMsg_CrashedPlugin, FilePath /* plugin_path */) - // Dsiplays a JavaScript out-of-memory message in the infobar. + // Displays a JavaScript out-of-memory message in the infobar. IPC_MESSAGE_ROUTED0(ViewHostMsg_JSOutOfMemory) // Displays a box to confirm that the user wants to navigate away from the diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 1011647..6ed31e2 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -297,6 +297,12 @@ void RenderView::Init(gfx::NativeViewId parent_hwnd, MessageLoop::current()); webwidget_ = WebView::Create(this, webkit_prefs); +#if defined(OS_LINUX) + // We have to enable ourselves as the editor delegate on linux so we can copy + // text selections to the X clipboard. + webview()->SetUseEditorDelegate(true); +#endif + // Don't let WebCore keep a B/F list - we have our own. // We let it keep 1 entry because FrameLoader::goToItem expects an item in the // backForwardList, which is used only in ASSERTs. @@ -425,6 +431,7 @@ void RenderView::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_HandleExtensionMessage, OnHandleExtensionMessage) IPC_MESSAGE_HANDLER(ViewMsg_ExtensionResponse, OnExtensionResponse) + IPC_MESSAGE_HANDLER(ViewMsg_RequestSelectionText, OnRequestSelectionText) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(RenderWidget::OnMessageReceived(message)) @@ -454,6 +461,10 @@ void RenderView::SendThumbnail() { Send(new ViewHostMsg_Thumbnail(routing_id_, url, score, thumbnail)); } +void RenderView::OnRequestSelectionText() { + Send(new ViewHostMsg_SetSelectionText(routing_id_, selection_text_)); +} + void RenderView::PrintPage(const ViewMsg_PrintPage_Params& params, const gfx::Size& canvas_size, WebFrame* frame) { @@ -2422,6 +2433,17 @@ void RenderView::SetTooltipText(WebView* webview, Send(new ViewHostMsg_SetTooltipText(routing_id_, tooltip_text)); } +void RenderView::DidChangeSelection(bool is_empty_selection) { +#if defined(OS_LINUX) + if (!is_empty_selection) { + // TODO(estade): find a way to incrementally update the selection text. + selection_text_ = webview()->GetMainFrame()->GetSelection(false); + Send(new ViewHostMsg_SelectionChanged(routing_id_)); + } +#endif +} + + void RenderView::DownloadUrl(const GURL& url, const GURL& referrer) { Send(new ViewHostMsg_DownloadUrl(routing_id_, url, referrer)); } diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 7f20d60..25692f2 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -280,6 +280,9 @@ class RenderView : public RenderWidget, virtual void OnNavStateChanged(WebView* webview); virtual void SetTooltipText(WebView* webview, const std::wstring& tooltip_text); + // Called when the text selection changed. This is only called on linux since + // on other platforms the RenderView doesn't act as an editor client delegate. + virtual void DidChangeSelection(bool is_empty_selection); virtual void DownloadUrl(const GURL& url, const GURL& referrer); @@ -593,6 +596,9 @@ class RenderView : public RenderWidget, void OnHandleExtensionMessage(const std::string& message, int channel_id); + // Sends the selection text to the browser. + void OnRequestSelectionText(); + // Prints the page listed in |params|. void PrintPage(const ViewMsg_PrintPage_Params& params, const gfx::Size& canvas_size, @@ -815,6 +821,10 @@ class RenderView : public RenderWidget, // Maps pending callback IDs to their frames. IDMap<WebFrame> pending_extension_callbacks_; + // The currently selected text. This is currently only updated on Linux, where + // it's for the selection clipboard. + std::string selection_text_; + DISALLOW_COPY_AND_ASSIGN(RenderView); }; |