普通文本  |  260行  |  8.46 KB

// Copyright (c) 2011 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/chromeos/extensions/file_browser_event_router.h"

#include "base/json/json_writer.h"
#include "base/memory/singleton.h"
#include "base/stl_util-inl.h"
#include "base/values.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/notifications/system_notification.h"
#include "chrome/browser/extensions/extension_event_names.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/extensions/file_manager_util.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"

const char kDiskAddedEventType[] = "added";
const char kDiskRemovedEventType[] = "removed";

const char* DeviceTypeToString(chromeos::DeviceType type) {
  switch (type) {
    case chromeos::FLASH:
      return "flash";
    case chromeos::HDD:
      return "hdd";
    case chromeos::OPTICAL:
      return "optical";
    default:
      break;
  }
  return "undefined";
}

DictionaryValue* DiskToDictionaryValue(
    const chromeos::MountLibrary::Disk* disk) {
  DictionaryValue* result = new DictionaryValue();
  result->SetString("mountPath", disk->mount_path());
  result->SetString("label", disk->device_label());
  result->SetString("deviceType", DeviceTypeToString(disk->device_type()));
  result->SetInteger("totalSizeKB", disk->total_size() / 1024);
  result->SetBoolean("readOnly", disk->is_read_only());
  return result;
}

ExtensionFileBrowserEventRouter::ExtensionFileBrowserEventRouter()
    : profile_(NULL) {
}

ExtensionFileBrowserEventRouter::~ExtensionFileBrowserEventRouter() {
}

void ExtensionFileBrowserEventRouter::ObserveFileSystemEvents(
    Profile* profile) {
  if (!profile)
    return;
  profile_ = profile;
  if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
    return;
  if (chromeos::UserManager::Get()->user_is_logged_in()) {
    chromeos::MountLibrary* lib =
        chromeos::CrosLibrary::Get()->GetMountLibrary();
    lib->RemoveObserver(this);
    lib->AddObserver(this);
    lib->RequestMountInfoRefresh();
  }
}

void ExtensionFileBrowserEventRouter::StopObservingFileSystemEvents() {
  if (!profile_)
    return;
  if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
    return;
  chromeos::MountLibrary* lib =
      chromeos::CrosLibrary::Get()->GetMountLibrary();
  lib->RemoveObserver(this);
  profile_ = NULL;
}

// static
ExtensionFileBrowserEventRouter*
    ExtensionFileBrowserEventRouter::GetInstance() {
  return Singleton<ExtensionFileBrowserEventRouter>::get();
}

void ExtensionFileBrowserEventRouter::DiskChanged(
    chromeos::MountLibraryEventType event,
    const chromeos::MountLibrary::Disk* disk) {
  if (event == chromeos::MOUNT_DISK_ADDED) {
    OnDiskAdded(disk);
  } else if (event == chromeos::MOUNT_DISK_REMOVED) {
    OnDiskRemoved(disk);
  } else if (event == chromeos::MOUNT_DISK_CHANGED) {
    OnDiskChanged(disk);
  }
}

void ExtensionFileBrowserEventRouter::DeviceChanged(
    chromeos::MountLibraryEventType event,
    const std::string& device_path) {
  if (event == chromeos::MOUNT_DEVICE_ADDED) {
    OnDeviceAdded(device_path);
  } else if (event == chromeos::MOUNT_DEVICE_REMOVED) {
    OnDeviceRemoved(device_path);
  } else if (event == chromeos::MOUNT_DEVICE_SCANNED) {
    OnDeviceScanned(device_path);
  }
}

void ExtensionFileBrowserEventRouter::DispatchEvent(
    const chromeos::MountLibrary::Disk* disk, bool added) {
  if (!profile_) {
    NOTREACHED();
    return;
  }

  ListValue args;
  DictionaryValue* mount_info = new DictionaryValue();
  args.Append(mount_info);
  mount_info->SetString("eventType",
                        added ? kDiskAddedEventType : kDiskRemovedEventType);
  DictionaryValue* disk_info = DiskToDictionaryValue(disk);
  mount_info->Set("volumeInfo", disk_info);

  std::string args_json;
  base::JSONWriter::Write(&args, false /* pretty_print */, &args_json);
  profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
      extension_event_names::kOnFileBrowserDiskChanged, args_json, NULL,
      GURL());
}

