Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/x/selection_requestor.h" | 5 #include "ui/base/x/selection_requestor.h" |
| 6 | 6 |
| 7 #include "base/run_loop.h" | 7 #include "base/run_loop.h" |
| 8 #include "ui/base/x/selection_utils.h" | 8 #include "ui/base/x/selection_utils.h" |
| 9 #include "ui/base/x/x11_util.h" | 9 #include "ui/base/x/x11_util.h" |
| 10 #include "ui/events/platform/platform_event_dispatcher.h" | 10 #include "ui/events/platform/platform_event_dispatcher.h" |
| 11 #include "ui/events/platform/platform_event_source.h" | 11 #include "ui/events/platform/platform_event_source.h" |
| 12 #include "ui/gfx/x/x11_types.h" | 12 #include "ui/gfx/x/x11_types.h" |
| 13 | 13 |
| 14 namespace ui { | 14 namespace ui { |
| 15 | 15 |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 const char kChromeSelection[] = "CHROME_SELECTION"; | 18 // Properties to be used in XConvertSelection() requests. The number of |
| 19 // properties is arbitrary. | |
| 20 const char* kChromeSelectionProperties[] = { | |
| 21 "CHROME_SELECTION0", | |
| 22 "CHROME_SELECTION1", | |
| 23 "CHROME_SELECTION2", | |
| 24 "CHROME_SELECTION3", | |
| 25 "CHROME_SELECTION4" | |
| 26 }; | |
| 19 | 27 |
| 20 const char* kAtomsToCache[] = { | 28 const char* kAtomsToCache[] = { |
| 21 kChromeSelection, | 29 kChromeSelectionProperties[0], |
| 30 kChromeSelectionProperties[1], | |
| 31 kChromeSelectionProperties[2], | |
| 32 kChromeSelectionProperties[3], | |
| 33 kChromeSelectionProperties[4], | |
| 22 NULL | 34 NULL |
| 23 }; | 35 }; |
| 24 | 36 |
| 25 } // namespace | 37 } // namespace |
| 26 | 38 |
| 27 SelectionRequestor::SelectionRequestor(Display* x_display, | 39 SelectionRequestor::SelectionRequestor(Display* x_display, |
| 28 Window x_window, | 40 Window x_window, |
| 29 Atom selection_name, | |
| 30 PlatformEventDispatcher* dispatcher) | 41 PlatformEventDispatcher* dispatcher) |
| 31 : x_display_(x_display), | 42 : x_display_(x_display), |
| 32 x_window_(x_window), | 43 x_window_(x_window), |
| 33 selection_name_(selection_name), | |
| 34 dispatcher_(dispatcher), | 44 dispatcher_(dispatcher), |
| 45 property_pool_(kChromeSelectionProperties, | |
| 46 kChromeSelectionProperties | |
| 47 + arraysize(kChromeSelectionProperties)), | |
| 35 atom_cache_(x_display_, kAtomsToCache) { | 48 atom_cache_(x_display_, kAtomsToCache) { |
| 36 } | 49 } |
| 37 | 50 |
| 38 SelectionRequestor::~SelectionRequestor() {} | 51 SelectionRequestor::~SelectionRequestor() {} |
| 39 | 52 |
| 40 bool SelectionRequestor::PerformBlockingConvertSelection( | 53 bool SelectionRequestor::PerformBlockingConvertSelection( |
| 54 Atom selection, | |
| 41 Atom target, | 55 Atom target, |
| 42 scoped_refptr<base::RefCountedMemory>* out_data, | 56 scoped_refptr<base::RefCountedMemory>* out_data, |
| 43 size_t* out_data_items, | 57 size_t* out_data_items, |
| 44 Atom* out_type) { | 58 Atom* out_type) { |
| 45 // The name of the property that we are either: | 59 // Find a property for XConvertSelection() to use. The selection owner will |
| 46 // - Passing as a parameter with the XConvertSelection() request. | 60 // set |property| on |xwindow_| with the data for the requested target. If |
| 47 // OR | 61 // there is no available property, make the request fail. |
| 48 // - Asking the selection owner to set on |x_window_|. | 62 if (property_pool_.empty()) |
| 49 Atom property = atom_cache_.GetAtom(kChromeSelection); | 63 return false; |
| 50 | 64 |
| 51 XConvertSelection(x_display_, | 65 const char* property_name = property_pool_.back(); |
| 52 selection_name_, | 66 property_pool_.pop_back(); |
|
Daniel Erat
2014/07/15 01:04:09
with the disclaimer that i don't know much about s
pkotwicz
2014/07/15 02:07:24
The problem is that there are callers of clipboard
Daniel Erat
2014/07/15 03:32:01
oh. is the nested message loop in BlockTillSelecti
| |
| 53 target, | 67 bool success = PerformBlockingConvertSelectionImpl(selection, target, |
| 54 property, | 68 property_name, out_data, out_data_items, out_type); |
| 55 x_window_, | 69 property_pool_.push_back(property_name); |
| 56 CurrentTime); | |
| 57 | |
| 58 // Now that we've thrown our message off to the X11 server, we block waiting | |
| 59 // for a response. | |
| 60 PendingRequest pending_request(target); | |
| 61 BlockTillSelectionNotifyForRequest(&pending_request); | |
| 62 | |
| 63 bool success = false; | |
| 64 if (pending_request.returned_property == property) { | |
| 65 success = ui::GetRawBytesOfProperty(x_window_, | |
| 66 pending_request.returned_property, | |
| 67 out_data, out_data_items, out_type); | |
| 68 } | |
| 69 if (pending_request.returned_property != None) | |
| 70 XDeleteProperty(x_display_, x_window_, pending_request.returned_property); | |
| 71 return success; | 70 return success; |
| 72 } | 71 } |
| 73 | 72 |
| 74 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter( | 73 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter( |
| 74 Atom selection, | |
| 75 Atom target, | 75 Atom target, |
| 76 const std::vector< ::Atom>& parameter) { | 76 const std::vector< ::Atom>& parameter) { |
| 77 SetAtomArrayProperty(x_window_, kChromeSelection, "ATOM", parameter); | 77 if (property_pool_.empty()) |
| 78 PerformBlockingConvertSelection(target, NULL, NULL, NULL); | 78 return; |
| 79 const char* property_name = property_pool_.back(); | |
| 80 property_pool_.pop_back(); | |
| 81 SetAtomArrayProperty(x_window_, property_name, "ATOM", parameter); | |
| 82 PerformBlockingConvertSelectionImpl(selection, target, property_name, NULL, | |
| 83 NULL, NULL); | |
| 84 property_pool_.push_back(property_name); | |
| 79 } | 85 } |
| 80 | 86 |
| 81 SelectionData SelectionRequestor::RequestAndWaitForTypes( | 87 SelectionData SelectionRequestor::RequestAndWaitForTypes( |
| 88 Atom selection, | |
| 82 const std::vector< ::Atom>& types) { | 89 const std::vector< ::Atom>& types) { |
| 83 for (std::vector< ::Atom>::const_iterator it = types.begin(); | 90 for (std::vector< ::Atom>::const_iterator it = types.begin(); |
| 84 it != types.end(); ++it) { | 91 it != types.end(); ++it) { |
| 85 scoped_refptr<base::RefCountedMemory> data; | 92 scoped_refptr<base::RefCountedMemory> data; |
| 86 ::Atom type = None; | 93 ::Atom type = None; |
| 87 if (PerformBlockingConvertSelection(*it, | 94 if (PerformBlockingConvertSelection(selection, |
| 95 *it, | |
| 88 &data, | 96 &data, |
| 89 NULL, | 97 NULL, |
| 90 &type) && | 98 &type) && |
| 91 type == *it) { | 99 type == *it) { |
| 92 return SelectionData(type, data); | 100 return SelectionData(type, data); |
| 93 } | 101 } |
| 94 } | 102 } |
| 95 | 103 |
| 96 return SelectionData(); | 104 return SelectionData(); |
| 97 } | 105 } |
| 98 | 106 |
| 99 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) { | 107 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) { |
| 100 // Find the PendingRequest for the corresponding XConvertSelection call. If | 108 // Find the PendingRequest for the corresponding XConvertSelection call. If |
| 101 // there are multiple pending requests on the same target, satisfy them in | 109 // there are multiple pending requests on the same target, satisfy them in |
| 102 // FIFO order. | 110 // FIFO order. |
| 103 PendingRequest* request_notified = NULL; | 111 PendingRequest* request_notified = NULL; |
| 104 if (selection_name_ == event.selection) { | 112 for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin(); |
| 105 for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin(); | 113 iter != pending_requests_.end(); ++iter) { |
| 106 iter != pending_requests_.end(); ++iter) { | 114 PendingRequest* request = *iter; |
| 107 PendingRequest* request = *iter; | 115 if (!request->returned && |
| 108 if (request->returned) | 116 request->selection == event.selection && |
| 109 continue; | 117 request->target == event.target) { |
| 110 if (request->target != event.target) | |
| 111 continue; | |
| 112 request_notified = request; | 118 request_notified = request; |
| 113 break; | 119 break; |
| 114 } | 120 } |
| 115 } | 121 } |
| 116 | 122 |
| 117 // This event doesn't correspond to any XConvertSelection calls that we | 123 // This event doesn't correspond to any XConvertSelection calls that we |
| 118 // issued in PerformBlockingConvertSelection. This shouldn't happen, but any | 124 // issued in PerformBlockingConvertSelection. This shouldn't happen, but any |
| 119 // client can send any message, so it can happen. | 125 // client can send any message, so it can happen. |
| 120 if (!request_notified) { | 126 if (!request_notified) { |
| 121 // ICCCM requires us to delete the property passed into SelectionNotify. If | 127 // ICCCM requires us to delete the property passed into SelectionNotify. If |
| 122 // |request_notified| is true, the property will be deleted when the run | 128 // |request_notified| is true, the property will be deleted when the run |
| 123 // loop has quit. | 129 // loop has quit. |
| 124 if (event.property != None) | 130 if (event.property != None) |
| 125 XDeleteProperty(x_display_, x_window_, event.property); | 131 XDeleteProperty(x_display_, x_window_, event.property); |
| 126 return; | 132 return; |
| 127 } | 133 } |
| 128 | 134 |
| 129 request_notified->returned_property = event.property; | 135 request_notified->returned_property = event.property; |
| 130 request_notified->returned = true; | 136 request_notified->returned = true; |
| 131 | 137 |
| 132 if (!request_notified->quit_closure.is_null()) | 138 if (!request_notified->quit_closure.is_null()) |
| 133 request_notified->quit_closure.Run(); | 139 request_notified->quit_closure.Run(); |
| 134 } | 140 } |
| 135 | 141 |
| 142 bool SelectionRequestor::PerformBlockingConvertSelectionImpl( | |
| 143 Atom selection, | |
| 144 Atom target, | |
| 145 const char* property_name, | |
| 146 scoped_refptr<base::RefCountedMemory>* out_data, | |
| 147 size_t* out_data_items, | |
| 148 Atom* out_type) { | |
| 149 Atom property = atom_cache_.GetAtom(property_name); | |
| 150 XConvertSelection(x_display_, | |
| 151 selection, | |
| 152 target, | |
| 153 property, | |
| 154 x_window_, | |
| 155 CurrentTime); | |
| 156 | |
| 157 // Now that we've thrown our message off to the X11 server, we block waiting | |
| 158 // for a response. | |
| 159 PendingRequest pending_request(selection, target); | |
| 160 BlockTillSelectionNotifyForRequest(&pending_request); | |
| 161 | |
| 162 bool success = false; | |
| 163 if (pending_request.returned_property == property) { | |
| 164 success = ui::GetRawBytesOfProperty(x_window_, | |
| 165 pending_request.returned_property, | |
| 166 out_data, out_data_items, out_type); | |
| 167 } | |
| 168 if (pending_request.returned_property != None) | |
| 169 XDeleteProperty(x_display_, x_window_, pending_request.returned_property); | |
| 170 return success; | |
| 171 } | |
| 172 | |
| 136 void SelectionRequestor::BlockTillSelectionNotifyForRequest( | 173 void SelectionRequestor::BlockTillSelectionNotifyForRequest( |
| 137 PendingRequest* request) { | 174 PendingRequest* request) { |
| 138 pending_requests_.push_back(request); | 175 pending_requests_.push_back(request); |
| 139 | 176 |
| 140 const int kMaxWaitTimeForClipboardResponse = 300; | 177 const int kMaxWaitTimeForClipboardResponse = 300; |
| 141 if (PlatformEventSource::GetInstance()) { | 178 if (PlatformEventSource::GetInstance()) { |
| 142 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | 179 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); |
| 143 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); | 180 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); |
| 144 base::RunLoop run_loop; | 181 base::RunLoop run_loop; |
| 145 | 182 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 164 if (wait_time.InMilliseconds() > kMaxWaitTimeForClipboardResponse) | 201 if (wait_time.InMilliseconds() > kMaxWaitTimeForClipboardResponse) |
| 165 break; | 202 break; |
| 166 } | 203 } |
| 167 } | 204 } |
| 168 | 205 |
| 169 DCHECK(!pending_requests_.empty()); | 206 DCHECK(!pending_requests_.empty()); |
| 170 DCHECK_EQ(request, pending_requests_.back()); | 207 DCHECK_EQ(request, pending_requests_.back()); |
| 171 pending_requests_.pop_back(); | 208 pending_requests_.pop_back(); |
| 172 } | 209 } |
| 173 | 210 |
| 174 SelectionRequestor::PendingRequest::PendingRequest(Atom target) | 211 SelectionRequestor::PendingRequest::PendingRequest(Atom requested_selection, |
| 175 : target(target), | 212 Atom requested_target) |
| 213 : selection(requested_selection), | |
| 214 target(requested_target), | |
| 176 returned_property(None), | 215 returned_property(None), |
| 177 returned(false) { | 216 returned(false) { |
| 178 } | 217 } |
| 179 | 218 |
| 180 SelectionRequestor::PendingRequest::~PendingRequest() { | 219 SelectionRequestor::PendingRequest::~PendingRequest() { |
| 181 } | 220 } |
| 182 | 221 |
| 183 } // namespace ui | 222 } // namespace ui |
| OLD | NEW |