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#ifndef UI_BASE_X_SELECTION_REQUESTOR_H_
6#define UI_BASE_X_SELECTION_REQUESTOR_H_
7
8#include <vector>
9
10#include "base/basictypes.h"
11#include "base/callback.h"
12#include "base/event_types.h"
13#include "base/memory/ref_counted_memory.h"
14#include "base/time/time.h"
15#include "base/timer/timer.h"
16#include "ui/base/ui_base_export.h"
17#include "ui/gfx/x/x11_atom_cache.h"
18#include "ui/gfx/x/x11_types.h"
19
20namespace ui {
21class PlatformEventDispatcher;
22class SelectionData;
23
24// Requests and later receives data from the X11 server through the selection
25// system.
26//
27// X11 uses a system called "selections" to implement clipboards and drag and
28// drop. This class interprets messages from the stateful selection request
29// API. SelectionRequestor should only deal with the X11 details; it does not
30// implement per-component fast-paths.
31class UI_BASE_EXPORT SelectionRequestor {
32 public:
33  SelectionRequestor(XDisplay* xdisplay,
34                     XID xwindow,
35                     PlatformEventDispatcher* dispatcher);
36  ~SelectionRequestor();
37
38  // Does the work of requesting |target| from |selection|, spinning up the
39  // nested message loop, and reading the resulting data back. The result is
40  // stored in |out_data|.
41  // |out_data_items| is the length of |out_data| in |out_type| items.
42  bool PerformBlockingConvertSelection(
43      XAtom selection,
44      XAtom target,
45      scoped_refptr<base::RefCountedMemory>* out_data,
46      size_t* out_data_items,
47      XAtom* out_type);
48
49  // Requests |target| from |selection|, passing |parameter| as a parameter to
50  // XConvertSelection().
51  void PerformBlockingConvertSelectionWithParameter(
52      XAtom selection,
53      XAtom target,
54      const std::vector<XAtom>& parameter);
55
56  // Returns the first of |types| offered by the current owner of |selection|.
57  // Returns an empty SelectionData object if none of |types| are available.
58  SelectionData RequestAndWaitForTypes(XAtom selection,
59                                       const std::vector<XAtom>& types);
60
61  // It is our owner's responsibility to plumb X11 SelectionNotify events on
62  // |xwindow_| to us.
63  void OnSelectionNotify(const XEvent& event);
64
65  // Returns true if SelectionOwner can process the XChangeProperty event,
66  // |event|.
67  bool CanDispatchPropertyEvent(const XEvent& event);
68
69  void OnPropertyEvent(const XEvent& event);
70
71 private:
72  friend class SelectionRequestorTest;
73
74  // A request that has been issued.
75  struct Request {
76    Request(XAtom selection, XAtom target, base::TimeTicks timeout);
77    ~Request();
78
79    // The target and selection requested in the XConvertSelection() request.
80    // Used for error detection.
81    XAtom selection;
82    XAtom target;
83
84    // Whether the result of the XConvertSelection() request is being sent
85    // incrementally.
86    bool data_sent_incrementally;
87
88    // The result data for the XConvertSelection() request.
89    std::vector<scoped_refptr<base::RefCountedMemory> > out_data;
90    size_t out_data_items;
91    XAtom out_type;
92
93    // Whether the XConvertSelection() request was successful.
94    bool success;
95
96    // The time when the request should be aborted.
97    base::TimeTicks timeout;
98
99    // Called to terminate the nested message loop.
100    base::Closure quit_closure;
101
102    // True if the request is complete.
103    bool completed;
104  };
105
106  // Aborts requests which have timed out.
107  void AbortStaleRequests();
108
109  // Mark |request| as completed. If the current request is completed, converts
110  // the selection for the next request.
111  void CompleteRequest(size_t index, bool success);
112
113  // Converts the selection for the request at |current_request_index_|.
114  void ConvertSelectionForCurrentRequest();
115
116  // Blocks till SelectionNotify is received for the target specified in
117  // |request|.
118  void BlockTillSelectionNotifyForRequest(Request* request);
119
120  // Returns the request at |current_request_index_| or NULL if there isn't any.
121  Request* GetCurrentRequest();
122
123  // Our X11 state.
124  XDisplay* x_display_;
125  XID x_window_;
126
127  // The property on |x_window_| set by the selection owner with the value of
128  // the selection.
129  XAtom x_property_;
130
131  // Dispatcher which handles SelectionNotify and SelectionRequest for
132  // |selection_name_|. PerformBlockingConvertSelection() calls the
133  // dispatcher directly if PerformBlockingConvertSelection() is called after
134  // the PlatformEventSource is destroyed.
135  // Not owned.
136  PlatformEventDispatcher* dispatcher_;
137
138  // In progress requests. Requests are added to the list at the start of
139  // PerformBlockingConvertSelection() and are removed and destroyed right
140  // before the method terminates.
141  std::vector<Request*> requests_;
142
143  // The index of the currently active request in |requests_|. The active
144  // request is the request for which XConvertSelection() has been
145  // called and for which we are waiting for a SelectionNotify response.
146  size_t current_request_index_;
147
148  // Used to abort requests if the selection owner takes too long to respond.
149  base::RepeatingTimer<SelectionRequestor> abort_timer_;
150
151  X11AtomCache atom_cache_;
152
153  DISALLOW_COPY_AND_ASSIGN(SelectionRequestor);
154};
155
156}  // namespace ui
157
158#endif  // UI_BASE_X_SELECTION_REQUESTOR_H_
159