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