| Index: ui/base/x/selection_owner.cc
|
| diff --git a/ui/base/x/selection_owner.cc b/ui/base/x/selection_owner.cc
|
| deleted file mode 100644
|
| index b2b44c9d313a1b1e1bec86b94c9e79e412d4a582..0000000000000000000000000000000000000000
|
| --- a/ui/base/x/selection_owner.cc
|
| +++ /dev/null
|
| @@ -1,384 +0,0 @@
|
| -// Copyright (c) 2013 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/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 {
|
| -
|
| -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,
|
| - XAtom property,
|
| - std::vector<std::pair<XAtom,XAtom> >* value) {
|
| - XAtom type = None;
|
| - int format = 0; // size in bits of each item in 'property'
|
| - unsigned long num_items = 0;
|
| - unsigned char* properties = NULL;
|
| - unsigned long remaining_bytes = 0;
|
| -
|
| - int result = XGetWindowProperty(gfx::GetXDisplay(),
|
| - window,
|
| - property,
|
| - 0, // offset into property data to
|
| - // read
|
| - (~0L), // entire array
|
| - False, // deleted
|
| - AnyPropertyType,
|
| - &type,
|
| - &format,
|
| - &num_items,
|
| - &remaining_bytes,
|
| - &properties);
|
| -
|
| - if (result != Success)
|
| - return false;
|
| -
|
| - // GTK does not require |type| to be kAtomPair.
|
| - if (format != 32 || num_items % 2 != 0) {
|
| - XFree(properties);
|
| - return false;
|
| - }
|
| -
|
| - XAtom* atom_properties = reinterpret_cast<XAtom*>(properties);
|
| - value->clear();
|
| - for (size_t i = 0; i < num_items; i+=2)
|
| - value->push_back(std::make_pair(atom_properties[i], atom_properties[i+1]));
|
| - XFree(properties);
|
| - return true;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -SelectionOwner::SelectionOwner(XDisplay* x_display,
|
| - XID x_window,
|
| - XAtom selection_name)
|
| - : x_display_(x_display),
|
| - x_window_(x_window),
|
| - selection_name_(selection_name),
|
| - max_request_size_(GetMaxRequestSize(x_display)),
|
| - atom_cache_(x_display_, kAtomsToCache) {
|
| -}
|
| -
|
| -SelectionOwner::~SelectionOwner() {
|
| - // If we are the selection owner, we need to release the selection so we
|
| - // don't receive further events. However, we don't call ClearSelectionOwner()
|
| - // because we don't want to do this indiscriminately.
|
| - if (XGetSelectionOwner(x_display_, selection_name_) == x_window_)
|
| - XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
|
| -}
|
| -
|
| -void SelectionOwner::RetrieveTargets(std::vector<XAtom>* targets) {
|
| - for (SelectionFormatMap::const_iterator it = format_map_.begin();
|
| - it != format_map_.end(); ++it) {
|
| - targets->push_back(it->first);
|
| - }
|
| -}
|
| -
|
| -void SelectionOwner::TakeOwnershipOfSelection(
|
| - const SelectionFormatMap& data) {
|
| - XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime);
|
| -
|
| - if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) {
|
| - // The X server agrees that we are the selection owner. Commit our data.
|
| - format_map_ = data;
|
| - }
|
| -}
|
| -
|
| -void SelectionOwner::ClearSelectionOwner() {
|
| - XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
|
| - format_map_ = SelectionFormatMap();
|
| -}
|
| -
|
| -void SelectionOwner::OnSelectionRequest(const XEvent& event) {
|
| - XID requestor = event.xselectionrequest.requestor;
|
| - XAtom requested_target = event.xselectionrequest.target;
|
| - XAtom requested_property = event.xselectionrequest.property;
|
| -
|
| - // Incrementally build our selection. By default this is a refusal, and we'll
|
| - // override the parts indicating success in the different cases.
|
| - XEvent reply;
|
| - reply.xselection.type = SelectionNotify;
|
| - reply.xselection.requestor = requestor;
|
| - reply.xselection.selection = event.xselectionrequest.selection;
|
| - reply.xselection.target = requested_target;
|
| - reply.xselection.property = None; // Indicates failure
|
| - reply.xselection.time = event.xselectionrequest.time;
|
| -
|
| - if (requested_target == atom_cache_.GetAtom(kMultiple)) {
|
| - // The contents of |requested_property| should be a list of
|
| - // <target,property> pairs.
|
| - std::vector<std::pair<XAtom,XAtom> > conversions;
|
| - if (GetAtomPairArrayProperty(requestor,
|
| - requested_property,
|
| - &conversions)) {
|
| - std::vector<XAtom> conversion_results;
|
| - for (size_t i = 0; i < conversions.size(); ++i) {
|
| - bool conversion_successful = ProcessTarget(conversions[i].first,
|
| - requestor,
|
| - conversions[i].second);
|
| - conversion_results.push_back(conversions[i].first);
|
| - conversion_results.push_back(
|
| - conversion_successful ? conversions[i].second : None);
|
| - }
|
| -
|
| - // Set the property to indicate which conversions succeeded. This matches
|
| - // what GTK does.
|
| - XChangeProperty(
|
| - x_display_,
|
| - requestor,
|
| - requested_property,
|
| - atom_cache_.GetAtom(kAtomPair),
|
| - 32,
|
| - PropModeReplace,
|
| - reinterpret_cast<const unsigned char*>(&conversion_results.front()),
|
| - conversion_results.size());
|
| -
|
| - reply.xselection.property = requested_property;
|
| - }
|
| - } else {
|
| - if (ProcessTarget(requested_target, requestor, requested_property))
|
| - reply.xselection.property = requested_property;
|
| - }
|
| -
|
| - // Send off the reply.
|
| - XSendEvent(x_display_, requestor, False, 0, &reply);
|
| -}
|
| -
|
| -void SelectionOwner::OnSelectionClear(const XEvent& event) {
|
| - DLOG(ERROR) << "SelectionClear";
|
| -
|
| - // TODO(erg): If we receive a SelectionClear event while we're handling data,
|
| - // 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) {
|
| - XAtom multiple_atom = atom_cache_.GetAtom(kMultiple);
|
| - XAtom save_targets_atom = atom_cache_.GetAtom(kSaveTargets);
|
| - XAtom targets_atom = atom_cache_.GetAtom(kTargets);
|
| -
|
| - if (target == multiple_atom || target == save_targets_atom)
|
| - return false;
|
| -
|
| - if (target == targets_atom) {
|
| - // We have been asked for TARGETS. Send an atom array back with the data
|
| - // types we support.
|
| - std::vector<XAtom> targets;
|
| - targets.push_back(targets_atom);
|
| - targets.push_back(save_targets_atom);
|
| - targets.push_back(multiple_atom);
|
| - RetrieveTargets(&targets);
|
| -
|
| - XChangeProperty(x_display_, requestor, property, XA_ATOM, 32,
|
| - PropModeReplace,
|
| - reinterpret_cast<unsigned char*>(&targets.front()),
|
| - targets.size());
|
| - return true;
|
| - } else {
|
| - // Try to find the data type in map.
|
| - SelectionFormatMap::const_iterator it = format_map_.find(target);
|
| - if (it != format_map_.end()) {
|
| - 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
|
| - // looking for its own internal types.
|
| - }
|
| - 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
|
|
|