What's new? | Help | Directory | Sign in
Google
gears
Improving Your Web Browser
  
  
  
    
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
// Copyright 2007, Google Inc.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// 3. Neither the name of Google Inc. nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Windows-specific implementation of desktop shortcut creation.

// TODO(cprince): remove platform-specific #ifdef guards when OS-specific
// sources (e.g. WIN32_CPPSRCS) are implemented
#ifdef WIN32
#include <assert.h>
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <tchar.h>
#include <wininet.h>

#include "gears/desktop/desktop.h"

#include "gears/base/common/basictypes.h"
#include "gears/base/common/common.h"
#include "gears/base/common/file.h"
#include "gears/base/common/paths.h"
#include "gears/base/common/png_utils.h"
#include "gears/base/common/scoped_win32_handles.h"
#include "gears/base/common/security_model.h"
#include "gears/base/common/string16.h"
#include "gears/base/common/string_utils.h"
#include "gears/base/common/url_utils.h"
#if BROWSER_IE
#include "gears/base/common/vista_utils.h"
#endif
#ifdef WINCE
#include "gears/desktop/dll_data_wince.h"
#endif
#include "gears/desktop/shortcut_utils_win32.h"
#include "gears/localserver/common/http_constants.h"
#include "third_party/scoped_ptr/scoped_ptr.h"
#include "genfiles/product_constants.h"

struct IcoHeader {
uint16 reserved;
uint16 type;
uint16 count;
};

struct IcoDirectory {
uint8 width;
uint8 height;
uint8 color_count;
uint8 reserved;
uint16 planes;
uint16 bpp;
uint32 data_size;
uint32 offset;
};

