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/run_loop.h"
      8 #include "ui/base/x/selection_utils.h"
      9 #include "ui/base/x/x11_util.h"
     10 #include "ui/events/platform/platform_event_dispatcher.h"
     11 #include "ui/events/platform/platform_event_source.h"
     12 #include "ui/gfx/x/x11_types.h"
     13 
     14 namespace ui {
     15 
     16 namespace {
     17 
     18 const char kChromeSelection[] = "CHROME_SELECTION";
     19 
     20 const char* kAtomsToCache[] = {
     21   kChromeSelection,
     22   NULL
     23 };
     24 
     25 }  // namespace
     26 
     27 SelectionRequestor::SelectionRequestor(Display* x_display,
     28                                        Window x_window,
     29                                        Atom selection_name,
     30                                        PlatformEventDispatcher* dispatcher)
     31     : x_display_(x_display),
     32       x_window_(x_window),
     33       selection_name_(selection_name),
     34       dispatcher_(dispatcher),
     35       atom_cache_(x_display_, kAtomsToCache) {
     36 }
     37 
     38 SelectionRequestor::~SelectionRequestor() {}
     39 
     40 bool SelectionRequestor::PerformBlockingConvertSelection(
     41     Atom target,
     42     scoped_refptr<base::RefCountedMemory>* out_data,
     43     size_t* out_data_bytes,
     44     size_t* out_data_items,
     45     Atom* out_type) {
     46   // The name of the property that we are either:
     47   // - Passing as a parameter with the XConvertSelection() request.
     48   // OR
     49   // - Asking the selection owner to set on |x_window_|.
     50   Atom property = atom_cache_.GetAtom(kChromeSelection);
     51 
     52   XConvertSelection(x_display_,
     53                     selection_name_,
     54                     target,
     55                     property,
     56                     x_window_,
     57                     CurrentTime);
     58 
     59   // Now that we've thrown our message off to the X11 server, we block waiting
     60   // for a response.
     61   PendingRequest pending_request(target);
     62   BlockTillSelectionNotifyForRequest(&pending_request);
     63 
     64   bool success = false;
     65   if (pending_request.returned_property == property) {
     66     success =  ui::GetRawBytesOfProperty(x_window_,
     67                                          pending_request.returned_property,
     68                                          out_data, out_data_bytes,
     69                                          out_data_items, out_type);
     70   }
     71   if (pending_request.returned_property != None)
     72     XDeleteProperty(x_display_, x_window_, pending_request.returned_property);
     73   return success;
     74 }
     75 
     76 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter(
     77     Atom target,
     78     const std::vector< ::Atom>& parameter) {
     79   SetAtomArrayProperty(x_window_, kChromeSelection, "ATOM", parameter);
     80   PerformBlockingConvertSelection(target, NULL, NULL, NULL, NULL);
     81 }
     82 
     83 SelectionData SelectionRequestor::RequestAndWaitForTypes(
     84     const std::vector< ::Atom>& types) {
     85   for (std::vector< ::Atom>::const_iterator it = types.begin();
     86        it != types.end(); ++it) {
     87     scoped_refptr<base::RefCountedMemory> data;
     88     size_t data_bytes = 0;
     89     ::Atom type = None;
     90     if (PerformBlockingConvertSelection(*it,
     91                                         &data,
     92                                         &data_bytes,
     93                                         NULL,
     94                                         &type) &&
     95         type == *it) {
     96       return SelectionData(type, data);
     97     }
     98   }
     99 
    100   return SelectionData();
    101 }
    102 
    103 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) {
    104   // Find the PendingRequest for the corresponding XConvertSelection call. If
    105   // there are multiple pending requests on the same target, satisfy them in
    106   // FIFO order.
    107   PendingRequest* request_notified = NULL;
    108   if (selection_name_ == event.selection) {
    109     for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin();
    110          iter != pending_requests_.end(); ++iter) {
    111       PendingRequest* request = *iter;
    112       if (request->returned)
    113         continue;
    114       if (request->target != event.target)
    115         continue;
    116       request_notified = request;
    117       break;
    118     }
    119   }
    120 
    121   // This event doesn't correspond to any XConvertSelection calls that we
    122   // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
    123   // client can send any message, so it can happen.
    124   if (!request_notified) {
    125     // ICCCM requires us to delete the property passed into SelectionNotify. If
    126     // |request_notified| is true, the property will be deleted when the run
    127     // loop has quit.
    128     if (event.property != None)
    129       XDeleteProperty(x_display_, x_window_, event.property);
    130     return;
    131   }
    132 
    133   request_notified->returned_property = event.property;
    134   request_notified->returned = true;
    135 
    136   if (!request_notified->quit_closure.is_null())
    137     request_notified->quit_closure.Run();
    138 }
    139 
    140 void SelectionRequestor::BlockTillSelectionNotifyForRequest(
    141     PendingRequest* request) {
    142   pending_requests_.push_back(request);
    143 
    144   const int kMaxWaitTimeForClipboardResponse = 300;
    145   if (PlatformEventSource::GetInstance()) {
    146     base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    147     base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
    148     base::RunLoop run_loop;
    149 
    150     request->quit_closure = run_loop.QuitClosure();
    151     loop->PostDelayedTask(
    152         FROM_HERE,
    153         request->quit_closure,
    154         base::TimeDelta::FromMilliseconds(kMaxWaitTimeForClipboardResponse));
    155 
    156     run_loop.Run();
    157   } else {
    158     // This occurs if PerformBlockingConvertSelection() is called during
    159     // shutdown and the PlatformEventSource has already been destroyed.
    160     base::TimeTicks start = base::TimeTicks::Now();
    161     while (!request->returned) {
    162       if (XPending(x_display_)) {
    163         XEvent event;
    164         XNextEvent(x_display_, &event);
    165         dispatcher_->DispatchEvent(&event);
    166       }
    167       base::TimeDelta wait_time = base::TimeTicks::Now() - start;
    168       if (wait_time.InMilliseconds() > kMaxWaitTimeForClipboardResponse)
    169         break;
    170     }
    171   }
    172 
    173   DCHECK(!pending_requests_.empty());
    174   DCHECK_EQ(request, pending_requests_.back());
    175   pending_requests_.pop_back();
    176 }
    177 
    178 SelectionRequestor::PendingRequest::PendingRequest(Atom target)
    179     : target(target),
    180       returned_property(None),
    181       returned(false) {
    182 }
    183 
    184 SelectionRequestor::PendingRequest::~PendingRequest() {
    185 }
    186 
    187 }  // namespace ui
    188