summaryrefslogtreecommitdiff
path: root/chromium/ui/base/x/selection_requestor.cc
blob: 405c0592f8010dff841a9993bc2740c213c5cc70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/x/selection_requestor.h"

#include "base/message_loop/message_pump_aurax11.h"
#include "base/run_loop.h"
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_util.h"

namespace ui {

namespace {

const char kChromeSelection[] = "CHROME_SELECTION";

const char* kAtomsToCache[] = {
  kChromeSelection,
  NULL
};

}  // namespace

SelectionRequestor::SelectionRequestor(Display* x_display,
                                       Window x_window,
                                       Atom selection_name)
    : x_display_(x_display),
      x_window_(x_window),
      in_nested_loop_(false),
      selection_name_(selection_name),
      current_target_(None),
      returned_property_(None),
      atom_cache_(x_display_, kAtomsToCache) {
}

SelectionRequestor::~SelectionRequestor() {}

bool SelectionRequestor::PerformBlockingConvertSelection(
    Atom target,
    scoped_refptr<base::RefCountedMemory>* out_data,
    size_t* out_data_bytes,
    size_t* out_data_items,
    Atom* out_type) {
  // The name of the property we're asking to be set on |x_window_|.
  Atom property_to_set = atom_cache_.GetAtom(kChromeSelection);

  XConvertSelection(x_display_,
                    selection_name_,
                    target,
                    property_to_set,
                    x_window_,
                    CurrentTime);

  // Now that we've thrown our message off to the X11 server, we block waiting
  // for a response.
  base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
  base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
  base::RunLoop run_loop(base::MessagePumpAuraX11::Current());

  current_target_ = target;
  in_nested_loop_ = true;
  quit_closure_ = run_loop.QuitClosure();
  run_loop.Run();
  in_nested_loop_ = false;
  current_target_ = None;

  if (returned_property_ != property_to_set)
    return false;

  return ui::GetRawBytesOfProperty(x_window_, returned_property_,
                                   out_data, out_data_bytes, out_data_items,
                                   out_type);
}

SelectionData SelectionRequestor::RequestAndWaitForTypes(
    const std::vector< ::Atom>& types) {
  for (std::vector< ::Atom>::const_iterator it = types.begin();
       it != types.end(); ++it) {
    scoped_refptr<base::RefCountedMemory> data;
    size_t data_bytes = 0;
    ::Atom type = None;
    if (PerformBlockingConvertSelection(*it,
                                        &data,
                                        &data_bytes,
                                        NULL,
                                        &type) &&
        type == *it) {
      return SelectionData(type, data);
    }
  }

  return SelectionData();
}

void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) {
  if (!in_nested_loop_) {
    // This shouldn't happen; we're not waiting on the X server for data, but
    // any client can send any message...
    return;
  }

  if (selection_name_ == event.selection &&
      current_target_ == event.target) {
    returned_property_ = event.property;
  } else {
    // I am assuming that if some other client sent us a message after we've
    // asked for data, but it's malformed, we should just treat as if they sent
    // us an error message.
    returned_property_ = None;
  }

  quit_closure_.Run();
}

}  // namespace ui