1
// Copyright 2008, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
#include "gears/desktop/desktop_cr.h"
28
#include "gears/base/chrome/browsing_context_cr.h"
29
#include "gears/base/chrome/module_cr.h"
30
#include "gears/base/common/paths.h"
31
#include "gears/base/common/string_utils.h"
32
#include "gears/desktop/desktop.h"
33
#include "gears/ui/common/html_dialog.h"
35
// We keep track of the set of URLs that have open shortcuts dialogs, so we
36
// don't open 2 at once.
37
static std::set<std::string16> *g_open_shortcuts_dialogs;
39
static void RegisterOpenDialog(const std::string16 &url) {
40
if (!g_open_shortcuts_dialogs)
41
g_open_shortcuts_dialogs = new std::set<std::string16>;
42
g_open_shortcuts_dialogs->insert(url);
45
static void UnregisterOpenDialog(const std::string16 &url) {
46
assert(g_open_shortcuts_dialogs);
47
g_open_shortcuts_dialogs->erase(url);
48
if (g_open_shortcuts_dialogs->empty()) {
49
delete g_open_shortcuts_dialogs;
50
g_open_shortcuts_dialogs = NULL;
54
// Like UTF8ToString16, but treats NULL as "".
55
static bool MaybeUTF8ToString16(const char *in, std::string16 *out) {
56
return in == NULL || UTF8ToString16(in, out);
59
// Handles the results of the async icon fetch. It relies on
60
// ModelessShortcutsDialog to manage its lifetime.
61
class ShortcutIconHandler : public Desktop::IconHandlerInterface {
63
ShortcutIconHandler(ModelessShortcutsDialog *shortcut_dialog,
65
: shortcut_dialog_(shortcut_dialog),
67
abort_interface_(NULL) {
68
assert(0 <= index_ && index_ < 4);
71
~ShortcutIconHandler() {
75
virtual void ProcessIcon(bool success,
76
const std::string16 &icon_error) {
77
// TODO(mpcomplete): handle error?
78
// Specifically when !success.
79
shortcut_dialog_->IconRequestFinished(index_);
82
virtual Desktop::IconData *mutable_icon() {
85
return &shortcut_dialog_->shortcut_info_.icon16x16;
87
return &shortcut_dialog_->shortcut_info_.icon32x32;
89
return &shortcut_dialog_->shortcut_info_.icon48x48;
91
return &shortcut_dialog_->shortcut_info_.icon128x128;
96
virtual void set_abort_interface(Desktop::AbortInterface *abort_interface) {
97
abort_interface_ = abort_interface;
101
if (!abort_interface_) {
104
return abort_interface_->Abort();
108
ModelessShortcutsDialog *shortcut_dialog_;
110
Desktop::AbortInterface *abort_interface_;
111
DISALLOW_EVIL_CONSTRUCTORS(ShortcutIconHandler);
114
// Simply here so that ShortcutIconHandler can be forward
115
// declared in the header file.
116
ModelessShortcutsDialog::ModelessShortcutsDialog(CPBrowsingContext context)
117
: browsing_context_(context), results_ready_(false),
118
shortcut_data_(NULL) {
121
ModelessShortcutsDialog::~ModelessShortcutsDialog() {
124
static void ResultsReadyCallback(Json::Value *result, void *closure) {
125
ModelessShortcutsDialog* dialog =
126
static_cast<ModelessShortcutsDialog *>(closure);
127
dialog->ResultsReady();
130
bool ModelessShortcutsDialog::ShowDialog(GearsShortcutData2 *shortcut_data) {
131
const char* name = CP_GET_MINOR_VERSION(CP::version()) >= 6 ?
132
shortcut_data->orig_name : shortcut_data->name;
133
if (!UTF8ToString16(name, &shortcut_info_.app_name) ||
134
!UTF8ToString16(shortcut_data->url, &shortcut_info_.app_url) ||
135
!MaybeUTF8ToString16(shortcut_data->icons[0].url,
136
&shortcut_info_.icon16x16.url) ||
137
!MaybeUTF8ToString16(shortcut_data->icons[1].url,
138
&shortcut_info_.icon32x32.url) ||
139
!MaybeUTF8ToString16(shortcut_data->icons[2].url,
140
&shortcut_info_.icon48x48.url) ||
141
!MaybeUTF8ToString16(shortcut_data->icons[3].url,
142
&shortcut_info_.icon128x128.url) ||
143
!MaybeUTF8ToString16(shortcut_data->description,
144
&shortcut_info_.app_description)) {
148
// Make sure we pass validation on the shortcut name.
149
EnsureStringValidPathComponent(shortcut_info_.app_name, false);
150
if (shortcut_info_.app_name.length() > kUserPathComponentMaxChars)
151
shortcut_info_.app_name.resize(kUserPathComponentMaxChars);
153
// Only allow one dialog for this URL.
154
if (g_open_shortcuts_dialogs &&
155
g_open_shortcuts_dialogs->find(shortcut_info_.app_url) !=
156
g_open_shortcuts_dialogs->end())
159
SecurityOrigin security_origin;
160
if (!security_origin.InitFromUrl(shortcut_info_.app_url.c_str()))
163
// TODO(mpcomplete): We'll probably want to override behavior if the
164
// shortcut exists - ie, show an edit dialog.
165
// TODO(mpcomplete): The browsing_context Chrome gives us here is an HWND.
166
// We need to associate it with an URLRequestContext.
167
scoped_refptr<CRBrowsingContext> cr_context(
168
new CRBrowsingContext(browsing_context_));
169
desktop_.reset(new Desktop(security_origin, cr_context.get()));
170
dialog_.reset(new HtmlDialog(cr_context.get()));
172
const int kShortcutsDialogWidth = 360;
173
const int kShortcutsDialogHeight = 240;
175
if (!desktop_->ValidateShortcutInfo(&shortcut_info_, false) ||
177
!desktop_->InitializeDialog(&shortcut_info_,
179
Desktop::DIALOG_STYLE_SIMPLE) ||
180
dialog_->DoModeless(STRING16(L"shortcuts_dialog.html"),
181
kShortcutsDialogWidth, kShortcutsDialogHeight,
182
ResultsReadyCallback, this) != HTML_DIALOG_SUCCESS)
185
shortcut_data_ = shortcut_data;
187
// Keep track of this dialog being open.
188
RegisterOpenDialog(shortcut_info_.app_url);
192
void ModelessShortcutsDialog::ResultsReady() {
193
results_ready_ = true;
195
if (dialog_->result == Json::Value::null ||
196
!dialog_->result["allow"].isBool() ||
197
!dialog_->result["allow"].asBool()) {
198
// The user doesn't want to create the shortcut, so cancel the icon fetch
199
// and handle the results immediately.
200
AbortPrefetchIcons();
201
HandleDialogResults();
205
if (AreIconsReady() && results_ready_)
206
HandleDialogResults();
209
bool ModelessShortcutsDialog::PrefetchIcons() {
211
for (size_t i = 0; i < 4; ++i) {
212
icon_handler_[i].reset(new ShortcutIconHandler(this, i));
213
if (!desktop_->FetchIcon(icon_handler_[i]->mutable_icon(), &error,
214
icon_handler_[i].get())) {
222
void ModelessShortcutsDialog::AbortPrefetchIcons() {
223
for (size_t i = 0; i < 4; ++i) {
224
if (icon_handler_[i].get()) {
225
// Deleting icon handlers will abort any pending
227
icon_handler_[i].reset();
232
void ModelessShortcutsDialog::IconRequestFinished(int index) {
233
// Figure out which icon this fetch was for, and mark the fetch as done by
235
icon_handler_[index].reset();
236
if (AreIconsReady() && results_ready_)
237
HandleDialogResults();
240
bool ModelessShortcutsDialog::AreIconsReady() {
241
for (size_t i = 0; i < 4; ++i) {
242
if (icon_handler_[i].get())
249
void ModelessShortcutsDialog::HandleDialogResults() {
250
bool success = desktop_->HandleDialogResults(&shortcut_info_, dialog_.get());
251
UnregisterOpenDialog(shortcut_info_.app_url);
253
bool allow = dialog_->result["allow"].asBool();
255
// Notify Chrome that the user's choice is ready.
256
GearsCreateShortcutResult result = {
258
(allow && success) ? CPERR_SUCCESS : CPERR_FAILURE
260
CP::browser_funcs().handle_command(
261
CP::cpid(), browsing_context_,
262
GEARSBROWSERCOMMAND_CREATE_SHORTCUT_DONE, &result);
267
// Helper to convert a Desktop::ShortcutInfo to the right format for Chrome.
268
static void ConvertToGearsShortcutData(
269
const Desktop::ShortcutInfo &info,
270
GearsShortcutData *shortcut_data) {
271
memset(shortcut_data, 0, sizeof(*shortcut_data));
272
shortcut_data->url = CP::String16ToUTF8Dup(info.app_url);
273
shortcut_data->name = CP::String16ToUTF8Dup(info.app_name);
274
shortcut_data->description = CP::String16ToUTF8Dup(info.app_description);
275
if (!info.icon16x16.url.empty()) {
276
shortcut_data->icons[0].width = shortcut_data->icons[0].height = 16;
277
shortcut_data->icons[0].url = CP::String16ToUTF8Dup(info.icon16x16.url);
279
if (!info.icon32x32.url.empty()) {
280
shortcut_data->icons[1].width = shortcut_data->icons[1].height = 32;
281
shortcut_data->icons[1].url = CP::String16ToUTF8Dup(info.icon32x32.url);
283
if (!info.icon48x48.url.empty()) {
284
shortcut_data->icons[2].width = shortcut_data->icons[2].height = 48;
285
shortcut_data->icons[2].url = CP::String16ToUTF8Dup(info.icon48x48.url);
287
if (!info.icon128x128.url.empty()) {
288
shortcut_data->icons[3].width = shortcut_data->icons[3].height = 128;
289
shortcut_data->icons[3].url = CP::String16ToUTF8Dup(info.icon128x128.url);
293
bool GetAllShortcuts(GearsShortcutList *shortcut_list) {
294
// Build the list in a temporary vector for ease of use.
295
std::vector<GearsShortcutData> shortcuts;
296
size_t num_shortcuts = 0;
298
PermissionsDB *permissions = PermissionsDB::GetDB();
303
std::vector<SecurityOrigin> origins;
304
if (!permissions->GetOriginsWithShortcuts(&origins)) {
305
LOG16((L"GetOriginsWithShortcuts() failed"));
309
for (size_t i = 0; i < origins.size(); ++i) {
310
std::vector<std::string16> names;
311
if (!permissions->GetOriginShortcuts(origins[i], &names)) {
312
LOG16((L"GetOriginShortcuts(%s) failed", origins[i].full_url().c_str()));
316
// This is a conservative resize (since we skip disallowed shortcuts).
317
shortcuts.resize(num_shortcuts + names.size());
318
for (size_t j = 0; j < names.size(); ++j) {
319
Desktop::ShortcutInfo info;
321
if (!permissions->GetShortcut(origins[i], names[j].c_str(),
326
&info.icon128x128.url,
327
&info.app_description,
329
LOG16((L"GetShortcut(%s) failed",
330
origins[i].full_url().c_str(), names[j].c_str()));
334
continue; // Skip disallowed shortcuts.
336
info.app_name = names[j];
337
ConvertToGearsShortcutData(info, &shortcuts[num_shortcuts++]);
341
// Copy back out of our temporary vector.
342
shortcut_list->num_shortcuts = num_shortcuts;
343
if (num_shortcuts > 0) {
344
shortcut_list->shortcuts = CP::Alloc<GearsShortcutData>(num_shortcuts);
345
memcpy(shortcut_list->shortcuts, &shortcuts[0],
346
sizeof(GearsShortcutData) * num_shortcuts);
348
shortcut_list->shortcuts = NULL;