#ifdef WINCE
bool WriteIconAsDLL(const char16 *file_path,
const uint8 *icon_data,
const int data_size) {
// On WinCE, a shortcut can only make use of an icon embedded in a module as
// a resource. There seems to be no documentation on the internal structure of
// a WinCE EXE or DLL, so we can't create one from scratch on the fly. Instead
// we use 'prototype' DLLs which contains no code, only a single icon. We
// take a copy of that DLL and replace the icon with the required icon.
//
// There seems to be no documentation on the format used to represent an icon
// when embedded in a module. Testing shows that it is not the same as on
// Win32 (see http://msdn2.microsoft.com/en-us/library/ms997538.aspx). First,
// the ICONIMAGE structure does not contain the AND mask (BITMAPINFOHEADER,
// color table and XOR mask only). The data for each image is followed by a
// footer in place of the AND mask. Also, the headers at the start of the
// resource differ and the presence of the icon affects other areas of the
// DLL. For these reasons, we use a separate prototype DLL for each of the two
// sets of image sizes we support.

// Read the icon header data, storing size information for each image. After
// this, icon_data points to the start of the bitmap data for the first image.
const IcoHeader *header = reinterpret_cast<const IcoHeader*>(icon_data);
icon_data += sizeof(IcoHeader);
struct IconImageData {
int image_size; // BITMAPINFOHEADER, color table, XOR mask and AND mask.
int and_mask_size;
const uint8 *footer;
int footer_size;
};
std::vector<IconImageData> icon_image_data;
for (int i = 0; i < header->count; ++i) {
const IcoDirectory* directory =
reinterpret_cast<const IcoDirectory*>(icon_data);
icon_data += sizeof(IcoDirectory);
int width = directory->width;
int height = directory->height;
// We only support square images.
assert(width == height);
IconImageData data;
int mask_row_bytes = width / 8;
mask_row_bytes = ((mask_row_bytes + 3) / 4) * 4; // round up, multiple of 4
data.and_mask_size = height * mask_row_bytes;
data.image_size = sizeof(BITMAPINFOHEADER) +
4 * height * width + // Color data
height * mask_row_bytes + // XOR mask
data.and_mask_size; // AND mask
icon_image_data.push_back(data);
}

// For now, we only support two cases ...
// - a single 16x16 image
// - a 32x32 followed by a 16x16 image
// Note that CreateIcoFile ensures that icon images are present in the icon
// data in order of decreasing size.
const int kImageSize16 =
sizeof(BITMAPINFOHEADER) + 4 * 16 * 16 + 16 * 4 + 16 * 4;
const int kImageSize32 =
sizeof(BITMAPINFOHEADER) + 4 * 32 * 32 + 32 * 4 + 32 * 4;
const uint8 *icon_dll_begin;
const uint8 *icon_dll_header;
const uint8 *icon_dll_end;
int icon_dll_begin_size;
int icon_dll_header_size;
int icon_dll_end_size;
if (icon_image_data.size() == 1) {
assert(icon_image_data[0].image_size == kImageSize16);
icon_dll_begin = reinterpret_cast<const uint8*>(kIcon16DllBegin);
icon_dll_begin_size = kIcon16DllBeginSize;
icon_dll_header = reinterpret_cast<const uint8*>(kIcon16DllHeader);
icon_dll_header_size = kIcon16DllHeaderSize;
icon_image_data[0].footer =
reinterpret_cast<const uint8*>(kIcon16Image16Footer);
icon_image_data[0].footer_size = kIcon16Image16FooterSize;
icon_dll_end = reinterpret_cast<const uint8*>(kIcon16DllEnd);
icon_dll_end_size = kIcon16DllEndSize;
} else if (icon_image_data.size() == 2) {
assert(icon_image_data[0].image_size == kImageSize32 &&
icon_image_data[1].image_size == kImageSize16);
icon_dll_begin = reinterpret_cast<const uint8*>(kIcon32and16DllBegin);
icon_dll_begin_size = kIcon32and16DllBeginSize;
icon_dll_header = reinterpret_cast<const uint8*>(kIcon32and16DllHeader);
icon_dll_header_size = kIcon32and16DllHeaderSize;
icon_image_data[0].footer =
reinterpret_cast<const uint8*>(kIcon32and16Image32Footer);
icon_image_data[0].footer_size = kIcon32and16Image32FooterSize;
icon_image_data[1].footer =
reinterpret_cast<const uint8*>(kIcon32and16Image16Footer);
icon_image_data[1].footer_size = kIcon32and16Image16FooterSize;
icon_dll_end = reinterpret_cast<const uint8*>(kIcon32and16DllEnd);
icon_dll_end_size = kIcon32and16DllEndSize;
} else {
assert(false);
return false;
}

// Calculate the file size.
int file_size = icon_dll_begin_size +
icon_dll_header_size +
icon_dll_end_size;
for (int i = 0; i < static_cast<int>(icon_image_data.size()); ++i) {
file_size += icon_image_data[i].image_size -
icon_image_data[i].and_mask_size +
icon_image_data[i].footer_size;

}
uint8 *data = new uint8[file_size];
if (NULL == data) {
return false;
}
int data_index = 0;

// Write the start of the DLL.
memcpy(&data[data_index], icon_dll_begin, icon_dll_begin_size);
data_index += icon_dll_begin_size;

// Write the icon header.
memcpy(&data[data_index], icon_dll_header, icon_dll_header_size);
data_index += icon_dll_header_size;

// Write the icon data.
for (int i = 0; i < static_cast<int>(icon_image_data.size()); ++i) {
memcpy(&data[data_index],
icon_data,
icon_image_data[i].image_size - icon_image_data[i].and_mask_size);
data_index += icon_image_data[i].image_size -
icon_image_data[i].and_mask_size;
memcpy(&data[data_index],
icon_image_data[i].footer,
icon_image_data[i].footer_size);
data_index += icon_image_data[i].footer_size;
icon_data += icon_image_data[i].image_size;
}

// Write the end of the DLL.
memcpy(&data[data_index], icon_dll_end, icon_dll_end_size);

// Write the data to file.
// If the file already exists, CreateNewFile will fail, but that's OK.
File::CreateNewFile(file_path);
if (!File::WriteBytesToFile(file_path, data, file_size)) {
return false;
}
return true;
}
#endif // WINCE