void ExtensionFileBrowserEventRouter::OnDiskAdded(
    const chromeos::MountLibrary::Disk* disk) {
  VLOG(1) << "Disk added: " << disk->device_path();
  if (disk->device_path().empty()) {
    VLOG(1) << "Empty system path for " << disk->device_path();
    return;
  }
  if (disk->is_parent()) {
    if (!disk->has_media())
      HideDeviceNotification(disk->system_path());
    return;
  }

  // If disk is not mounted yet, give it a try.
  if (disk->mount_path().empty()) {
    // Initiate disk mount operation.
    chromeos::MountLibrary* lib =
        chromeos::CrosLibrary::Get()->GetMountLibrary();
    lib->MountPath(disk->device_path().c_str());
  }
}

void ExtensionFileBrowserEventRouter::OnDiskRemoved(
    const chromeos::MountLibrary::Disk* disk) {
  VLOG(1) << "Disk removed: " << disk->device_path();
  HideDeviceNotification(disk->system_path());
  MountPointMap::iterator iter = mounted_devices_.find(disk->device_path());
  if (iter == mounted_devices_.end())
    return;

  chromeos::MountLibrary* lib =
      chromeos::CrosLibrary::Get()->GetMountLibrary();
  // TODO(zelidrag): This for some reason does not work as advertized.
  // we might need to clean up mount directory on FILE thread here as well.
  lib->UnmountPath(disk->device_path().c_str());

  DispatchEvent(disk, false);
  mounted_devices_.erase(iter);
}

void ExtensionFileBrowserEventRouter::OnDiskChanged(
    const chromeos::MountLibrary::Disk* disk) {
  VLOG(1) << "Disk changed : " << disk->device_path();
  if (!disk->mount_path().empty()) {
    HideDeviceNotification(disk->system_path());
    // Remember this mount point.
    if (mounted_devices_.find(disk->device_path()) == mounted_devices_.end()) {
      mounted_devices_.insert(
          std::pair<std::string, std::string>(disk->device_path(),
                                              disk->mount_path()));
      DispatchEvent(disk, true);
      HideDeviceNotification(disk->system_path());
      FileManagerUtil::ShowFullTabUrl(profile_, FilePath(disk->mount_path()));
    }
  }
}

void ExtensionFileBrowserEventRouter::OnDeviceAdded(
    const std::string& device_path) {
  VLOG(1) << "Device added : " << device_path;
  // TODO(zelidrag): Find better icon here.
  ShowDeviceNotification(device_path, IDR_PAGEINFO_INFO,
      l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_SCANNING_MESSAGE));

}

void ExtensionFileBrowserEventRouter::OnDeviceRemoved(
    const std::string& system_path) {
  HideDeviceNotification(system_path);
}

void ExtensionFileBrowserEventRouter::OnDeviceScanned(
    const std::string& device_path) {
  VLOG(1) << "Device scanned : " << device_path;
}

void ExtensionFileBrowserEventRouter::ShowDeviceNotification(
    const std::string& system_path, int icon_resource_id,
    const string16& message) {
  NotificationMap::iterator iter = FindNotificationForPath(system_path);
  std::string mount_path;
  if (iter != notifications_.end()) {
    iter->second->Show(message, false, false);
  } else {
    if (!profile_) {
      NOTREACHED();
      return;
    }
    chromeos::SystemNotification* notification =
        new chromeos::SystemNotification(
            profile_,
            system_path,
            icon_resource_id,
            l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_DETECTION_TITLE));
    notifications_.insert(NotificationMap::value_type(system_path,
        linked_ptr<chromeos::SystemNotification>(notification)));
    notification->Show(message, false, false);
  }
}

void ExtensionFileBrowserEventRouter::HideDeviceNotification(
    const std::string& system_path) {
  NotificationMap::iterator iter = FindNotificationForPath(system_path);
  if (iter != notifications_.end()) {
    iter->second->Hide();
    notifications_.erase(iter);
  }
}

ExtensionFileBrowserEventRouter::NotificationMap::iterator
    ExtensionFileBrowserEventRouter::FindNotificationForPath(
        const std::string& system_path) {
  for (NotificationMap::iterator iter = notifications_.begin();
       iter != notifications_.end();
       ++iter) {
    const std::string& notification_device_path = iter->first;
    // Doing a sub string match so that we find if this new one is a subdevice
    // of another already inserted device.
    if (StartsWithASCII(system_path, notification_device_path, true)) {
      return iter;
    }
  }
  return notifications_.end();
}