1 // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/dialog_test.h"
6
7 #include <string>
8
9 #include "include/cef_browser.h"
10 #include "include/wrapper/cef_helpers.h"
11 #include "tests/cefclient/browser/test_runner.h"
12 #include "tests/shared/browser/file_util.h"
13
14 namespace client {
15 namespace dialog_test {
16
17 namespace {
18
19 const char kTestUrlPath[] = "/dialogs";
20 const char kFileOpenMessageName[] = "DialogTest.FileOpen";
21 const char kFileOpenMultipleMessageName[] = "DialogTest.FileOpenMultiple";
22 const char kFileOpenFolderMessageName[] = "DialogTest.FileOpenFolder";
23 const char kFileSaveMessageName[] = "DialogTest.FileSave";
24
25 // Store persistent dialog state information.
26 class DialogState : public base::RefCountedThreadSafe<DialogState> {
27 public:
DialogState()28 DialogState()
29 : mode_(FILE_DIALOG_OPEN), last_selected_filter_(0), pending_(false) {}
30
31 cef_file_dialog_mode_t mode_;
32 int last_selected_filter_;
33 CefString last_file_;
34 bool pending_;
35
36 DISALLOW_COPY_AND_ASSIGN(DialogState);
37 };
38
39 // Callback executed when the file dialog is dismissed.
40 class DialogCallback : public CefRunFileDialogCallback {
41 public:
DialogCallback(CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback,scoped_refptr<DialogState> dialog_state)42 DialogCallback(
43 CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback,
44 scoped_refptr<DialogState> dialog_state)
45 : router_callback_(router_callback), dialog_state_(dialog_state) {}
46
OnFileDialogDismissed(int last_selected_filter,const std::vector<CefString> & file_paths)47 virtual void OnFileDialogDismissed(
48 int last_selected_filter,
49 const std::vector<CefString>& file_paths) override {
50 CEF_REQUIRE_UI_THREAD();
51 DCHECK(dialog_state_->pending_);
52
53 if (!file_paths.empty()) {
54 if (dialog_state_->mode_ != FILE_DIALOG_OPEN_FOLDER)
55 dialog_state_->last_selected_filter_ = last_selected_filter;
56
57 dialog_state_->last_file_ = file_paths[0];
58 if (dialog_state_->mode_ == FILE_DIALOG_OPEN_FOLDER) {
59 std::string last_file = dialog_state_->last_file_;
60 if (last_file[last_file.length() - 1] != file_util::kPathSep) {
61 // Add a trailing slash so we know it's a directory. Otherwise, file
62 // dialogs will think the last path component is a file name.
63 last_file += file_util::kPathSep;
64 dialog_state_->last_file_ = last_file;
65 }
66 }
67 }
68
69 // Send a message back to the render process with the list of file paths.
70 std::string response;
71 for (int i = 0; i < static_cast<int>(file_paths.size()); ++i) {
72 if (!response.empty())
73 response += "|"; // Use a delimiter disallowed in file paths.
74 response += file_paths[i];
75 }
76
77 router_callback_->Success(response);
78 router_callback_ = nullptr;
79
80 dialog_state_->pending_ = false;
81 dialog_state_ = nullptr;
82 }
83
84 private:
85 CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback_;
86 scoped_refptr<DialogState> dialog_state_;
87
88 IMPLEMENT_REFCOUNTING(DialogCallback);
89 DISALLOW_COPY_AND_ASSIGN(DialogCallback);
90 };
91
92 // Handle messages in the browser process.
93 class Handler : public CefMessageRouterBrowserSide::Handler {
94 public:
Handler()95 Handler() {}
96
97 // Called due to cefQuery execution in dialogs.html.
OnQuery(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int64 query_id,const CefString & request,bool persistent,CefRefPtr<Callback> callback)98 virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
99 CefRefPtr<CefFrame> frame,
100 int64 query_id,
101 const CefString& request,
102 bool persistent,
103 CefRefPtr<Callback> callback) override {
104 CEF_REQUIRE_UI_THREAD();
105
106 // Only handle messages from the test URL.
107 const std::string& url = frame->GetURL();
108 if (!test_runner::IsTestURL(url, kTestUrlPath))
109 return false;
110
111 if (!dialog_state_.get())
112 dialog_state_ = new DialogState;
113
114 // Make sure we're only running one dialog at a time.
115 DCHECK(!dialog_state_->pending_);
116
117 std::vector<CefString> accept_filters;
118 std::string title;
119
120 const std::string& message_name = request;
121 if (message_name == kFileOpenMessageName) {
122 dialog_state_->mode_ = FILE_DIALOG_OPEN;
123 title = "My Open Dialog";
124 } else if (message_name == kFileOpenMultipleMessageName) {
125 dialog_state_->mode_ = FILE_DIALOG_OPEN_MULTIPLE;
126 title = "My Open Multiple Dialog";
127 } else if (message_name == kFileOpenFolderMessageName) {
128 dialog_state_->mode_ = FILE_DIALOG_OPEN_FOLDER;
129 title = "My Open Folder Dialog";
130 } else if (message_name == kFileSaveMessageName) {
131 dialog_state_->mode_ = static_cast<cef_file_dialog_mode_t>(
132 FILE_DIALOG_SAVE | FILE_DIALOG_OVERWRITEPROMPT_FLAG);
133 title = "My Save Dialog";
134 } else {
135 NOTREACHED();
136 return true;
137 }
138
139 if (dialog_state_->mode_ != FILE_DIALOG_OPEN_FOLDER) {
140 // Build filters based on mime time.
141 accept_filters.push_back("text/*");
142
143 // Build filters based on file extension.
144 accept_filters.push_back(".log");
145 accept_filters.push_back(".patch");
146
147 // Add specific filters as-is.
148 accept_filters.push_back("Document Files|.doc;.odt");
149 accept_filters.push_back("Image Files|.png;.jpg;.gif");
150 accept_filters.push_back("PDF Files|.pdf");
151 }
152
153 dialog_state_->pending_ = true;
154
155 browser->GetHost()->RunFileDialog(
156 dialog_state_->mode_, title, dialog_state_->last_file_, accept_filters,
157 dialog_state_->last_selected_filter_,
158 new DialogCallback(callback, dialog_state_));
159
160 return true;
161 }
162
163 private:
164 scoped_refptr<DialogState> dialog_state_;
165
166 DISALLOW_COPY_AND_ASSIGN(Handler);
167 };
168
169 } // namespace
170
CreateMessageHandlers(test_runner::MessageHandlerSet & handlers)171 void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
172 handlers.insert(new Handler());
173 }
174
175 } // namespace dialog_test
176 } // namespace client
177