// Creates the icon file which contains the various different sized icons.
static bool CreateIcoFile(const std::string16 &icons_path,
const Desktop::ShortcutInfo &shortcut) {
std::vector<const Desktop::IconData *> icons_to_write;

// Add each icon size that has been provided to the icon list.
#ifdef WINCE
// We don't use 128x128 or 48x48 on WinCE.
#else
// We ignore 128x128 because it isn't supported by Windows.
if (!shortcut.icon48x48.raw_data.empty()) {
icons_to_write.push_back(&shortcut.icon48x48);
}
#endif
if (!shortcut.icon32x32.raw_data.empty()) {
icons_to_write.push_back(&shortcut.icon32x32);
}

if (!shortcut.icon16x16.raw_data.empty()) {
icons_to_write.push_back(&shortcut.icon16x16);
}

// Make sure we have at least one icon.
if (icons_to_write.empty()) {
return false;
}

// Initialize to the size of the header.
int data_size = sizeof(IcoHeader);

for (size_t i = 0; i < icons_to_write.size(); ++i) {
// Increase data_size by size of the icon data.
data_size += sizeof(BITMAPINFOHEADER);

// Increase data_size by size of the image and mask data.

// Note: in the .ico format, each *row* of image and mask data must be
// a multiple of 4 bytes. Pad if necessary.
int row_bytes;

// 32 bits per pixel for the image data.
row_bytes = icons_to_write[i]->width * 4; // already a multiple of 4
data_size += row_bytes * icons_to_write[i]->height;

// 1 bit per pixel for the XOR mask, then 1 bit per pixel for the AND mask.
row_bytes = icons_to_write[i]->width / 8;
row_bytes = ((row_bytes + 3) / 4) * 4; // round up to multiple of 4
data_size += row_bytes * icons_to_write[i]->height; // XOR mask
data_size += row_bytes * icons_to_write[i]->height; // AND mask

// Increase data_size by size of directory entry.
data_size += sizeof(IcoDirectory);
}

// Allocate the space for the icon.
uint8 *data = new uint8[data_size];
memset(data, 0, data_size);

IcoHeader *icon_header = reinterpret_cast<IcoHeader *>(data);
icon_header->reserved = 0; // Must be 0;
icon_header->type = 1; // 1 for ico.
icon_header->count = icons_to_write.size();

// Icon image data starts past the header and the directory.
int dest_offset =
sizeof(IcoHeader) +
icons_to_write.size() * sizeof(IcoDirectory);
for (size_t i = 0; i < icons_to_write.size(); ++i) {
IcoDirectory directory;
directory.width = icons_to_write[i]->width;
directory.height = icons_to_write[i]->height;
directory.color_count = 0;
directory.reserved = 0;
directory.planes = 1;
directory.bpp = 32;

// Size of the header + size of the pixels + size of the bitmask.
// TODO: Is this correct? Should it account for rounding to multiples of 4?
directory.data_size =
sizeof(BITMAPINFOHEADER) +
4 * icons_to_write[i]->width * icons_to_write[i]->height +
icons_to_write[i]->width * icons_to_write[i]->height / 4;

directory.offset = dest_offset;

BITMAPINFOHEADER bmp_header;
memset(&bmp_header, 0, sizeof(bmp_header));
bmp_header.biSize = sizeof(bmp_header);
bmp_header.biWidth = icons_to_write[i]->width;
// 'biHeight' is the combined height of the XOR and AND masks.
bmp_header.biHeight = icons_to_write[i]->height * 2;
bmp_header.biPlanes = 1;
bmp_header.biBitCount = 32;

// Write the directory entry.
memcpy(&data[sizeof(IcoHeader) + i * sizeof(IcoDirectory)],
reinterpret_cast<uint8 *>(&directory),
sizeof(IcoDirectory));

// Write the bitmap header to the data segment.
memcpy(&data[dest_offset],
reinterpret_cast<uint8 *>(&bmp_header),
sizeof(BITMAPINFOHEADER));

// Move the offset past the header.
dest_offset += sizeof(BITMAPINFOHEADER);

// Write the color data.
// Note that icon rows are stored bottom to top, so we flip the row order.
for (int row = (icons_to_write[i]->height - 1); row >= 0; --row) {
int src_row_offset = row * icons_to_write[i]->width * 4;

// Copy a single row.
memcpy(&data[dest_offset],
reinterpret_cast<const uint8*>(
&icons_to_write[i]->raw_data.at(src_row_offset)),
4 * icons_to_write[i]->width);

// Move the write offset forward one row.
dest_offset += 4 * icons_to_write[i]->width;
}

// Compute mask information.
int mask_row_bytes = icons_to_write[i]->width / 8;
mask_row_bytes = ((mask_row_bytes + 3) / 4) * 4; // round up, multiple of 4

// Write the XOR mask.
for (int row = (icons_to_write[i]->height - 1); row >= 0; --row) {
// 'stripe' is an 8-column segment, for grouping 1bpp data into bytes
for (int stripe = 0; stripe < (icons_to_write[i]->width / 8); ++stripe) {
int xor_mask_value = 0;
for (int bit = 0; bit < 8; ++bit) {
// If alpha is 0x00, make bit transparent (1), else leave opaque (0).
int raw_pixel_offset =
(((row * icons_to_write[i]->width) + (stripe * 8) + bit) * 4);
if (0 == icons_to_write[i]->raw_data.at(raw_pixel_offset + 3)) {
xor_mask_value |= (0x80 >> bit);
}
}
data[dest_offset + stripe] = static_cast<uint8>(xor_mask_value);
}
// Update offset *after* finishing mask row, as it may include padding.
dest_offset += mask_row_bytes;
}

// Move the write offset past the AND mask (unused in WinXP and later).
dest_offset += mask_row_bytes * icons_to_write[i]->height;
}

#ifdef WINCE
// On WinCE, we don't write the icon directly, but embed it in a DLL.
bool success = WriteIconAsDLL(icons_path.c_str(), data, data_size);
#else
File::CreateNewFile(icons_path.c_str());
bool success = File::WriteBytesToFile(icons_path.c_str(), data, data_size);
#endif

delete[] data;

return success;
}

bool Desktop::CreateShortcutPlatformImpl(const SecurityOrigin &origin,
const ShortcutInfo &shortcut,
uint32 locations,
std::string16 *error) {
char16 browser_path[MAX_PATH] = {0};

if (!GetModuleFileName(NULL, browser_path, MAX_PATH)) {
*error = GET_INTERNAL_ERROR_MESSAGE();
return false;
}

std::string16 icons_path;
if (!GetDataDirectory(origin, &icons_path)) {
*error = GET_INTERNAL_ERROR_MESSAGE();
return false;
}

AppendDataName(STRING16(L"icons"), kDataSuffixForDesktop, &icons_path);
icons_path += kPathSeparator;
icons_path += shortcut.app_name;
#ifdef WINCE
// On WinCE, we don't write the icon directly, but embed it in a DLL. We don't
// use extension '.dll' because this casues some devices to warn the user that
// they are running an untrusted application when the icon is used.
icons_path += STRING16(L".icon");
#else
icons_path += STRING16(L".ico");
#endif

if (!CreateIcoFile(icons_path, shortcut)) {
*error = GET_INTERNAL_ERROR_MESSAGE();
return false;
}

#if BROWSER_IE
if (VistaUtils::IsRunningOnVista()) {
std::string16 broker_path;
if (!GetInstallDirectory(&broker_path)) {
*error = GET_INTERNAL_ERROR_MESSAGE();
return false;
}
broker_path += STRING16(L"\\vista_broker.exe");

// Build up the command line
std::string16 locations_string = IntegerToString16(locations);
const char16 *command_line_parts[] = {
broker_path.c_str(),
shortcut.app_name.c_str(),
browser_path,
shortcut.app_url.c_str(),
icons_path.c_str(),
locations_string.c_str(),
};

std::string16 command_line;
for (int i = 0; i < ARRAYSIZE(command_line_parts); ++i) {
// Escape any double quotes since we also use them for argument
// delimiters.
std::string16 part(command_line_parts[i]);
ReplaceAll(part, std::string16(STRING16(L"\\")),
std::string16(STRING16(L"\\\\")));

command_line += STRING16(L"\"");
command_line += part;
command_line += STRING16(L"\" ");
}

STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
PROCESS_INFORMATION process_info = {0};

BOOL success = CreateProcessW(NULL, // application name (NULL to get from
// command line)
const_cast<char16 *>(command_line.c_str()),
NULL, // process attributes (NULL means
// process handle not inheritable)
NULL, // thread attributes (NULL means thread
// handle not inheritable)
FALSE, // inherit handles
0, // creation flags
NULL, // environment block (NULL to use
// parent's)
NULL, // starting block (NULL to use
// parent's)
&startup_info,
&process_info);
if (!success) {
*error = GET_INTERNAL_ERROR_MESSAGE();
return false;
}

// To make sure that process and thread handles gets destroyed on exit.
SAFE_HANDLE process_handle(process_info.hProcess);
SAFE_HANDLE thread_handle(process_info.hThread);

// Wait for process to exit.
const int kTimeoutMs = 10000;
if (WaitForSingleObject(process_info.hProcess,
kTimeoutMs) != WAIT_OBJECT_0) {
*error = GET_INTERNAL_ERROR_MESSAGE();
return false;
}

DWORD return_value = 0;
if (!GetExitCodeProcess(process_info.hProcess, &return_value)) {
*error = GET_INTERNAL_ERROR_MESSAGE();
return false;
}

if (return_value != 0) {
*error = STRING16(L"Could not create shortcut. Broker failed on line: ");
*error += IntegerToString16(return_value);
*error += STRING16(L".");
return false;
}
return true;
}
#endif

for (uint32 location = 0x8000; location > 0; location >>= 1) {
if (locations & location) {
if (!CreateShortcutFileWin32(shortcut.app_name, browser_path,
shortcut.app_url, icons_path,
location, error)) {
return false;
}
}
}
return true;
}
#endif // #ifdef WIN32
Show details Hide details

Change log

r1532 by gears.daemon on May 06, 2008   Diff
[Author: aa]

Fix for: http://code.google.com/p/google-
gears/issues/detail?id=472

In Firefox 3, the trick of using rundll32
to send the crash reports was not
working for a variety of reasons.
Refactored to use a separate executable,
which seems safer long-term, and probably
more like what we will do for
other platforms.
...
Go to: 
Project members, sign in to write a code review

Older revisions

r1491 by gears.daemon on Apr 30, 2008   Diff
[Author: mpcomplete]

Factor shortcut creation code that
doesn't rely on a JS context out of
GearsDesktop.
...
r1455 by gears.daemon on Apr 23, 2008   Diff
[Author: zork]

Add the ability for Gears to put
shortcuts on the Start Menu and Quick
Launch bars.
...
r1361 by gears.daemon on Apr 11, 2008   Diff
[Author: cprince]

Gears Relocation: update files to
reflect new /third_party location.

...
All revisions of this file

File info

Size: 19583 bytes, 517 lines