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
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/extensions/web_app_extension_shortcut.h"
#include <utility>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/one_shot_event.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/extension_ui_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#import "chrome/common/mac/app_mode_common.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/constants.h"
using content::BrowserThread;
namespace {
// Class that invokes a provided |callback| when destroyed, and supplies a means
// to keep the instance alive via posted tasks. The provided |callback| will
// always be invoked on the UI thread.
class Latch : public base::RefCountedThreadSafe<
Latch,
content::BrowserThread::DeleteOnUIThread> {
public:
explicit Latch(base::OnceClosure callback) : callback_(std::move(callback)) {}
Latch(const Latch&) = delete;
Latch& operator=(const Latch&) = delete;
// Wraps a reference to |this| in a Closure and returns it. Running the
// Closure does nothing. The Closure just serves to keep a reference alive
// until |this| is ready to be destroyed; invoking the |callback|.
base::RepeatingClosure NoOpClosure() {
return base::BindRepeating([](Latch*) {}, base::RetainedRef(this));
}
private:
friend class base::RefCountedThreadSafe<Latch>;
friend class base::DeleteHelper<Latch>;
friend struct content::BrowserThread::DeleteOnThread<
content::BrowserThread::UI>;
~Latch() { std::move(callback_).Run(); }
base::OnceClosure callback_;
};
} // namespace
namespace web_app {
// Mac-specific version of ShouldCreateShortcutFor() used during batch
// upgrades to ensure all shortcuts a user may still have are repaired when
// required by a Chrome upgrade.
bool ShouldUpgradeShortcutFor(Profile* profile,
const extensions::Extension* extension) {
if (extension->location() ==
extensions::mojom::ManifestLocation::kComponent ||
!extensions::ui_util::CanDisplayInAppLauncher(extension, profile)) {
return false;
}
return extension->is_app();
}
void UpdateShortcutsForAllApps(Profile* profile, base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Wait for extensions to be ready before continuing.
auto* extension_system = extensions::ExtensionSystem::Get(profile);
if (!extension_system)
return;
if (!extension_system->is_ready()) {
extension_system->ready().Post(
FROM_HERE, base::BindOnce(&UpdateShortcutsForAllApps, profile,
std::move(callback)));
return;
}
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(profile);
if (!registry)
return;
// Note: This can be replaced with a `BarrierCallback`, and the callback is
// now guaranteed to be called on the calling thread.
scoped_refptr<Latch> latch = new Latch(std::move(callback));
// Update all apps.
extensions::ExtensionSet candidates =
registry->GenerateInstalledExtensionsSet();
for (auto& extension_refptr : candidates) {
const extensions::Extension* extension = extension_refptr.get();
if (ShouldUpgradeShortcutFor(profile, extension)) {
UpdateAllShortcuts(std::u16string(), profile, extension,
latch->NoOpClosure());
}
}
}
} // namespace web_app
namespace chrome {
void ShowCreateChromeAppShortcutsDialog(
gfx::NativeWindow /*parent_window*/,
Profile* profile,
const extensions::Extension* app,
base::OnceCallback<void(bool)> close_callback) {
// On Mac, the Applications folder is the only option, so don't bother asking
// the user anything. Just create shortcuts.
CreateShortcuts(web_app::SHORTCUT_CREATION_BY_USER,
web_app::ShortcutLocations(), profile, app,
base::BindOnce(std::move(close_callback)));
}
void ShowCreateChromeAppShortcutsDialog(
gfx::NativeWindow /*parent_window*/,
Profile* profile,
const std::string& app_id,
base::OnceCallback<void(bool)> close_callback) {
// On Mac, the Applications folder is the only option, so don't bother asking
// the user anything. Just create shortcuts via OS integration.
auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
CHECK(provider);
provider->scheduler().SynchronizeOsIntegration(
app_id, base::BindOnce(std::move(close_callback), true),
web_app::ConvertShortcutLocationsToSynchronizeOptions(
web_app::ShortcutLocations(), web_app::SHORTCUT_CREATION_BY_USER),
/*upgrade_to_fully_installed_if_installed=*/true);
}
} // namespace chrome
|