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
Esse commit está contido em:
estade@chromium.org
2009-04-02 22:30:26 +00:00
commit 879d157762
9 arquivos alterados com 212 adições e 31 exclusões
+18 -6
Ver Arquivo
@@ -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();
}
+3 -2
Ver Arquivo
@@ -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.
@@ -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() { }
@@ -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 =
@@ -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_
+12 -1
Ver Arquivo
@@ -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
+22
Ver Arquivo
@@ -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));
}
+10
Ver Arquivo
@@ -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);
};
+22 -9
Ver Arquivo
@@ -6,17 +6,11 @@
#include <stdio.h>
#include <string.h>
/* Small program to dump the contents of GTK's clipboard to the terminal.
* Feel free to add to it or improve formatting or whatnot.
*/
int main(int argc, char* argv[]) {
gtk_init(&argc, &argv);
namespace {
void PrintClipboardContents(GtkClipboard* clip) {
GdkAtom* targets;
int num_targets = 0;
// For now only look at GDK_SELECTION_CLIPBOARD. This could be extended
// to look at GDK_SELECTION_PRIMARY (the X clipboard).
GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_wait_for_targets(clip, &targets, &num_targets);
printf("Available targets:\n---------------\n");
@@ -34,6 +28,10 @@ int main(int argc, char* argv[]) {
if (strstr(gdk_atom_name(targets[i]), "image")) {
printf("(image omitted)\n\n");
continue;
} else if (strstr(gdk_atom_name(targets[i]), "TIMESTAMP")) {
// TODO(estade): Print the time stamp in human readable format.
printf("(time omitted)\n\n");
continue;
}
for (int j = 0; j < data->length; j++) {
@@ -44,3 +42,18 @@ int main(int argc, char* argv[]) {
printf("\n\n");
}
}
}
/* Small program to dump the contents of GTK's clipboards to the terminal.
* Feel free to add to it or improve formatting or whatnot.
*/
int main(int argc, char* argv[]) {
gtk_init(&argc, &argv);
printf("Desktop clipboard\n");
PrintClipboardContents(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
printf("X clipboard\n");
PrintClipboardContents(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
}