Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / base / x / selection_owner.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_owner.h"
6
7 #include <X11/Xlib.h>
8 #include <X11/Xatom.h>
9
10 #include "base/logging.h"
11 #include "ui/base/x/selection_utils.h"
12 #include "ui/base/x/x11_util.h"
13
14 namespace ui {
15
16 namespace {
17
18 const char kAtomPair[] = "ATOM_PAIR";
19 const char kMultiple[] = "MULTIPLE";
20 const char kSaveTargets[] = "SAVE_TARGETS";
21 const char kTargets[] = "TARGETS";
22
23 const char* kAtomsToCache[] = {
24   kAtomPair,
25   kMultiple,
26   kSaveTargets,
27   kTargets,
28   NULL
29 };
30
31 // Gets the value of an atom pair array property. On success, true is returned
32 // and the value is stored in |value|.
33 bool GetAtomPairArrayProperty(XID window,
34                               Atom property,
35                               std::vector<std::pair<Atom,Atom> >* value) {
36   Atom type = None;
37   int format = 0;  // size in bits of each item in 'property'
38   unsigned long num_items = 0;
39   unsigned char* properties = NULL;
40   unsigned long remaining_bytes = 0;
41
42   int result = XGetWindowProperty(gfx::GetXDisplay(),
43                                   window,
44                                   property,
45                                   0,          // offset into property data to
46                                               // read
47                                   (~0L),      // entire array
48                                   False,      // deleted
49                                   AnyPropertyType,
50                                   &type,
51                                   &format,
52                                   &num_items,
53                                   &remaining_bytes,
54                                   &properties);
55
56   if (result != Success)
57     return false;
58
59   // GTK does not require |type| to be kAtomPair.
60   if (format != 32 || num_items % 2 != 0) {
61     XFree(properties);
62     return false;
63   }
64
65   Atom* atom_properties = reinterpret_cast<Atom*>(properties);
66   value->clear();
67   for (size_t i = 0; i < num_items; i+=2)
68     value->push_back(std::make_pair(atom_properties[i], atom_properties[i+1]));
69   XFree(properties);
70   return true;
71 }
72
73 }  // namespace
74
75 SelectionOwner::SelectionOwner(Display* x_display,
76                                Window x_window,
77                                Atom selection_name)
78     : x_display_(x_display),
79       x_window_(x_window),
80       selection_name_(selection_name),
81       atom_cache_(x_display_, kAtomsToCache) {
82 }
83
84 SelectionOwner::~SelectionOwner() {
85   // If we are the selection owner, we need to release the selection so we
86   // don't receive further events. However, we don't call ClearSelectionOwner()
87   // because we don't want to do this indiscriminately.
88   if (XGetSelectionOwner(x_display_, selection_name_) == x_window_)
89     XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
90 }
91
92 void SelectionOwner::RetrieveTargets(std::vector<Atom>* targets) {
93   for (SelectionFormatMap::const_iterator it = format_map_.begin();
94        it != format_map_.end(); ++it) {
95     targets->push_back(it->first);
96   }
97 }
98
99 void SelectionOwner::TakeOwnershipOfSelection(
100     const SelectionFormatMap& data) {
101   XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime);
102
103   if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) {
104     // The X server agrees that we are the selection owner. Commit our data.
105     format_map_ = data;
106   }
107 }
108
109 void SelectionOwner::ClearSelectionOwner() {
110   XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
111   format_map_ = SelectionFormatMap();
112 }
113
114 void SelectionOwner::OnSelectionRequest(const XSelectionRequestEvent& event) {
115   // Incrementally build our selection. By default this is a refusal, and we'll
116   // override the parts indicating success in the different cases.
117   XEvent reply;
118   reply.xselection.type = SelectionNotify;
119   reply.xselection.requestor = event.requestor;
120   reply.xselection.selection = event.selection;
121   reply.xselection.target = event.target;
122   reply.xselection.property = None;  // Indicates failure
123   reply.xselection.time = event.time;
124
125   if (event.target == atom_cache_.GetAtom(kMultiple)) {
126     // The contents of |event.property| should be a list of
127     // <target,property> pairs.
128     std::vector<std::pair<Atom,Atom> > conversions;
129     if (GetAtomPairArrayProperty(event.requestor,
130                                  event.property,
131                                  &conversions)) {
132       std::vector<Atom> conversion_results;
133       for (size_t i = 0; i < conversions.size(); ++i) {
134         bool conversion_successful = ProcessTarget(conversions[i].first,
135                                                    event.requestor,
136                                                    conversions[i].second);
137         conversion_results.push_back(conversions[i].first);
138         conversion_results.push_back(
139             conversion_successful ? conversions[i].second : None);
140       }
141
142       // Set the property to indicate which conversions succeeded. This matches
143       // what GTK does.
144       XChangeProperty(
145           x_display_,
146           event.requestor,
147           event.property,
148           atom_cache_.GetAtom(kAtomPair),
149           32,
150           PropModeReplace,
151           reinterpret_cast<const unsigned char*>(&conversion_results.front()),
152           conversion_results.size());
153
154       reply.xselection.property = event.property;
155     }
156   } else {
157     if (ProcessTarget(event.target, event.requestor, event.property))
158       reply.xselection.property = event.property;
159   }
160
161   // Send off the reply.
162   XSendEvent(x_display_, event.requestor, False, 0, &reply);
163 }
164
165 void SelectionOwner::OnSelectionClear(const XSelectionClearEvent& event) {
166   DLOG(ERROR) << "SelectionClear";
167
168   // TODO(erg): If we receive a SelectionClear event while we're handling data,
169   // we need to delay clearing.
170 }
171
172 bool SelectionOwner::ProcessTarget(Atom target,
173                                    ::Window requestor,
174                                    ::Atom property) {
175   Atom multiple_atom = atom_cache_.GetAtom(kMultiple);
176   Atom save_targets_atom = atom_cache_.GetAtom(kSaveTargets);
177   Atom targets_atom = atom_cache_.GetAtom(kTargets);
178
179   if (target == multiple_atom || target == save_targets_atom)
180     return false;
181
182   if (target == targets_atom) {
183     // We have been asked for TARGETS. Send an atom array back with the data
184     // types we support.
185     std::vector<Atom> targets;
186     targets.push_back(targets_atom);
187     targets.push_back(save_targets_atom);
188     targets.push_back(multiple_atom);
189     RetrieveTargets(&targets);
190
191     XChangeProperty(x_display_, requestor, property, XA_ATOM, 32,
192                     PropModeReplace,
193                     reinterpret_cast<unsigned char*>(&targets.front()),
194                     targets.size());
195     return true;
196   } else {
197     // Try to find the data type in map.
198     SelectionFormatMap::const_iterator it = format_map_.find(target);
199     if (it != format_map_.end()) {
200       XChangeProperty(x_display_, requestor, property, target, 8,
201                       PropModeReplace,
202                       const_cast<unsigned char*>(
203                           reinterpret_cast<const unsigned char*>(
204                               it->second->front())),
205                       it->second->size());
206       return true;
207     }
208     // I would put error logging here, but GTK ignores TARGETS and spams us
209     // looking for its own internal types.
210   }
211   return false;
212 }
213
214 }  // namespace ui
215