Commit 7e794388 authored by pkotwicz@chromium.org's avatar pkotwicz@chromium.org

Enable copying long text to Chrome and pasting long text from Chrome

This CL adds support for the INCR property
(http://tronche.com/gui/x/icccm/sec-2.html#s-2.7.2) to SelectionRequestor and
SelectionOwner

BUG=367549
TEST=Manual, see bug
R=derat,dcheng,erg
TBR=erg for ui/base/BUILD.gn

Review URL: https://codereview.chromium.org/397223002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287404 0039d316-1c4b-4281-b951-d872f2087c98
parent 7afe6559
......@@ -256,6 +256,8 @@ component("base") {
"window_open_disposition.cc",
"window_open_disposition.h",
"work_area_watcher_observer.h",
"x/x11_foreign_window_manager.cc",
"x/x11_foreign_window_manager.h",
"x/x11_menu_list.cc",
"x/x11_menu_list.h",
"x/x11_util.cc",
......@@ -331,6 +333,8 @@ component("base") {
}
if (!use_x11) {
sources -= [
"x/x11_foreign_window_manager.cc",
"x/x11_foreign_window_manager.h",
"x/x11_menu_list.cc",
"x/x11_menu_list.h",
"x/x11_util.cc",
......
......@@ -513,7 +513,15 @@ void Clipboard::AuraX11Details::StoreCopyPasteDataAndWait() {
}
bool Clipboard::AuraX11Details::CanDispatchEvent(const PlatformEvent& event) {
return event->xany.window == x_window_;
if (event->xany.window == x_window_)
return true;
if (event->type == PropertyNotify) {
return primary_owner_.CanDispatchPropertyEvent(*event) ||
clipboard_owner_.CanDispatchPropertyEvent(*event) ||
selection_requestor_.CanDispatchPropertyEvent(*event);
}
return false;
}
uint32_t Clipboard::AuraX11Details::DispatchEvent(const PlatformEvent& xev) {
......@@ -544,6 +552,15 @@ uint32_t Clipboard::AuraX11Details::DispatchEvent(const PlatformEvent& xev) {
}
break;
}
case PropertyNotify: {
if (primary_owner_.CanDispatchPropertyEvent(*xev))
primary_owner_.OnPropertyEvent(*xev);
if (clipboard_owner_.CanDispatchPropertyEvent(*xev))
clipboard_owner_.OnPropertyEvent(*xev);
if (selection_requestor_.CanDispatchPropertyEvent(*xev))
selection_requestor_.OnPropertyEvent(*xev);
break;
}
default:
break;
}
......
......@@ -371,6 +371,8 @@
'x/selection_requestor.h',
'x/selection_utils.cc',
'x/selection_utils.h',
'x/x11_foreign_window_manager.cc',
'x/x11_foreign_window_manager.h',
'x/x11_menu_list.cc',
'x/x11_menu_list.h',
'x/x11_util.cc',
......
......@@ -4,11 +4,13 @@
#include "ui/base/x/selection_owner.h"
#include <algorithm>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include "base/logging.h"
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_foreign_window_manager.h"
#include "ui/base/x/x11_util.h"
namespace ui {
......@@ -16,18 +18,41 @@ namespace ui {
namespace {
const char kAtomPair[] = "ATOM_PAIR";
const char kIncr[] = "INCR";
const char kMultiple[] = "MULTIPLE";
const char kSaveTargets[] = "SAVE_TARGETS";
const char kTargets[] = "TARGETS";
const char* kAtomsToCache[] = {
kAtomPair,
kIncr,
kMultiple,
kSaveTargets,
kTargets,
NULL
};
// The period of |incremental_transfer_abort_timer_|. Arbitrary but must be <=
// than kIncrementalTransferTimeoutMs.
const int kTimerPeriodMs = 1000;
// The amount of time to wait for the selection requestor to process the data
// sent by the selection owner before aborting an incremental data transfer.
const int kIncrementalTransferTimeoutMs = 10000;
COMPILE_ASSERT(kTimerPeriodMs <= kIncrementalTransferTimeoutMs,
timer_period_must_be_less_or_equal_to_transfer_timeout);
// Returns a conservative max size of the data we can pass into
// XChangeProperty(). Copied from GTK.
size_t GetMaxRequestSize(XDisplay* display) {
long extended_max_size = XExtendedMaxRequestSize(display);
long max_size =
(extended_max_size ? extended_max_size : XMaxRequestSize(display)) - 100;
return std::min(static_cast<long>(0x40000),
std::max(static_cast<long>(0), max_size));
}
// Gets the value of an atom pair array property. On success, true is returned
// and the value is stored in |value|.
bool GetAtomPairArrayProperty(XID window,
......@@ -78,6 +103,7 @@ SelectionOwner::SelectionOwner(XDisplay* x_display,
: x_display_(x_display),
x_window_(x_window),
selection_name_(selection_name),
max_request_size_(GetMaxRequestSize(x_display)),
atom_cache_(x_display_, kAtomsToCache) {
}
......@@ -173,6 +199,22 @@ void SelectionOwner::OnSelectionClear(const XEvent& event) {
// we need to delay clearing.
}
bool SelectionOwner::CanDispatchPropertyEvent(const XEvent& event) {
return event.xproperty.state == PropertyDelete &&
FindIncrementalTransferForEvent(event) != incremental_transfers_.end();
}
void SelectionOwner::OnPropertyEvent(const XEvent& event) {
std::vector<IncrementalTransfer>::iterator it =
FindIncrementalTransferForEvent(event);
if (it == incremental_transfers_.end())
return;
ProcessIncrementalTransfer(&(*it));
if (!it->data.get())
CompleteIncrementalTransfer(it);
}
bool SelectionOwner::ProcessTarget(XAtom target,
XID requestor,
XAtom property) {
......@@ -201,12 +243,59 @@ bool SelectionOwner::ProcessTarget(XAtom target,
// Try to find the data type in map.
SelectionFormatMap::const_iterator it = format_map_.find(target);
if (it != format_map_.end()) {
XChangeProperty(x_display_, requestor, property, target, 8,
PropModeReplace,
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(
it->second->front())),
it->second->size());
if (it->second->size() > max_request_size_) {
// We must send the data back in several chunks due to a limitation in
// the size of X requests. Notify the selection requestor that the data
// will be sent incrementally by returning data of type "INCR".
int length = it->second->size();
XChangeProperty(x_display_,
requestor,
property,
atom_cache_.GetAtom(kIncr),
32,
PropModeReplace,
reinterpret_cast<unsigned char*>(&length),
1);
// Wait for the selection requestor to indicate that it has processed
// the selection result before sending the first chunk of data. The
// selection requestor indicates this by deleting |property|.
base::TimeTicks timeout =
base::TimeTicks::Now() +
base::TimeDelta::FromMilliseconds(kIncrementalTransferTimeoutMs);
int foreign_window_manager_id =
ui::XForeignWindowManager::GetInstance()->RequestEvents(
requestor, PropertyChangeMask);
incremental_transfers_.push_back(
IncrementalTransfer(requestor,
target,
property,
it->second,
0,
timeout,
foreign_window_manager_id));
// Start a timer to abort the data transfer in case that the selection
// requestor does not support the INCR property or gets destroyed during
// the data transfer.
if (!incremental_transfer_abort_timer_.IsRunning()) {
incremental_transfer_abort_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kTimerPeriodMs),
this,
&SelectionOwner::AbortStaleIncrementalTransfers);
}
} else {
XChangeProperty(
x_display_,
requestor,
property,
target,
8,
PropModeReplace,
const_cast<unsigned char*>(it->second->front()),
it->second->size());
}
return true;
}
// I would put error logging here, but GTK ignores TARGETS and spams us
......@@ -215,4 +304,81 @@ bool SelectionOwner::ProcessTarget(XAtom target,
return false;
}
void SelectionOwner::ProcessIncrementalTransfer(IncrementalTransfer* transfer) {
size_t remaining = transfer->data->size() - transfer->offset;
size_t chunk_length = std::min(remaining, max_request_size_);
XChangeProperty(
x_display_,
transfer->window,
transfer->property,
transfer->target,
8,
PropModeReplace,
const_cast<unsigned char*>(transfer->data->front() + transfer->offset),
chunk_length);
transfer->offset += chunk_length;
transfer->timeout = base::TimeTicks::Now() +
base::TimeDelta::FromMilliseconds(kIncrementalTransferTimeoutMs);
// When offset == data->size(), we still need to transfer a zero-sized chunk
// to notify the selection requestor that the transfer is complete. Clear
// transfer->data once the zero-sized chunk is sent to indicate that state
// related to this data transfer can be cleared.
if (chunk_length == 0)
transfer->data = NULL;
}
void SelectionOwner::AbortStaleIncrementalTransfers() {
base::TimeTicks now = base::TimeTicks::Now();
for (int i = static_cast<int>(incremental_transfers_.size()) - 1;
i >= 0; --i) {
if (incremental_transfers_[i].timeout <= now)
CompleteIncrementalTransfer(incremental_transfers_.begin() + i);
}
}
void SelectionOwner::CompleteIncrementalTransfer(
std::vector<IncrementalTransfer>::iterator it) {
ui::XForeignWindowManager::GetInstance()->CancelRequest(
it->foreign_window_manager_id);
incremental_transfers_.erase(it);
if (incremental_transfers_.empty())
incremental_transfer_abort_timer_.Stop();
}
std::vector<SelectionOwner::IncrementalTransfer>::iterator
SelectionOwner::FindIncrementalTransferForEvent(const XEvent& event) {
for (std::vector<IncrementalTransfer>::iterator it =
incremental_transfers_.begin();
it != incremental_transfers_.end();
++it) {
if (it->window == event.xproperty.window &&
it->property == event.xproperty.atom) {
return it;
}
}
return incremental_transfers_.end();
}
SelectionOwner::IncrementalTransfer::IncrementalTransfer(
XID window,
XAtom target,
XAtom property,
const scoped_refptr<base::RefCountedMemory>& data,
int offset,
base::TimeTicks timeout,
int foreign_window_manager_id)
: window(window),
target(target),
property(property),
data(data),
offset(offset),
timeout(timeout),
foreign_window_manager_id(foreign_window_manager_id) {
}
SelectionOwner::IncrementalTransfer::~IncrementalTransfer() {
}
} // namespace ui
......@@ -9,6 +9,9 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/ref_counted_memory.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/base/ui_base_export.h"
#include "ui/base/x/selection_utils.h"
#include "ui/gfx/x/x11_atom_cache.h"
......@@ -45,15 +48,67 @@ class UI_BASE_EXPORT SelectionOwner {
// It is our owner's responsibility to plumb X11 events on |xwindow_| to us.
void OnSelectionRequest(const XEvent& event);
void OnSelectionClear(const XEvent& event);
// TODO(erg): Do we also need to follow PropertyNotify events? We currently
// don't, but there were open todos in the previous implementation.
// Returns true if SelectionOwner can process the XPropertyEvent event,
// |event|.
bool CanDispatchPropertyEvent(const XEvent& event);
void OnPropertyEvent(const XEvent& event);
private:
// Holds state related to an incremental data transfer.
struct IncrementalTransfer {
IncrementalTransfer(XID window,
XAtom target,
XAtom property,
const scoped_refptr<base::RefCountedMemory>& data,
int offset,
base::TimeTicks timeout,
int foreign_window_manager_id);
~IncrementalTransfer();
// Parameters from the XSelectionRequest. The data is transferred over
// |property| on |window|.
XID window;
XAtom target;
XAtom property;
// The data to be transferred.
scoped_refptr<base::RefCountedMemory> data;
// The offset from the beginning of |data| of the first byte to be
// transferred in the next chunk.
size_t offset;
// Time when the transfer should be aborted because the selection requestor
// is taking too long to notify us that we can send the next chunk.
base::TimeTicks timeout;
// Used to unselect PropertyChangeMask on |window| when we are done with
// the data transfer.
int foreign_window_manager_id;
};
// Attempts to convert the selection to |target|. If the conversion is
// successful, true is returned and the result is stored in the |property|
// of |requestor|.
bool ProcessTarget(XAtom target, XID requestor, XAtom property);
// Sends the next chunk of data for given the incremental data transfer.
void ProcessIncrementalTransfer(IncrementalTransfer* transfer);
// Aborts any incremental data transfers which have timed out.
void AbortStaleIncrementalTransfers();
// Called when the transfer at |it| has completed to do cleanup.
void CompleteIncrementalTransfer(
std::vector<IncrementalTransfer>::iterator it);
// Returns the incremental data transfer, if any, which was waiting for
// |event|.
std::vector<IncrementalTransfer>::iterator FindIncrementalTransferForEvent(
const XEvent& event);
// Our X11 state.
XDisplay* x_display_;
XID x_window_;
......@@ -61,9 +116,17 @@ class UI_BASE_EXPORT SelectionOwner {
// The X11 selection that this instance communicates on.
XAtom selection_name_;
// The maximum size of data we can put in XChangeProperty().
size_t max_request_size_;
// The data we are currently serving.
SelectionFormatMap format_map_;
std::vector<IncrementalTransfer> incremental_transfers_;
// Used to abort stale incremental data transfers.
base::RepeatingTimer<SelectionOwner> incremental_transfer_abort_timer_;
X11AtomCache atom_cache_;
DISALLOW_COPY_AND_ASSIGN(SelectionOwner);
......
......@@ -19,14 +19,44 @@ namespace ui {
namespace {
const char kChromeSelection[] = "CHROME_SELECTION";
const char kIncr[] = "INCR";
const char* kAtomsToCache[] = {
kChromeSelection,
kIncr,
NULL
};
// The amount of time to wait for a request to complete.
const int kRequestTimeoutMs = 300;
// The period of |abort_timer_|. Arbitrary but must be <= than
// kRequestTimeoutMs.
const int kTimerPeriodMs = 100;
// The amount of time to wait for a request to complete before aborting it.
const int kRequestTimeoutMs = 10000;
COMPILE_ASSERT(kTimerPeriodMs <= kRequestTimeoutMs,
timer_period_must_be_less_or_equal_to_request_timeout);
// Combines |data| into a single RefCountedMemory object.
scoped_refptr<base::RefCountedMemory> CombineRefCountedMemory(
const std::vector<scoped_refptr<base::RefCountedMemory> >& data) {
if (data.size() == 1u)
return data[0];
size_t length = 0;
for (size_t i = 0; i < data.size(); ++i)
length += data[i]->size();
std::vector<unsigned char> combined_data;
combined_data.reserve(length);
for (size_t i = 0; i < data.size(); ++i) {
combined_data.insert(combined_data.end(),
data[i]->front(),
data[i]->front() + data[i]->size());
}
return scoped_refptr<base::RefCountedMemory>(
base::RefCountedBytes::TakeVector(&combined_data));
}
} // namespace
......@@ -55,7 +85,7 @@ bool SelectionRequestor::PerformBlockingConvertSelection(
base::TimeDelta::FromMilliseconds(kRequestTimeoutMs);
Request request(selection, target, timeout);
requests_.push_back(&request);
if (requests_.size() == 1u)
if (current_request_index_ == (requests_.size() - 1))
ConvertSelectionForCurrentRequest();
BlockTillSelectionNotifyForRequest(&request);
......@@ -73,7 +103,7 @@ bool SelectionRequestor::PerformBlockingConvertSelection(
if (request.success) {
if (out_data)
*out_data = request.out_data;
*out_data = CombineRefCountedMemory(request.out_data);
if (out_data_items)
*out_data_items = request.out_data_items;
if (out_type)
......@@ -123,40 +153,98 @@ void SelectionRequestor::OnSelectionNotify(const XEvent& event) {
return;
}
request->success = false;
bool success = false;
if (event_property == x_property_) {
request->success = ui::GetRawBytesOfProperty(x_window_,
x_property_,
&request->out_data,
&request->out_data_items,
&request->out_type);
scoped_refptr<base::RefCountedMemory> out_data;
success = ui::GetRawBytesOfProperty(x_window_,
x_property_,
&out_data,
&request->out_data_items,
&request->out_type);
if (success) {
request->out_data.clear();
request->out_data.push_back(out_data);
}
}
if (event_property != None)
XDeleteProperty(x_display_, x_window_, event_property);
CompleteRequest(current_request_index_);
if (request->out_type == atom_cache_.GetAtom(kIncr)) {
request->data_sent_incrementally = true;
request->out_data.clear();
request->out_data_items = 0u;
request->out_type = None;
request->timeout = base::TimeTicks::Now() +
base::TimeDelta::FromMilliseconds(kRequestTimeoutMs);
} else {
CompleteRequest(current_request_index_, success);
}
}
bool SelectionRequestor::CanDispatchPropertyEvent(const XEvent& event) {
return event.xproperty.window == x_window_ &&
event.xproperty.atom == x_property_ &&
event.xproperty.state == PropertyNewValue;
}
void SelectionRequestor::OnPropertyEvent(const XEvent& event) {
Request* request = GetCurrentRequest();
if (!request || !request->data_sent_incrementally)
return;
scoped_refptr<base::RefCountedMemory> out_data;
size_t out_data_items = 0u;
Atom out_type = None;
bool success = ui::GetRawBytesOfProperty(x_window_,
x_property_,
&out_data,
&out_data_items,
&out_type);
if (!success) {
CompleteRequest(current_request_index_, false);
return;
}
if (request->out_type != None && request->out_type != out_type) {
CompleteRequest(current_request_index_, false);
return;
}
request->out_data.push_back(out_data);
request->out_data_items += out_data_items;
request->out_type = out_type;
// Delete the property to tell the selection owner to send the next chunk.
XDeleteProperty(x_display_, x_window_, x_property_);
request->timeout = base::TimeTicks::Now() +
base::TimeDelta::FromMilliseconds(kRequestTimeoutMs);
if (out_data->size() == 0u)
CompleteRequest(current_request_index_, true);
}
void SelectionRequestor::AbortStaleRequests() {
base::TimeTicks now = base::TimeTicks::Now();
for (size_t i = current_request_index_;
i < requests_.size() && requests_[i]->timeout <= now;
++i) {
CompleteRequest(i);
for (size_t i = current_request_index_; i < requests_.size(); ++i) {
if (requests_[i]->timeout <= now)
CompleteRequest(i, false);
}
}
void SelectionRequestor::CompleteRequest(size_t index) {
void SelectionRequestor::CompleteRequest(size_t index, bool success) {
if (index >= requests_.size())
return;
Request* request = requests_[index];
if (request->completed)
return;
request->success = success;
request->completed = true;
if (index == current_request_index_) {
++current_request_index_;
while (GetCurrentRequest() && GetCurrentRequest()->completed)
++current_request_index_;
ConvertSelectionForCurrentRequest();
}
......@@ -180,7 +268,7 @@ void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) {
if (PlatformEventSource::GetInstance()) {
if (!abort_timer_.IsRunning()) {
abort_timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kRequestTimeoutMs),
base::TimeDelta::FromMilliseconds(kTimerPeriodMs),
this,
&SelectionRequestor::AbortStaleRequests);
}
......@@ -219,6 +307,7 @@ SelectionRequestor::Request::Request(XAtom selection,
base::TimeTicks timeout)
: selection(selection),
target(target),
data_sent_incrementally(false),
out_data_items(0u),
out_type(None),
success(false),
......
......@@ -62,6 +62,12 @@ class UI_BASE_EXPORT SelectionRequestor {
// |xwindow_| to us.
void OnSelectionNotify(const XEvent& event);
// Returns true if SelectionOwner can process the XChangeProperty event,
// |event|.
bool CanDispatchPropertyEvent(const XEvent& event);
void OnPropertyEvent(const XEvent& event);
private:
friend class SelectionRequestorTest;
......@@ -75,8 +81,12 @@ class UI_BASE_EXPORT SelectionRequestor {
XAtom selection;
XAtom target;
// Whether the result of the XConvertSelection() request is being sent
// incrementally.
bool data_sent_incrementally;
// The result data for the XConvertSelection() request.
scoped_refptr<base::RefCountedMemory> out_data;
std::vector<scoped_refptr<base::RefCountedMemory> > out_data;
size_t out_data_items;
XAtom out_type;
......@@ -98,7 +108,7 @@ class UI_BASE_EXPORT SelectionRequestor {
// Mark |request| as completed. If the current request is completed, converts
// the selection for the next request.
void CompleteRequest(size_t index);
void CompleteRequest(size_t index, bool success);
// Converts the selection for the request at |current_request_index_|.
void ConvertSelectionForCurrentRequest();
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/x/x11_foreign_window_manager.h"
#include <X11/Xlib.h>
#include "base/memory/singleton.h"
namespace ui {
// static
XForeignWindowManager* XForeignWindowManager::GetInstance() {
return Singleton<XForeignWindowManager>::get();
}
int XForeignWindowManager::RequestEvents(XID xid, long event_mask) {
request_map_[xid].push_back(Request(next_request_id_, event_mask));
UpdateSelectedEvents(xid);
return next_request_id_++;
}
void XForeignWindowManager::CancelRequest(int request_id) {
for (std::map<XID, RequestVector>::iterator map_it = request_map_.begin();
map_it != request_map_.end(); ++map_it) {
RequestVector* vector = &map_it->second;
for (RequestVector::iterator vector_it = vector->begin();
vector_it != vector->end(); ++vector_it) {
if (vector_it->request_id == request_id) {
vector->erase(vector_it);
UpdateSelectedEvents(map_it->first);
if (vector->empty())
request_map_.erase(map_it);
return;
}
}
}
}
void XForeignWindowManager::OnWindowDestroyed(XID xid) {
request_map_.erase(xid);
}
XForeignWindowManager::XForeignWindowManager() : next_request_id_(0) {
}
XForeignWindowManager::~XForeignWindowManager() {
}
void XForeignWindowManager::UpdateSelectedEvents(XID xid) {
std::map<XID, RequestVector>::iterator it = request_map_.find(xid);
if (it == request_map_.end())
return;
long event_mask = NoEventMask;
const RequestVector& list = it->second;
for (size_t i = 0; i < list.size(); ++i)
event_mask |= list[i].event_mask;
XSelectInput(gfx::GetXDisplay(), xid, event_mask);
}
XForeignWindowManager::Request::Request(int request_id, long event_mask)
: request_id(request_id),
event_mask(event_mask) {
}
XForeignWindowManager::Request::~Request() {
}
} // namespace ui
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_X_X11_FOREIGN_WINDOW_MANAGER_H_
#define UI_BASE_X_X11_FOREIGN_WINDOW_MANAGER_H_
#include <map>
#include <vector>
#include "base/basictypes.h"
#include "ui/base/ui_base_export.h"
#include "ui/gfx/x/x11_types.h"
// A process wide singleton for selecting events on X windows which were not
// created by Chrome.
template <typename T> struct DefaultSingletonTraits;
namespace ui {
// Manages the events that Chrome has selected on X windows which were not
// created by Chrome. This class allows several clients to select events
// on the same X window.
class UI_BASE_EXPORT XForeignWindowManager {
public:
static XForeignWindowManager* GetInstance();
// Requests that events associated with |event_mask| on |xid| be reported to
// Chrome. Returns an id to use for canceling the request.
int RequestEvents(XID xid, long event_mask) WARN_UNUSED_RESULT;
// Cancels the request with |request_id|. Unless there is another request for
// events on the X window associated with |request_id|, this stops Chrome from
// getting events for the X window.
void CancelRequest(int request_id);
// Called by X11DesktopHandler when |xid| is destroyed.
void OnWindowDestroyed(XID xid);
private:
friend struct DefaultSingletonTraits<XForeignWindowManager>;
struct Request {
Request(int request_id, long entry_event_mask);
~Request();
int request_id;
long event_mask;
};
XForeignWindowManager();
~XForeignWindowManager();
// Updates which events are selected on |xid|.
void UpdateSelectedEvents(XID xid);
// The id of the next request.
int next_request_id_;
typedef std::vector<Request> RequestVector;
std::map<XID, RequestVector> request_map_;
DISALLOW_COPY_AND_ASSIGN(XForeignWindowManager);
};
} // namespace ui
#endif // UI_BASE_X_X11_FOREIGN_WINDOW_MANAGER_H_
......@@ -16,6 +16,7 @@
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_foreign_window_manager.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/event.h"
#include "ui/events/platform/platform_event_source.h"
......@@ -141,6 +142,14 @@ class DesktopDragDropClientAuraX11::X11DragContext
// The XID of the window that's initiated the drag.
unsigned long source_window_;
// The DesktopDragDropClientAuraX11 for |source_window_| if |source_window_|
// belongs to a Chrome window.
DesktopDragDropClientAuraX11* source_client_;
// Used to unselect PropertyChangeMask on |source_window_| if |source_window_|
// does not belong to a Chrome window when X11DragContext is destroyed.
int foreign_window_manager_source_window_id_;
// The client we inform once we're done with requesting data.
DesktopDragDropClientAuraX11* drag_drop_client_;
......@@ -175,6 +184,9 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
: atom_cache_(atom_cache),
local_window_(local_window),
source_window_(event.data.l[0]),
source_client_(
DesktopDragDropClientAuraX11::GetForWindow(source_window_)),
foreign_window_manager_source_window_id_(0),
drag_drop_client_(NULL),
waiting_to_handle_position_(false),
suggested_action_(None) {
......@@ -195,13 +207,13 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
}
}
DesktopDragDropClientAuraX11* client =
DesktopDragDropClientAuraX11::GetForWindow(source_window_);
if (!client) {
if (!source_client_) {
// The window doesn't have a DesktopDragDropClientAuraX11, that means it's
// created by some other process. Listen for messages on it.
ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
foreign_window_manager_source_window_id_ =
ui::XForeignWindowManager::GetInstance()->RequestEvents(
source_window_, PropertyChangeMask);
// We must perform a full sync here because we could be racing
// |source_window_|.
......@@ -210,7 +222,7 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
// This drag originates from an aura window within our process. This means
// that we can shortcut the X11 server and ask the owning SelectionOwner
// for the data it's offering.
fetched_targets_ = client->GetFormatMap();
fetched_targets_ = source_client_->GetFormatMap();
unfetched_targets_.clear();
}
......@@ -218,8 +230,12 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
}
DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
// Unsubscribe from message events.
ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
if (!source_client_) {
// Unsubscribe from message events.
ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
ui::XForeignWindowManager::GetInstance()->CancelRequest(
foreign_window_manager_source_window_id_);
}
}
void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
......@@ -283,9 +299,7 @@ void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
}
void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
DesktopDragDropClientAuraX11* client =
DesktopDragDropClientAuraX11::GetForWindow(source_window_);
if (!client) {
if (!source_client_) {
std::vector<Atom> atom_array;
if (!ui::GetAtomArrayProperty(source_window_,
"XdndActionList",
......@@ -298,7 +312,7 @@ void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
// We have a property notify set up for other windows in case they change
// their action list. Thankfully, the views interface is static and you
// can't change the action list after you enter StartDragAndDrop().
actions_ = client->GetOfferedDragOperations();
actions_ = source_client_->GetOfferedDragOperations();
}
}
......
......@@ -10,6 +10,7 @@
#include "base/message_loop/message_loop.h"
#include "ui/aura/env.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/x/x11_foreign_window_manager.h"
#include "ui/base/x/x11_menu_list.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/platform/platform_event_source.h"
......@@ -167,26 +168,12 @@ uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent& event) {
break;
}
// Menus created by Chrome can be drag and drop targets. Since they are
// direct children of the screen root window and have override_redirect
// we cannot use regular _NET_CLIENT_LIST_STACKING property to find them
// and use a separate cache to keep track of them.
// TODO(varkha): Implement caching of all top level X windows and their
// coordinates and stacking order to eliminate repeated calls to X server
// during mouse movement, drag and shaping events.
case CreateNotify: {
// The window might be destroyed if the message pump haven't gotten a
// chance to run but we can safely ignore the X error.
gfx::X11ErrorTracker error_tracker;
XCreateWindowEvent *xcwe = &event->xcreatewindow;
ui::XMenuList::GetInstance()->MaybeRegisterMenu(xcwe->window);
case CreateNotify:
OnWindowCreatedOrDestroyed(event->type, event->xcreatewindow.window);
break;
}
case DestroyNotify: {
XDestroyWindowEvent *xdwe = &event->xdestroywindow;
ui::XMenuList::GetInstance()->MaybeUnregisterMenu(xdwe->window);
case DestroyNotify:
OnWindowCreatedOrDestroyed(event->type, event->xdestroywindow.window);
break;
}
default:
NOTREACHED();
}
......@@ -226,4 +213,28 @@ void X11DesktopHandler::OnActiveWindowChanged(::Window xid,
}
}
void X11DesktopHandler::OnWindowCreatedOrDestroyed(int event_type,
XID window) {
// Menus created by Chrome can be drag and drop targets. Since they are
// direct children of the screen root window and have override_redirect
// we cannot use regular _NET_CLIENT_LIST_STACKING property to find them
// and use a separate cache to keep track of them.
// TODO(varkha): Implement caching of all top level X windows and their
// coordinates and stacking order to eliminate repeated calls to the X server
// during mouse movement, drag and shaping events.
if (event_type == CreateNotify) {
// The window might be destroyed if the message pump did not get a chance to
// run but we can safely ignore the X error.
gfx::X11ErrorTracker error_tracker;
ui::XMenuList::GetInstance()->MaybeRegisterMenu(window);
} else {
ui::XMenuList::GetInstance()->MaybeUnregisterMenu(window);
}
if (event_type == DestroyNotify) {
// Notify the XForeignWindowManager that |window| has been destroyed.
ui::XForeignWindowManager::GetInstance()->OnWindowDestroyed(window);
}
}
} // namespace views
......@@ -76,6 +76,10 @@ class VIEWS_EXPORT X11DesktopHandler : public ui::PlatformEventDispatcher,
// Handles changes in activation.
void OnActiveWindowChanged(::Window window, ActiveState active_state);
// Called when |window| has been created or destroyed. |window| may not be
// managed by Chrome.
void OnWindowCreatedOrDestroyed(int event_type, XID window);
// The display and the native X window hosting the root window.
XDisplay* xdisplay_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment