web_app.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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#include "chrome/browser/web_applications/web_app.h" 6 7#if defined(OS_WIN) 8#include <shellapi.h> 9#include <shlobj.h> 10#endif // defined(OS_WIN) 11 12#include <algorithm> 13#include <string> 14#include <vector> 15 16#include "base/callback.h" 17#include "base/file_util.h" 18#include "base/md5.h" 19#include "base/message_loop.h" 20#include "base/path_service.h" 21#include "base/thread.h" 22#include "base/scoped_ptr.h" 23#include "base/utf_string_conversions.h" 24#include "base/win/windows_version.h" 25#include "chrome/browser/browser_thread.h" 26#include "chrome/browser/download/download_util.h" 27#include "chrome/browser/profiles/profile.h" 28#include "chrome/browser/tab_contents/tab_contents.h" 29#include "chrome/common/chrome_constants.h" 30#include "chrome/common/chrome_paths.h" 31#include "chrome/common/chrome_plugin_util.h" 32#include "chrome/common/notification_registrar.h" 33#include "chrome/common/url_constants.h" 34#include "chrome/common/web_apps.h" 35 36#if defined(OS_LINUX) 37#include "base/environment.h" 38#endif // defined(OS_LINUX) 39 40#if defined(OS_WIN) 41#include "base/win_util.h" 42#include "chrome/common/notification_details.h" 43#include "chrome/common/notification_source.h" 44#include "gfx/icon_util.h" 45#endif // defined(OS_WIN) 46 47namespace { 48 49#if defined(OS_WIN) 50const FilePath::CharType kIconChecksumFileExt[] = FILE_PATH_LITERAL(".ico.md5"); 51 52// Returns true if |ch| is in visible ASCII range and not one of 53// "/ \ : * ? " < > | ; ,". 54bool IsValidFilePathChar(char16 c) { 55 if (c < 32) 56 return false; 57 58 switch (c) { 59 case '/': 60 case '\\': 61 case ':': 62 case '*': 63 case '?': 64 case '"': 65 case '<': 66 case '>': 67 case '|': 68 case ';': 69 case ',': 70 return false; 71 }; 72 73 return true; 74} 75 76// Returns sanitized name that could be used as a file name 77FilePath GetSanitizedFileName(const string16& name) { 78 string16 file_name; 79 80 for (size_t i = 0; i < name.length(); ++i) { 81 char16 c = name[i]; 82 if (!IsValidFilePathChar(c)) 83 c = '_'; 84 85 file_name += c; 86 } 87 88 return FilePath(file_name); 89} 90#endif // defined(OS_WIN) 91 92// Returns relative directory of given web app url. 93FilePath GetWebAppDir(const GURL& url) { 94 FilePath::StringType host; 95 FilePath::StringType scheme_port; 96 97#if defined(OS_WIN) 98 host = UTF8ToWide(url.host()); 99 scheme_port = (url.has_scheme() ? UTF8ToWide(url.scheme()) : L"http") + 100 FILE_PATH_LITERAL("_") + 101 (url.has_port() ? UTF8ToWide(url.port()) : L"80"); 102#elif defined(OS_POSIX) 103 host = url.host(); 104 scheme_port = url.scheme() + FILE_PATH_LITERAL("_") + url.port(); 105#endif 106 107 return FilePath(host).Append(scheme_port); 108} 109 110// Returns data directory for given web app url 111FilePath GetWebAppDataDirectory(const FilePath& root_dir, 112 const GURL& url) { 113 return root_dir.Append(GetWebAppDir(url)); 114} 115 116#if defined(TOOLKIT_VIEWS) 117// Predicator for sorting images from largest to smallest. 118bool IconPrecedes(const WebApplicationInfo::IconInfo& left, 119 const WebApplicationInfo::IconInfo& right) { 120 return left.width < right.width; 121} 122#endif 123 124#if defined(OS_WIN) 125// Calculates image checksum using MD5. 126void GetImageCheckSum(const SkBitmap& image, MD5Digest* digest) { 127 DCHECK(digest); 128 129 SkAutoLockPixels image_lock(image); 130 MD5Sum(image.getPixels(), image.getSize(), digest); 131} 132 133// Saves |image| as an |icon_file| with the checksum. 134bool SaveIconWithCheckSum(const FilePath& icon_file, const SkBitmap& image) { 135 if (!IconUtil::CreateIconFileFromSkBitmap(image, icon_file)) 136 return false; 137 138 MD5Digest digest; 139 GetImageCheckSum(image, &digest); 140 141 FilePath cheksum_file(icon_file.ReplaceExtension(kIconChecksumFileExt)); 142 return file_util::WriteFile(cheksum_file, 143 reinterpret_cast<const char*>(&digest), 144 sizeof(digest)) == sizeof(digest); 145} 146 147// Returns true if |icon_file| is missing or different from |image|. 148bool ShouldUpdateIcon(const FilePath& icon_file, const SkBitmap& image) { 149 FilePath checksum_file(icon_file.ReplaceExtension(kIconChecksumFileExt)); 150 151 // Returns true if icon_file or checksum file is missing. 152 if (!file_util::PathExists(icon_file) || 153 !file_util::PathExists(checksum_file)) 154 return true; 155 156 MD5Digest persisted_image_checksum; 157 if (sizeof(persisted_image_checksum) != file_util::ReadFile(checksum_file, 158 reinterpret_cast<char*>(&persisted_image_checksum), 159 sizeof(persisted_image_checksum))) 160 return true; 161 162 MD5Digest downloaded_image_checksum; 163 GetImageCheckSum(image, &downloaded_image_checksum); 164 165 // Update icon if checksums are not equal. 166 return memcmp(&persisted_image_checksum, &downloaded_image_checksum, 167 sizeof(MD5Digest)) != 0; 168} 169 170// Saves |image| to |icon_file| if the file is outdated and refresh shell's 171// icon cache to ensure correct icon is displayed. Returns true if icon_file 172// is up to date or successfully updated. 173bool CheckAndSaveIcon(const FilePath& icon_file, const SkBitmap& image) { 174 if (ShouldUpdateIcon(icon_file, image)) { 175 if (SaveIconWithCheckSum(icon_file, image)) { 176 // Refresh shell's icon cache. This call is quite disruptive as user would 177 // see explorer rebuilding the icon cache. It would be great that we find 178 // a better way to achieve this. 179 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, 180 NULL, NULL); 181 } else { 182 return false; 183 } 184 } 185 186 return true; 187} 188 189#endif // defined(OS_WIN) 190 191// Represents a task that creates web application shortcut. This runs on 192// file thread and schedules the callback (if any) on the calling thread 193// when finished (either success or failure). 194class CreateShortcutTask : public Task { 195 public: 196 CreateShortcutTask(const FilePath& profile_path, 197 const ShellIntegration::ShortcutInfo& shortcut_info, 198 web_app::CreateShortcutCallback* callback); 199 200 private: 201 class CreateShortcutCallbackTask : public Task { 202 public: 203 CreateShortcutCallbackTask(web_app::CreateShortcutCallback* callback, 204 bool success) 205 : callback_(callback), 206 success_(success) { 207 } 208 209 // Overridden from Task: 210 virtual void Run() { 211 callback_->Run(success_); 212 } 213 214 private: 215 web_app::CreateShortcutCallback* callback_; 216 bool success_; 217 }; 218 219 // Overridden from Task: 220 virtual void Run(); 221 222 // Returns true if shortcut is created successfully. 223 bool CreateShortcut(); 224 225 // Path to store persisted data for web app. 226 FilePath web_app_path_; 227 228 // Out copy of profile path. 229 FilePath profile_path_; 230 231 // Our copy of short cut data. 232 ShellIntegration::ShortcutInfo shortcut_info_; 233 234 // Callback when task is finished. 235 web_app::CreateShortcutCallback* callback_; 236 MessageLoop* message_loop_; 237 238 DISALLOW_COPY_AND_ASSIGN(CreateShortcutTask); 239}; 240 241CreateShortcutTask::CreateShortcutTask( 242 const FilePath& profile_path, 243 const ShellIntegration::ShortcutInfo& shortcut_info, 244 web_app::CreateShortcutCallback* callback) 245 : web_app_path_(GetWebAppDataDirectory(web_app::GetDataDir(profile_path), 246 shortcut_info.url)), 247 profile_path_(profile_path), 248 shortcut_info_(shortcut_info), 249 callback_(callback), 250 message_loop_(MessageLoop::current()) { 251 DCHECK(message_loop_ != NULL); 252} 253 254void CreateShortcutTask::Run() { 255 bool success = CreateShortcut(); 256 257 if (callback_ != NULL) 258 message_loop_->PostTask(FROM_HERE, 259 new CreateShortcutCallbackTask(callback_, success)); 260} 261 262bool CreateShortcutTask::CreateShortcut() { 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 264 265#if defined(OS_LINUX) 266 scoped_ptr<base::Environment> env(base::Environment::Create()); 267 268 std::string shortcut_template; 269 if (!ShellIntegration::GetDesktopShortcutTemplate(env.get(), 270 &shortcut_template)) { 271 return false; 272 } 273 ShellIntegration::CreateDesktopShortcut(shortcut_info_, shortcut_template); 274 return true; // assuming always success. 275#elif defined(OS_WIN) 276 // Shortcut paths under which to create shortcuts. 277 std::vector<FilePath> shortcut_paths; 278 279 // Locations to add to shortcut_paths. 280 struct { 281 const bool& use_this_location; 282 int location_id; 283 const wchar_t* sub_dir; 284 } locations[] = { 285 { 286 shortcut_info_.create_on_desktop, 287 chrome::DIR_USER_DESKTOP, 288 NULL 289 }, { 290 shortcut_info_.create_in_applications_menu, 291 base::DIR_START_MENU, 292 NULL 293 }, { 294 shortcut_info_.create_in_quick_launch_bar, 295 // For Win7, create_in_quick_launch_bar means pinning to taskbar. Use 296 // base::PATH_START as a flag for this case. 297 (base::win::GetVersion() >= base::win::VERSION_WIN7) ? 298 base::PATH_START : base::DIR_APP_DATA, 299 (base::win::GetVersion() >= base::win::VERSION_WIN7) ? 300 NULL : L"Microsoft\\Internet Explorer\\Quick Launch" 301 } 302 }; 303 304 // Populate shortcut_paths. 305 for (int i = 0; i < arraysize(locations); ++i) { 306 if (locations[i].use_this_location) { 307 FilePath path; 308 309 // Skip the Win7 case. 310 if (locations[i].location_id == base::PATH_START) 311 continue; 312 313 if (!PathService::Get(locations[i].location_id, &path)) { 314 NOTREACHED(); 315 return false; 316 } 317 318 if (locations[i].sub_dir != NULL) 319 path = path.Append(locations[i].sub_dir); 320 321 shortcut_paths.push_back(path); 322 } 323 } 324 325 bool pin_to_taskbar = 326 shortcut_info_.create_in_quick_launch_bar && 327 (base::win::GetVersion() >= base::win::VERSION_WIN7); 328 329 // For Win7's pinning support, any shortcut could be used. So we only create 330 // the shortcut file when there is no shortcut file will be created. That is, 331 // user only selects "Pin to taskbar". 332 if (pin_to_taskbar && shortcut_paths.empty()) { 333 // Creates the shortcut in web_app_path_ in this case. 334 shortcut_paths.push_back(web_app_path_); 335 } 336 337 if (shortcut_paths.empty()) { 338 NOTREACHED(); 339 return false; 340 } 341 342 // Ensure web_app_path_ exists. 343 if (!file_util::PathExists(web_app_path_) && 344 !file_util::CreateDirectory(web_app_path_)) { 345 NOTREACHED(); 346 return false; 347 } 348 349 // Generates file name to use with persisted ico and shortcut file. 350 FilePath file_name = GetSanitizedFileName(shortcut_info_.title); 351 352 // Creates an ico file to use with shortcut. 353 FilePath icon_file = web_app_path_.Append(file_name).ReplaceExtension( 354 FILE_PATH_LITERAL(".ico")); 355 if (!CheckAndSaveIcon(icon_file, shortcut_info_.favicon)) { 356 NOTREACHED(); 357 return false; 358 } 359 360 FilePath chrome_exe; 361 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 362 NOTREACHED(); 363 return false; 364 } 365 366 // Working directory. 367 FilePath chrome_folder = chrome_exe.DirName(); 368 369 std::string switches = 370 ShellIntegration::GetCommandLineArgumentsCommon(shortcut_info_.url, 371 shortcut_info_.extension_id); 372 std::wstring wide_switchs(UTF8ToWide(switches)); 373 374 // Sanitize description 375 if (shortcut_info_.description.length() >= MAX_PATH) 376 shortcut_info_.description.resize(MAX_PATH - 1); 377 378 // Generates app id from web app url and profile path. 379 std::wstring app_id = ShellIntegration::GetAppId( 380 UTF8ToWide(web_app::GenerateApplicationNameFromURL(shortcut_info_.url)), 381 profile_path_); 382 383 FilePath shortcut_to_pin; 384 385 bool success = true; 386 for (size_t i = 0; i < shortcut_paths.size(); ++i) { 387 FilePath shortcut_file = shortcut_paths[i].Append(file_name). 388 ReplaceExtension(FILE_PATH_LITERAL(".lnk")); 389 390 int unique_number = download_util::GetUniquePathNumber(shortcut_file); 391 if (unique_number == -1) { 392 success = false; 393 continue; 394 } else if (unique_number > 0) { 395 download_util::AppendNumberToPath(&shortcut_file, unique_number); 396 } 397 398 success &= file_util::CreateShortcutLink(chrome_exe.value().c_str(), 399 shortcut_file.value().c_str(), 400 chrome_folder.value().c_str(), 401 wide_switchs.c_str(), 402 shortcut_info_.description.c_str(), 403 icon_file.value().c_str(), 404 0, 405 app_id.c_str()); 406 407 // Any shortcut would work for the pinning. We use the first one. 408 if (success && pin_to_taskbar && shortcut_to_pin.empty()) 409 shortcut_to_pin = shortcut_file; 410 } 411 412 if (success && pin_to_taskbar) { 413 if (!shortcut_to_pin.empty()) { 414 success &= file_util::TaskbarPinShortcutLink( 415 shortcut_to_pin.value().c_str()); 416 } else { 417 NOTREACHED(); 418 success = false; 419 } 420 } 421 422 return success; 423#else 424 NOTIMPLEMENTED(); 425 return false; 426#endif 427} 428 429#if defined(OS_WIN) 430// UpdateShortcutWorker holds all context data needed for update shortcut. 431// It schedules a pre-update check to find all shortcuts that needs to be 432// updated. If there are such shortcuts, it schedules icon download and 433// update them when icons are downloaded. It observes TAB_CLOSING notification 434// and cancels all the work when the underlying tab is closing. 435class UpdateShortcutWorker : public NotificationObserver { 436 public: 437 explicit UpdateShortcutWorker(TabContents* tab_contents); 438 439 void Run(); 440 441 private: 442 // Overridden from NotificationObserver: 443 virtual void Observe(NotificationType type, 444 const NotificationSource& source, 445 const NotificationDetails& details); 446 447 // Downloads icon via TabContents. 448 void DownloadIcon(); 449 450 // Callback when icon downloaded. 451 void OnIconDownloaded(int download_id, bool errored, const SkBitmap& image); 452 453 // Checks if shortcuts exists on desktop, start menu and quick launch. 454 void CheckExistingShortcuts(); 455 456 // Update shortcut files and icons. 457 void UpdateShortcuts(); 458 void UpdateShortcutsOnFileThread(); 459 460 // Callback after shortcuts are updated. 461 void OnShortcutsUpdated(bool); 462 463 // Deletes the worker on UI thread where it gets created. 464 void DeleteMe(); 465 void DeleteMeOnUIThread(); 466 467 NotificationRegistrar registrar_; 468 469 // Underlying TabContents whose shortcuts will be updated. 470 TabContents* tab_contents_; 471 472 // Icons info from tab_contents_'s web app data. 473 web_app::IconInfoList unprocessed_icons_; 474 475 // Cached shortcut data from the tab_contents_. 476 ShellIntegration::ShortcutInfo shortcut_info_; 477 478 // Our copy of profile path. 479 FilePath profile_path_; 480 481 // File name of shortcut/ico file based on app title. 482 FilePath file_name_; 483 484 // Existing shortcuts. 485 std::vector<FilePath> shortcut_files_; 486 487 DISALLOW_COPY_AND_ASSIGN(UpdateShortcutWorker); 488}; 489 490UpdateShortcutWorker::UpdateShortcutWorker(TabContents* tab_contents) 491 : tab_contents_(tab_contents), 492 profile_path_(tab_contents->profile()->GetPath()) { 493 web_app::GetShortcutInfoForTab(tab_contents_, &shortcut_info_); 494 web_app::GetIconsInfo(tab_contents_->web_app_info(), &unprocessed_icons_); 495 file_name_ = GetSanitizedFileName(shortcut_info_.title); 496 497 registrar_.Add(this, NotificationType::TAB_CLOSING, 498 Source<NavigationController>(&tab_contents_->controller())); 499} 500 501void UpdateShortcutWorker::Run() { 502 // Starting by downloading app icon. 503 DownloadIcon(); 504} 505 506void UpdateShortcutWorker::Observe(NotificationType type, 507 const NotificationSource& source, 508 const NotificationDetails& details) { 509 if (type == NotificationType::TAB_CLOSING && 510 Source<NavigationController>(source).ptr() == 511 &tab_contents_->controller()) { 512 // Underlying tab is closing. 513 tab_contents_ = NULL; 514 } 515} 516 517void UpdateShortcutWorker::DownloadIcon() { 518 // FetchIcon must run on UI thread because it relies on TabContents 519 // to download the icon. 520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 521 522 if (tab_contents_ == NULL) { 523 DeleteMe(); // We are done if underlying TabContents is gone. 524 return; 525 } 526 527 if (unprocessed_icons_.empty()) { 528 // No app icon. Just use the favicon from TabContents. 529 UpdateShortcuts(); 530 return; 531 } 532 533 tab_contents_->fav_icon_helper().DownloadImage( 534 unprocessed_icons_.back().url, 535 std::max(unprocessed_icons_.back().width, 536 unprocessed_icons_.back().height), 537 NewCallback(this, &UpdateShortcutWorker::OnIconDownloaded)); 538 unprocessed_icons_.pop_back(); 539} 540 541void UpdateShortcutWorker::OnIconDownloaded(int download_id, 542 bool errored, 543 const SkBitmap& image) { 544 if (tab_contents_ == NULL) { 545 DeleteMe(); // We are done if underlying TabContents is gone. 546 return; 547 } 548 549 if (!errored && !image.isNull()) { 550 // Update icon with download image and update shortcut. 551 shortcut_info_.favicon = image; 552 tab_contents_->SetAppIcon(image); 553 UpdateShortcuts(); 554 } else { 555 // Try the next icon otherwise. 556 DownloadIcon(); 557 } 558} 559 560void UpdateShortcutWorker::CheckExistingShortcuts() { 561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 562 563 // Locations to check to shortcut_paths. 564 struct { 565 bool& use_this_location; 566 int location_id; 567 const wchar_t* sub_dir; 568 } locations[] = { 569 { 570 shortcut_info_.create_on_desktop, 571 chrome::DIR_USER_DESKTOP, 572 NULL 573 }, { 574 shortcut_info_.create_in_applications_menu, 575 base::DIR_START_MENU, 576 NULL 577 }, { 578 shortcut_info_.create_in_quick_launch_bar, 579 // For Win7, create_in_quick_launch_bar means pinning to taskbar. 580 base::DIR_APP_DATA, 581 (base::win::GetVersion() >= base::win::VERSION_WIN7) ? 582 L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar" : 583 L"Microsoft\\Internet Explorer\\Quick Launch" 584 } 585 }; 586 587 for (int i = 0; i < arraysize(locations); ++i) { 588 locations[i].use_this_location = false; 589 590 FilePath path; 591 if (!PathService::Get(locations[i].location_id, &path)) { 592 NOTREACHED(); 593 continue; 594 } 595 596 if (locations[i].sub_dir != NULL) 597 path = path.Append(locations[i].sub_dir); 598 599 FilePath shortcut_file = path.Append(file_name_). 600 ReplaceExtension(FILE_PATH_LITERAL(".lnk")); 601 if (file_util::PathExists(shortcut_file)) { 602 locations[i].use_this_location = true; 603 shortcut_files_.push_back(shortcut_file); 604 } 605 } 606} 607 608void UpdateShortcutWorker::UpdateShortcuts() { 609 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 610 NewRunnableMethod(this, 611 &UpdateShortcutWorker::UpdateShortcutsOnFileThread)); 612} 613 614void UpdateShortcutWorker::UpdateShortcutsOnFileThread() { 615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 616 617 FilePath web_app_path = GetWebAppDataDirectory( 618 web_app::GetDataDir(profile_path_), shortcut_info_.url); 619 620 // Ensure web_app_path exists. web_app_path could be missing for a legacy 621 // shortcut created by gears. 622 if (!file_util::PathExists(web_app_path) && 623 !file_util::CreateDirectory(web_app_path)) { 624 NOTREACHED(); 625 return; 626 } 627 628 FilePath icon_file = web_app_path.Append(file_name_).ReplaceExtension( 629 FILE_PATH_LITERAL(".ico")); 630 CheckAndSaveIcon(icon_file, shortcut_info_.favicon); 631 632 // Update existing shortcuts' description, icon and app id. 633 CheckExistingShortcuts(); 634 if (!shortcut_files_.empty()) { 635 // Generates app id from web app url and profile path. 636 std::wstring app_id = ShellIntegration::GetAppId( 637 UTF8ToWide(web_app::GenerateApplicationNameFromURL(shortcut_info_.url)), 638 profile_path_); 639 640 // Sanitize description 641 if (shortcut_info_.description.length() >= MAX_PATH) 642 shortcut_info_.description.resize(MAX_PATH - 1); 643 644 for (size_t i = 0; i < shortcut_files_.size(); ++i) { 645 file_util::UpdateShortcutLink(NULL, 646 shortcut_files_[i].value().c_str(), 647 NULL, 648 NULL, 649 shortcut_info_.description.c_str(), 650 icon_file.value().c_str(), 651 0, 652 app_id.c_str()); 653 } 654 } 655 656 OnShortcutsUpdated(true); 657} 658 659void UpdateShortcutWorker::OnShortcutsUpdated(bool) { 660 DeleteMe(); // We are done. 661} 662 663void UpdateShortcutWorker::DeleteMe() { 664 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 665 DeleteMeOnUIThread(); 666 } else { 667 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 668 NewRunnableMethod(this, &UpdateShortcutWorker::DeleteMeOnUIThread)); 669 } 670} 671 672void UpdateShortcutWorker::DeleteMeOnUIThread() { 673 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 674 delete this; 675} 676#endif // defined(OS_WIN) 677 678}; // namespace 679 680#if defined(OS_WIN) 681// Allows UpdateShortcutWorker without adding refcounting. UpdateShortcutWorker 682// manages its own life time and will delete itself when it's done. 683DISABLE_RUNNABLE_METHOD_REFCOUNT(UpdateShortcutWorker); 684#endif // defined(OS_WIN) 685 686namespace web_app { 687 688std::string GenerateApplicationNameFromURL(const GURL& url) { 689 std::string t; 690 t.append(url.host()); 691 t.append("_"); 692 t.append(url.path()); 693 return t; 694} 695 696void CreateShortcut( 697 const FilePath& data_dir, 698 const ShellIntegration::ShortcutInfo& shortcut_info, 699 CreateShortcutCallback* callback) { 700 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 701 new CreateShortcutTask(data_dir, shortcut_info, callback)); 702} 703 704bool IsValidUrl(const GURL& url) { 705 static const char* const kValidUrlSchemes[] = { 706 chrome::kFileScheme, 707 chrome::kFtpScheme, 708 chrome::kHttpScheme, 709 chrome::kHttpsScheme, 710 chrome::kExtensionScheme, 711 }; 712 713 for (size_t i = 0; i < arraysize(kValidUrlSchemes); ++i) { 714 if (url.SchemeIs(kValidUrlSchemes[i])) 715 return true; 716 } 717 718 return false; 719} 720 721FilePath GetDataDir(const FilePath& profile_path) { 722 return profile_path.Append(chrome::kWebAppDirname); 723} 724 725#if defined(TOOLKIT_VIEWS) 726void GetIconsInfo(const WebApplicationInfo& app_info, 727 IconInfoList* icons) { 728 DCHECK(icons); 729 730 icons->clear(); 731 for (size_t i = 0; i < app_info.icons.size(); ++i) { 732 // We only take square shaped icons (i.e. width == height). 733 if (app_info.icons[i].width == app_info.icons[i].height) { 734 icons->push_back(app_info.icons[i]); 735 } 736 } 737 738 std::sort(icons->begin(), icons->end(), &IconPrecedes); 739} 740#endif 741 742void GetShortcutInfoForTab(TabContents* tab_contents, 743 ShellIntegration::ShortcutInfo* info) { 744 DCHECK(info); // Must provide a valid info. 745 746 const WebApplicationInfo& app_info = tab_contents->web_app_info(); 747 748 info->url = app_info.app_url.is_empty() ? tab_contents->GetURL() : 749 app_info.app_url; 750 info->title = app_info.title.empty() ? 751 (tab_contents->GetTitle().empty() ? UTF8ToUTF16(info->url.spec()) : 752 tab_contents->GetTitle()) : 753 app_info.title; 754 info->description = app_info.description; 755 info->favicon = tab_contents->GetFavIcon(); 756} 757 758void UpdateShortcutForTabContents(TabContents* tab_contents) { 759#if defined(OS_WIN) 760 // UpdateShortcutWorker will delete itself when it's done. 761 UpdateShortcutWorker* worker = new UpdateShortcutWorker(tab_contents); 762 worker->Run(); 763#endif // defined(OS_WIN) 764} 765 766}; // namespace web_app 767