Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

Commit

Permalink
Add a notification about auto-granted access to file systems.
Browse files Browse the repository at this point in the history
For auto-launched kiosk apps permissions are granted automatically, without a
user consent. In such case a notification will be shown though.

TEST=unit_tests: *FileSystemApiConsentProviderTest*
BUG=440674

Review URL: https://codereview.chromium.org/1032313002

Cr-Commit-Position: refs/heads/master@{#324169}
  • Loading branch information
mtomasz-chromium authored and Commit bot committed Apr 8, 2015
1 parent aa89c5b commit 232bdea
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 17 deletions.
34 changes: 23 additions & 11 deletions chrome/browser/extensions/api/file_system/file_system_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "chrome/browser/chromeos/file_manager/app_id.h"
#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
#include "chrome/browser/extensions/api/file_system/request_file_system_dialog_view.h"
#include "chrome/browser/extensions/api/file_system/request_file_system_notification.h"
#include "chrome/browser/ui/simple_message_box.h"
#include "components/user_manager/user_manager.h"
#include "extensions/common/constants.h"
Expand Down Expand Up @@ -293,22 +294,25 @@ ConsentProvider::ConsentProvider(DelegateInterface* delegate)
ConsentProvider::~ConsentProvider() {
}

void ConsentProvider::RequestConsent(const extensions::Extension& extension,
base::WeakPtr<file_manager::Volume> volume,
bool writable,
const ConsentCallback& callback) {
void ConsentProvider::RequestConsent(
const extensions::Extension& extension,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable,
const ConsentCallback& callback) {
DCHECK(IsGrantable(extension));

// If auto-launched kiosk app, then no need to ask user.
if (delegate_->IsAutoLaunched(extension)) {
// If a whitelisted component, then no need to ask or inform the user.
if (extension.location() == Manifest::COMPONENT &&
delegate_->IsWhitelistedComponent(extension)) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(callback, CONSENT_GRANTED));
return;
}

// If a whitelisted component, then no need to ask user, either.
if (extension.location() == Manifest::COMPONENT &&
delegate_->IsWhitelistedComponent(extension)) {
// If auto-launched kiosk app, then no need to ask user either, but show the
// notification.
if (delegate_->IsAutoLaunched(extension)) {
delegate_->ShowNotification(extension, volume, writable);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(callback, CONSENT_GRANTED));
return;
Expand Down Expand Up @@ -371,7 +375,7 @@ void ConsentProviderDelegate::SetAutoDialogButtonForTest(

void ConsentProviderDelegate::ShowDialog(
const extensions::Extension& extension,
base::WeakPtr<file_manager::Volume> volume,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable,
const file_system_api::ConsentProvider::ShowDialogCallback& callback) {
content::WebContents* const foreground_contents =
Expand Down Expand Up @@ -400,6 +404,14 @@ void ConsentProviderDelegate::ShowDialog(
writable, base::Bind(callback));
}

void ConsentProviderDelegate::ShowNotification(
const extensions::Extension& extension,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable) {
RequestFileSystemNotification::ShowAutoGrantedNotification(
profile_, extension, volume, writable);
}

bool ConsentProviderDelegate::IsAutoLaunched(
const extensions::Extension& extension) {
chromeos::KioskAppManager::App app_info;
Expand Down Expand Up @@ -1270,7 +1282,7 @@ ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() {
}

void FileSystemRequestFileSystemFunction::OnConsentReceived(
base::WeakPtr<file_manager::Volume> volume,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable,
ConsentProvider::Consent result) {
using file_manager::VolumeManager;
Expand Down
17 changes: 13 additions & 4 deletions chrome/browser/extensions/api/file_system/file_system_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,16 @@ class ConsentProvider {
public:
// Shows a dialog for granting permissions.
virtual void ShowDialog(const extensions::Extension& extension,
base::WeakPtr<file_manager::Volume> volume,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable,
const ShowDialogCallback& callback) = 0;

// Shows a notification about permissions automatically granted access.
virtual void ShowNotification(
const extensions::Extension& extension,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable) = 0;

// Checks if the extension was launched in auto-launch kiosk mode.
virtual bool IsAutoLaunched(const extensions::Extension& extension) = 0;

Expand All @@ -80,7 +86,7 @@ class ConsentProvider {
// volume by the |extension|. Must be called only if the extension is
// grantable, which can be checked with IsGrantable().
void RequestConsent(const extensions::Extension& extension,
base::WeakPtr<file_manager::Volume> volume,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable,
const ConsentCallback& callback);

Expand Down Expand Up @@ -114,10 +120,13 @@ class ConsentProviderDelegate : public ConsentProvider::DelegateInterface {

// ConsentProvider::DelegateInterface overrides:
void ShowDialog(const extensions::Extension& extension,
base::WeakPtr<file_manager::Volume> volume,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable,
const file_system_api::ConsentProvider::ShowDialogCallback&
callback) override;
void ShowNotification(const extensions::Extension& extension,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable) override;
bool IsAutoLaunched(const extensions::Extension& extension) override;
bool IsWhitelistedComponent(const extensions::Extension& extension) override;

Expand Down Expand Up @@ -378,7 +387,7 @@ class FileSystemRequestFileSystemFunction : public UIThreadExtensionFunction {
private:
// Called when a user grants or rejects permissions for the file system
// access.
void OnConsentReceived(base::WeakPtr<file_manager::Volume> volume,
void OnConsentReceived(const base::WeakPtr<file_manager::Volume>& volume,
bool writable,
file_system_api::ConsentProvider::Consent result);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class TestingConsentProviderDelegate
public:
TestingConsentProviderDelegate()
: show_dialog_counter_(0),
show_notification_counter_(0),
dialog_button_(ui::DIALOG_BUTTON_NONE),
is_auto_launched_(false) {}

Expand All @@ -104,18 +105,25 @@ class TestingConsentProviderDelegate
}

int show_dialog_counter() const { return show_dialog_counter_; }
int show_notification_counter() const { return show_notification_counter_; }

private:
// ConsentProvider::DelegateInterface overrides:
void ShowDialog(
const extensions::Extension& extension,
base::WeakPtr<Volume> volume,
const base::WeakPtr<Volume>& volume,
bool writable,
const ConsentProvider::ShowDialogCallback& callback) override {
++show_dialog_counter_;
callback.Run(dialog_button_);
}

void ShowNotification(const extensions::Extension& extension,
const base::WeakPtr<Volume>& volume,
bool writable) override {
++show_notification_counter_;
}

bool IsAutoLaunched(const extensions::Extension& extension) override {
return is_auto_launched_;
}
Expand All @@ -125,6 +133,7 @@ class TestingConsentProviderDelegate
}

int show_dialog_counter_;
int show_notification_counter_;
ui::DialogButton dialog_button_;
bool is_auto_launched_;
std::string whitelisted_component_id_;
Expand Down Expand Up @@ -330,6 +339,7 @@ TEST_F(FileSystemApiConsentProviderTest, ForNonKioskApps) {
base::RunLoop().RunUntilIdle();

EXPECT_EQ(0, delegate.show_dialog_counter());
EXPECT_EQ(0, delegate.show_notification_counter());
EXPECT_EQ(ConsentProvider::CONSENT_GRANTED, result);
}

Expand All @@ -346,7 +356,7 @@ TEST_F(FileSystemApiConsentProviderTest, ForNonKioskApps) {

TEST_F(FileSystemApiConsentProviderTest, ForKioskApps) {
// Non-component apps in auto-launch kiosk mode will be granted access
// instantly without asking for user consent.
// instantly without asking for user consent, but with a notification.
{
scoped_refptr<Extension> auto_launch_kiosk_app(
test_util::BuildApp(ExtensionBuilder().Pass())
Expand All @@ -369,6 +379,7 @@ TEST_F(FileSystemApiConsentProviderTest, ForKioskApps) {
base::RunLoop().RunUntilIdle();

EXPECT_EQ(0, delegate.show_dialog_counter());
EXPECT_EQ(1, delegate.show_notification_counter());
EXPECT_EQ(ConsentProvider::CONSENT_GRANTED, result);
}

Expand All @@ -394,6 +405,7 @@ TEST_F(FileSystemApiConsentProviderTest, ForKioskApps) {
base::RunLoop().RunUntilIdle();

EXPECT_EQ(1, delegate.show_dialog_counter());
EXPECT_EQ(0, delegate.show_notification_counter());
EXPECT_EQ(ConsentProvider::CONSENT_GRANTED, result);
}

Expand All @@ -412,6 +424,7 @@ TEST_F(FileSystemApiConsentProviderTest, ForKioskApps) {
base::RunLoop().RunUntilIdle();

EXPECT_EQ(1, delegate.show_dialog_counter());
EXPECT_EQ(0, delegate.show_notification_counter());
EXPECT_EQ(ConsentProvider::CONSENT_REJECTED, result);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/file_system/request_file_system_notification.h"

#include "base/memory/ref_counted.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "chrome/browser/extensions/app_icon_loader_impl.h"
#include "chrome/grit/generated_resources.h"
#include "extensions/common/extension.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_delegate.h"
#include "ui/message_center/notification_types.h"
#include "ui/message_center/notifier_settings.h"

using file_manager::Volume;
using message_center::Notification;

namespace {

// Extension icon size for the notification.
const int kIconSize = 48;

scoped_ptr<Notification> CreateAutoGrantedNotification(
const extensions::Extension& extension,
const base::WeakPtr<Volume>& volume,
bool writable,
message_center::NotificationDelegate* delegate) {
DCHECK(delegate);

// If the volume is gone, then do not show the notification.
if (!volume.get())
return scoped_ptr<Notification>(nullptr);

const std::string notification_id =
extension.id() + "-" + volume->volume_id();
message_center::RichNotificationData data;
data.clickable = false;

// TODO(mtomasz): Share this code with RequestFileSystemDialogView.
const base::string16 display_name =
base::UTF8ToUTF16(!volume->volume_label().empty() ? volume->volume_label()
: volume->volume_id());
const base::string16 message = l10n_util::GetStringFUTF16(
writable
? IDS_FILE_SYSTEM_REQUEST_FILE_SYSTEM_NOTIFICATION_WRITABLE_MESSAGE
: IDS_FILE_SYSTEM_REQUEST_FILE_SYSTEM_NOTIFICATION_MESSAGE,
display_name);

scoped_ptr<Notification> notification(new Notification(
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
base::UTF8ToUTF16(extension.name()), message,
gfx::Image(), // Updated asynchronously later.
base::string16(), // display_source
message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
notification_id),
data, delegate));

return notification.Pass();
}

} // namespace

// static
void RequestFileSystemNotification::ShowAutoGrantedNotification(
Profile* profile,
const extensions::Extension& extension,
const base::WeakPtr<Volume>& volume,
bool writable) {
DCHECK(profile);
scoped_refptr<RequestFileSystemNotification>
request_file_system_notification = make_scoped_refptr(
new RequestFileSystemNotification(profile, extension));
scoped_ptr<message_center::Notification> notification(
CreateAutoGrantedNotification(
extension, volume, writable,
request_file_system_notification.get() /* delegate */));
if (notification.get())
request_file_system_notification->Show(notification.Pass());
}

void RequestFileSystemNotification::SetAppImage(const std::string& id,
const gfx::ImageSkia& image) {
extension_icon_.reset(new gfx::Image(image));

// If there is a pending notification, then show it now.
if (pending_notification_.get()) {
pending_notification_->set_icon(*extension_icon_.get());
g_browser_process->message_center()->AddNotification(
pending_notification_.Pass());
}
}

RequestFileSystemNotification::RequestFileSystemNotification(
Profile* profile,
const extensions::Extension& extension)
: icon_loader_(
new extensions::AppIconLoaderImpl(profile, kIconSize, this)) {
icon_loader_->FetchImage(extension.id());
}

RequestFileSystemNotification::~RequestFileSystemNotification() {
}

void RequestFileSystemNotification::Show(
scoped_ptr<Notification> notification) {
pending_notification_.reset(notification.release());
// If the extension icon is not known yet, then defer showing the notification
// until it is (from SetAppImage).
if (!extension_icon_.get())
return;

pending_notification_->set_icon(*extension_icon_.get());
g_browser_process->message_center()->AddNotification(
pending_notification_.Pass());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_REQUEST_FILE_SYSTEM_NOTIFICATION_H_
#define CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_REQUEST_FILE_SYSTEM_NOTIFICATION_H_

#include <string>

#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/extensions/app_icon_loader.h"
#include "ui/message_center/notification_delegate.h"

class Profile;

namespace extensions {
class Extension;
} // namespace extensions

namespace file_manager {
class Volume;
} // namespace file_manager

namespace gfx {
class Image;
class ImageSkia;
} // namespace gfx

namespace message_center {
class Notification;
} // namespace message_center

// Shows notifications for the chrome.fileSystem.requestFileSystem() API.
class RequestFileSystemNotification
: public message_center::NotificationDelegate,
public extensions::AppIconLoader::Delegate {
public:
// Shows a notification about automatically granted access to a file system.
static void ShowAutoGrantedNotification(
Profile* profile,
const extensions::Extension& extension,
const base::WeakPtr<file_manager::Volume>& volume,
bool writable);

private:
RequestFileSystemNotification(Profile* profile,
const extensions::Extension& extension);
~RequestFileSystemNotification() override;

// Shows the notification. Can be called only once.
void Show(scoped_ptr<message_center::Notification> notification);

// extensions::AppIconLoader::Delegate overrides:
void SetAppImage(const std::string& id, const gfx::ImageSkia& image) override;

scoped_ptr<extensions::AppIconLoader> icon_loader_;
scoped_ptr<gfx::Image> extension_icon_;
scoped_ptr<message_center::Notification> pending_notification_;

DISALLOW_COPY_AND_ASSIGN(RequestFileSystemNotification);
};

#endif // CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_REQUEST_FILE_SYSTEM_NOTIFICATION_H_
Loading

0 comments on commit 232bdea

Please sign in to comment.