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 |