Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / ui / base / x / selection_requestor.cc
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;
57
58   // Stop waiting for a response after a certain amount of time.
59   const int kMaxWaitTimeForClipboardResponse = 300;
60   loop->PostDelayedTask(
61       FROM_HERE,
62       run_loop.QuitClosure(),
63       base::TimeDelta::FromMilliseconds(kMaxWaitTimeForClipboardResponse));
64
65   PendingRequest pending_request(target, run_loop.QuitClosure());
66   pending_requests_.push_back(&pending_request);
67   run_loop.Run();
68   DCHECK(!pending_requests_.empty());
69   DCHECK_EQ(&pending_request, pending_requests_.back());
70   pending_requests_.pop_back();
71
72   bool success = false;
73   if (pending_request.returned_property == property_to_set) {
74     success =  ui::GetRawBytesOfProperty(x_window_,
75                                          pending_request.returned_property,
76                                          out_data, out_data_bytes,
77                                          out_data_items, out_type);
78   }
79   if (pending_request.returned_property != None)
80     XDeleteProperty(x_display_, x_window_, pending_request.returned_property);
81   return success;
82 }
83
84 SelectionData SelectionRequestor::RequestAndWaitForTypes(
85     const std::vector< ::Atom>& types) {
86   for (std::vector< ::Atom>::const_iterator it = types.begin();
87        it != types.end(); ++it) {
88     scoped_refptr<base::RefCountedMemory> data;
89     size_t data_bytes = 0;
90     ::Atom type = None;
91     if (PerformBlockingConvertSelection(*it,
92                                         &data,
93                                         &data_bytes,
94                                         NULL,
95                                         &type) &&
96         type == *it) {
97       return SelectionData(type, data);
98     }
99   }
100
101   return SelectionData();
102 }
103
104 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) {
105   // Find the PendingRequest for the corresponding XConvertSelection call. If
106   // there are multiple pending requests on the same target, satisfy them in
107   // FIFO order.
108   PendingRequest* request_notified = NULL;
109   if (selection_name_ == event.selection) {
110     for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin();
111          iter != pending_requests_.end(); ++iter) {
112       PendingRequest* request = *iter;
113       if (request->returned)
114         continue;
115       if (request->target != event.target)
116         continue;
117       request_notified = request;
118       break;
119     }
120   }
121
122   // This event doesn't correspond to any XConvertSelection calls that we
123   // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
124   // client can send any message, so it can happen.
125   if (!request_notified) {
126     // ICCCM requires us to delete the property passed into SelectionNotify. If
127     // |request_notified| is true, the property will be deleted when the run
128     // loop has quit.
129     if (event.property != None)
130       XDeleteProperty(x_display_, x_window_, event.property);
131     return;
132   }
133
134   request_notified->returned_property = event.property;
135   request_notified->returned = true;
136   request_notified->quit_closure.Run();
137 }
138
139 SelectionRequestor::PendingRequest::PendingRequest(Atom target,
140                                                    base::Closure quit_closure)
141     : target(target),
142       quit_closure(quit_closure),
143       returned_property(None),
144       returned(false) {
145 }
146
147 SelectionRequestor::PendingRequest::~PendingRequest() {
148 }
149
150 }  // namespace ui