Home | History | Annotate | Download | only in x
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/base/x/selection_requestor.h"
      6 
      7 #include "base/message_loop/message_pump_x11.h"
      8 #include "base/run_loop.h"
      9 #include "ui/base/x/selection_utils.h"
     10 #include "ui/base/x/x11_util.h"
     11 
     12 namespace ui {
     13 
     14 namespace {
     15 
     16 const char kChromeSelection[] = "CHROME_SELECTION";
     17 
     18 const char* kAtomsToCache[] = {
     19   kChromeSelection,
     20   NULL
     21 };
     22 
     23 }  // namespace
     24 
     25 SelectionRequestor::SelectionRequestor(Display* x_display,
     26                                        Window x_window,
     27                                        Atom selection_name)
     28     : x_display_(x_display),
     29       x_window_(x_window),
     30       selection_name_(selection_name),
     31       atom_cache_(x_display_, kAtomsToCache) {
     32 }
     33 
     34 SelectionRequestor::~SelectionRequestor() {}
     35 
     36 bool SelectionRequestor::PerformBlockingConvertSelection(
     37     Atom target,
     38     scoped_refptr<base::RefCountedMemory>* out_data,
     39     size_t* out_data_bytes,
     40     size_t* out_data_items,
     41     Atom* out_type) {
     42   // The name of the property we're asking to be set on |x_window_|.
     43   Atom property_to_set = atom_cache_.GetAtom(kChromeSelection);
     44 
     45   XConvertSelection(x_display_,
     46                     selection_name_,
     47                     target,
     48                     property_to_set,
     49                     x_window_,
     50                     CurrentTime);
     51 
     52   // Now that we've thrown our message off to the X11 server, we block waiting
     53   // for a response.
     54   base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
     55   base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
     56   base::RunLoop run_loop(base::MessagePumpX11::Current());
     57 
     58   PendingRequest pending_request(target, run_loop.QuitClosure());
     59   pending_requests_.push_back(&pending_request);
     60   run_loop.Run();
     61   DCHECK(!pending_requests_.empty());
     62   DCHECK_EQ(&pending_request, pending_requests_.back());
     63   pending_requests_.pop_back();
     64 
     65   if (pending_request.returned_property != property_to_set)
     66     return false;
     67 
     68   return ui::GetRawBytesOfProperty(x_window_, pending_request.returned_property,
     69                                    out_data, out_data_bytes, out_data_items,
     70                                    out_type);
     71 }
     72 
     73 SelectionData SelectionRequestor::RequestAndWaitForTypes(
     74     const std::vector< ::Atom>& types) {
     75   for (std::vector< ::Atom>::const_iterator it = types.begin();
     76        it != types.end(); ++it) {
     77     scoped_refptr<base::RefCountedMemory> data;
     78     size_t data_bytes = 0;
     79     ::Atom type = None;
     80     if (PerformBlockingConvertSelection(*it,
     81                                         &data,
     82                                         &data_bytes,
     83                                         NULL,
     84                                         &type) &&
     85         type == *it) {
     86       return SelectionData(type, data);
     87     }
     88   }
     89 
     90   return SelectionData();
     91 }
     92 
     93 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) {
     94   // Find the PendingRequest for the corresponding XConvertSelection call. If
     95   // there are multiple pending requests on the same target, satisfy them in
     96   // FIFO order.
     97   PendingRequest* request_notified = NULL;
     98   if (selection_name_ == event.selection) {
     99     for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin();
    100          iter != pending_requests_.end(); ++iter) {
    101       PendingRequest* request = *iter;
    102       if (request->returned)
    103         continue;
    104       if (request->target != event.target)
    105         continue;
    106       request_notified = request;
    107       break;
    108     }
    109   }
    110 
    111   // This event doesn't correspond to any XConvertSelection calls that we
    112   // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
    113   // client can send any message, so it can happen.
    114   if (!request_notified)
    115     return;
    116 
    117   request_notified->returned_property = event.property;
    118   request_notified->returned = true;
    119   request_notified->quit_closure.Run();
    120 }
    121 
    122 SelectionRequestor::PendingRequest::PendingRequest(Atom target,
    123                                                    base::Closure quit_closure)
    124     : target(target),
    125       quit_closure(quit_closure),
    126       returned_property(None),
    127       returned(false) {
    128 }
    129 
    130 SelectionRequestor::PendingRequest::~PendingRequest() {
    131 }
    132 
    133 }  // namespace ui
    134