void AddChromeFrameWorkItems(const InstallationState& original_state,
                             const InstallerState& installer_state,
                             const FilePath& setup_path,
                             const Version& new_version,
                             const Product& product,
                             WorkItemList* list) {
  DCHECK(product.is_chrome_frame());
  if (!installer_state.is_multi_install()) {
    VLOG(1) << "Not adding GCF specific work items for single install.";
    return;
  }

  std::wstring version_key(product.distribution()->GetVersionKey());
  bool ready_mode = product.HasOption(kOptionReadyMode);
  HKEY root = installer_state.root_key();
  const bool is_install =
      (installer_state.operation() != InstallerState::UNINSTALL);
  bool update_chrome_uninstall_command = false;
  BrowserDistribution* dist =
      installer_state.multi_package_binaries_distribution();
  if (ready_mode) {
    list->AddCreateRegKeyWorkItem(root, dist->GetStateKey());
    list->AddSetRegValueWorkItem(root, dist->GetStateKey(),
        kChromeFrameReadyModeField,
        static_cast<int64>(is_install ? 1 : 0),  // The value we want to set.
        is_install ? false : true);  // Overwrite existing value.
    if (is_install) {
      FilePath installer_path(installer_state
          .GetInstallerDirectory(new_version).Append(setup_path.BaseName()));

      CommandLine basic_cl(installer_path);
      basic_cl.AppendSwitch(switches::kChromeFrame);
      basic_cl.AppendSwitch(switches::kMultiInstall);

      if (installer_state.system_install())
        basic_cl.AppendSwitch(switches::kSystemLevel);

      CommandLine temp_opt_out(basic_cl);
      temp_opt_out.AppendSwitch(switches::kChromeFrameReadyModeTempOptOut);

      CommandLine end_temp_opt_out(basic_cl);
      end_temp_opt_out.AppendSwitch(
          switches::kChromeFrameReadyModeEndTempOptOut);

      CommandLine opt_out(installer_path);
      AppendUninstallCommandLineFlags(installer_state, product, &opt_out);
      opt_out.AppendSwitch(switches::kForceUninstall);

      CommandLine opt_in(basic_cl);
      opt_in.AppendSwitch(switches::kChromeFrameReadyModeOptIn);

      list->AddSetRegValueWorkItem(root, version_key,
                                   google_update::kRegCFTempOptOutCmdField,
                                   temp_opt_out.command_line_string(), true);
      list->AddSetRegValueWorkItem(root, version_key,
                                   google_update::kRegCFEndTempOptOutCmdField,
                                   end_temp_opt_out.command_line_string(),
                                   true);
      list->AddSetRegValueWorkItem(root, version_key,
                                   google_update::kRegCFOptOutCmdField,
                                   opt_out.command_line_string(), true);
      list->AddSetRegValueWorkItem(root, version_key,
                                   google_update::kRegCFOptInCmdField,
                                   opt_in.command_line_string(), true);
    } else {
      update_chrome_uninstall_command =
          (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ==
           NULL);
    }
  } else {

    list->AddDeleteRegValueWorkItem(root, dist->GetStateKey(),
        kChromeFrameReadyModeField);

    const Product* chrome =
        installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
    if (chrome) {
    } else {
      const ProductState* chrome_state = original_state.GetProductState(
          installer_state.system_install(),
          BrowserDistribution::CHROME_BROWSER);
      update_chrome_uninstall_command =
          (chrome_state != NULL) && chrome_state->is_multi_install();
    }
  }

  if (!ready_mode || !is_install) {
    list->AddDeleteRegValueWorkItem(root, version_key,
                                    google_update::kRegCFTempOptOutCmdField);
    list->AddDeleteRegValueWorkItem(root, version_key,
                                    google_update::kRegCFEndTempOptOutCmdField);
    list->AddDeleteRegValueWorkItem(root, version_key,
                                    google_update::kRegCFOptOutCmdField);
    list->AddDeleteRegValueWorkItem(root, version_key,
                                    google_update::kRegCFOptInCmdField);
  }

  if (update_chrome_uninstall_command) {
    const ProductState* chrome_state = original_state.GetProductState(
        installer_state.system_install(),
        BrowserDistribution::CHROME_BROWSER);
    if (chrome_state != NULL) {
      DCHECK(chrome_state->is_multi_install());
      Product chrome(BrowserDistribution::GetSpecificDistribution(
                         BrowserDistribution::CHROME_BROWSER));
      chrome.InitializeFromUninstallCommand(chrome_state->uninstall_command());
      AddUninstallShortcutWorkItems(installer_state, setup_path,
                                    chrome_state->version(), list, chrome);
    } else {
      NOTREACHED() << "What happened to Chrome?";
    }
  }
}


void AddElevationPolicyWorkItems(const InstallationState& original_state,
                                 const InstallerState& installer_state,
                                 const Version& new_version,
                                 WorkItemList* install_list) {
  if (!installer_state.is_multi_install()) {
    VLOG(1) << "Not adding elevation policy for single installs";
    return;
  } else {
    const ProductState* cf_state =
        original_state.GetProductState(installer_state.system_install(),
                                       BrowserDistribution::CHROME_FRAME);
    if (cf_state && !cf_state->is_multi_install()) {
      LOG(WARNING) << "Not adding elevation policy since a single install "
                      "of CF exists";
      return;
    }
  }

  FilePath binary_dir(
      GetChromeInstallPath(installer_state.system_install(),
          BrowserDistribution::GetSpecificDistribution(
              BrowserDistribution::CHROME_BINARIES)));

  struct {
    const wchar_t* sub_key;
    const wchar_t* executable;
    const FilePath exe_dir;
  } low_rights_entries[] = {
    { L"ElevationPolicy\\", kChromeLauncherExe,
      binary_dir.Append(ASCIIToWide(new_version.GetString())) },
    { L"DragDrop\\", chrome::kBrowserProcessExecutableName, binary_dir },
  };

  bool uninstall = (installer_state.operation() == InstallerState::UNINSTALL);
  HKEY root = installer_state.root_key();
  const wchar_t kLowRightsKeyPath[] =
      L"SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\";
  std::wstring key_path(kLowRightsKeyPath);

  wchar_t cf_classid[64] = {0};
  StringFromGUID2(__uuidof(ChromeFrame), cf_classid, arraysize(cf_classid));

  for (size_t i = 0; i < arraysize(low_rights_entries); ++i) {
    key_path.append(low_rights_entries[i].sub_key).append(cf_classid);
    if (uninstall) {
      install_list->AddDeleteRegKeyWorkItem(root, key_path);
    } else {
      install_list->AddCreateRegKeyWorkItem(root, key_path);
      install_list->AddSetRegValueWorkItem(root, key_path, L"Policy",
          static_cast<DWORD>(3), true);
      install_list->AddSetRegValueWorkItem(root, key_path, L"AppName",
          low_rights_entries[i].executable, true);
      install_list->AddSetRegValueWorkItem(root, key_path, L"AppPath",
          low_rights_entries[i].exe_dir.value(), true);
    }
    key_path.resize(arraysize(kLowRightsKeyPath) - 1);
  }
}


void AddGoogleUpdateWorkItems(const InstallationState& original_state,
                              const InstallerState& installer_state,
                              WorkItemList* install_list) {
  if (installer_state.operation() != InstallerState::MULTI_INSTALL &&
      installer_state.operation() != InstallerState::MULTI_UPDATE) {
    VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation();
    return;
  }

  if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
    std::wstring multi_key(
        installer_state.multi_package_binaries_distribution()->GetStateKey());
    const ProductState* chrome_product_state =
        original_state.GetNonVersionedProductState(
            installer_state.system_install(),
            BrowserDistribution::CHROME_BROWSER);

    const std::wstring& brand(chrome_product_state->brand());
    if (!brand.empty()) {
      install_list->AddCreateRegKeyWorkItem(installer_state.root_key(),
                                            multi_key);
      install_list->AddSetRegValueWorkItem(installer_state.root_key(),
                                           multi_key,
                                           google_update::kRegBrandField,
                                           brand,
                                           false);
    }
  }

  AddUsageStatsWorkItems(original_state, installer_state, install_list);

}


void AddInstallWorkItems(const InstallationState& original_state,
                         const InstallerState& installer_state,
                         const FilePath& setup_path,
                         const FilePath& archive_path,
                         const FilePath& src_path,
                         const FilePath& temp_path,
                         const Version& new_version,
                         scoped_ptr<Version>* current_version,
                         WorkItemList* install_list) {
  DCHECK(install_list);

  const FilePath& target_path = installer_state.target_path();

  install_list->AddCreateDirWorkItem(temp_path);
  install_list->AddCreateDirWorkItem(target_path);

  if (current_version != NULL && current_version->get() != NULL) {
    FilePath old_installer_dir(
        installer_state.GetInstallerDirectory(**current_version));
    FilePath old_archive(old_installer_dir.Append(archive_path.BaseName()));
    install_list->AddDeleteTreeWorkItem(old_archive, temp_path)
        ->set_ignore_failure(true);
  }

  FilePath new_chrome_exe(target_path.Append(installer::kChromeNewExe));

  install_list->AddDeleteTreeWorkItem(new_chrome_exe, temp_path);
  install_list->AddCopyTreeWorkItem(
      src_path.Append(installer::kChromeExe).value(),
      target_path.Append(installer::kChromeExe).value(),
      temp_path.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value());

  if (base::win::OSInfo::GetInstance()->wow64_status() !=
      base::win::OSInfo::WOW64_DISABLED) {
    install_list->AddMoveTreeWorkItem(
        src_path.Append(installer::kWowHelperExe).value(),
        target_path.Append(installer::kWowHelperExe).value(),
        temp_path.value(),
        WorkItem::ALWAYS_MOVE);
  }

  bool check_for_duplicates =
      (current_version != NULL && current_version->get() != NULL &&
       current_version->get()->Equals(new_version));
  install_list->AddMoveTreeWorkItem(
      src_path.AppendASCII(new_version.GetString()).value(),
      target_path.AppendASCII(new_version.GetString()).value(),
      temp_path.value(),
      check_for_duplicates ? WorkItem::CHECK_DUPLICATES :
                             WorkItem::ALWAYS_MOVE);

  install_list->AddCopyTreeWorkItem(
      src_path.Append(installer::kDictionaries).value(),
      target_path.Append(installer::kDictionaries).value(),
      temp_path.value(), WorkItem::IF_NOT_PRESENT);

  install_list->AddDeleteTreeWorkItem(
      target_path.Append(installer::kChromeOldExe), temp_path);

  AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
                        new_version, install_list);

  const HKEY root = installer_state.root_key();

  const Products& products = installer_state.products();
  for (size_t i = 0; i < products.size(); ++i) {
    const Product* product = products[i];

    AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
                                  install_list, *product);

    AddVersionKeyWorkItems(root, product->distribution(), new_version,
                           install_list);
  }

  if (installer_state.is_multi_install()) {
    AddVersionKeyWorkItems(root,
        installer_state.multi_package_binaries_distribution(), new_version,
        install_list);
  }

  AddProductSpecificWorkItems(original_state, installer_state, setup_path,
                              new_version, install_list);

  AddGoogleUpdateWorkItems(original_state, installer_state, install_list);

  AddQuickEnableWorkItems(installer_state, original_state, &setup_path,
                          &new_version, install_list);

  AppendPostInstallTasks(installer_state,
                         setup_path,
                         new_chrome_exe,
                         current_version->get(),
                         new_version,
                         temp_path,
                         install_list);
}


void AddProductSpecificWorkItems(const InstallationState& original_state,
                                 const InstallerState& installer_state,
                                 const FilePath& setup_path,
                                 const Version& new_version,
                                 WorkItemList* list) {
  const Products& products = installer_state.products();
  for (size_t i = 0; i < products.size(); ++i) {
    const Product& p = *products[i];
    if (p.is_chrome_frame()) {
      AddChromeFrameWorkItems(original_state, installer_state, setup_path,
                              new_version, p, list);
    }
  }
}


void AddRegisterComDllWorkItems(const FilePath& dll_folder,
                                const std::vector<FilePath>& dll_list,
                                bool system_level,
                                bool do_register,
                                bool ignore_failures,
                                WorkItemList* work_item_list) {
  DCHECK(work_item_list);
  if (dll_list.empty()) {
    VLOG(1) << "No COM DLLs to register";
  } else {
    std::vector<FilePath>::const_iterator dll_iter(dll_list.begin());
    for (; dll_iter != dll_list.end(); ++dll_iter) {
      FilePath dll_path = dll_folder.Append(*dll_iter);
      WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
          dll_path.value(), do_register, !system_level);
      DCHECK(work_item);
      work_item->set_ignore_failure(ignore_failures);
    }
  }
}


void AddRegisterComDllWorkItemsForPackage(const InstallerState& installer_state,
                                          const Version* old_version,
                                          const Version& new_version,
                                          WorkItemList* work_item_list) {
  std::vector<FilePath> com_dll_list;
  installer_state.AddComDllList(&com_dll_list);

  if (!com_dll_list.empty()) {
    if (old_version) {
      FilePath old_dll_path(installer_state.target_path().AppendASCII(
          old_version->GetString()));

      installer::AddRegisterComDllWorkItems(old_dll_path,
                                            com_dll_list,
                                            installer_state.system_install(),
                                            false,  // Unregister
                                            true,   // May fail
                                            work_item_list);
    }

    FilePath dll_path(installer_state.target_path().AppendASCII(
        new_version.GetString()));
    installer::AddRegisterComDllWorkItems(dll_path,
                                          com_dll_list,
                                          installer_state.system_install(),
                                          true,   // Register
                                          false,  // Must succeed.
                                          work_item_list);
  }
}


void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
                             BrowserDistribution* dist,
                             bool set,
                             WorkItemList* work_item_list) {
  DCHECK(work_item_list);
  DWORD msi_value = set ? 1 : 0;
  WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
      installer_state.root_key(), dist->GetStateKey(),
      google_update::kRegMSIField, msi_value, true);
  DCHECK(set_msi_work_item);
  set_msi_work_item->set_ignore_failure(true);
  set_msi_work_item->set_log_message("Could not write MSI marker!");
}


void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
                                   const FilePath& setup_path,
                                   const Version& new_version,
                                   WorkItemList* install_list,
                                   const Product& product) {
  HKEY reg_root = installer_state.root_key();
  BrowserDistribution* browser_dist = product.distribution();
  DCHECK(browser_dist);

  FilePath install_path(installer_state.target_path());
  FilePath installer_path(installer_state.GetInstallerDirectory(new_version));
  installer_path = installer_path.Append(setup_path.BaseName());

  CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
  AppendUninstallCommandLineFlags(installer_state, product,
                                  &uninstall_arguments);

  if (product.is_chrome() &&
      installer_state.operation() != InstallerState::UNINSTALL) {
    const Products& products = installer_state.products();
    for (size_t i = 0; i < products.size(); ++i) {
      const Product& p = *products[i];
      if (!p.is_chrome() && !p.ShouldCreateUninstallEntry())
        p.AppendUninstallFlags(&uninstall_arguments);
    }
  }

  std::wstring update_state_key(browser_dist->GetStateKey());
  install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key);
  install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
      installer::kUninstallStringField, installer_path.value(), true);
  install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
      installer::kUninstallArgumentsField,
      uninstall_arguments.command_line_string(), true);

  if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) {
    CommandLine quoted_uninstall_cmd(installer_path);
    DCHECK_EQ(quoted_uninstall_cmd.command_line_string()[0], '"');
    quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);

    std::wstring uninstall_reg = browser_dist->GetUninstallRegPath();
    install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg);
    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
        installer::kUninstallDisplayNameField,
        browser_dist->GetAppShortCutName(), true);
    install_list->AddSetRegValueWorkItem(reg_root,
        uninstall_reg, installer::kUninstallStringField,
        quoted_uninstall_cmd.command_line_string(), true);
    install_list->AddSetRegValueWorkItem(reg_root,
                                         uninstall_reg,
                                         L"InstallLocation",
                                         install_path.value(),
                                         true);

    FilePath chrome_icon(install_path.Append(installer::kChromeExe));
    ShellUtil::GetChromeIcon(product.distribution(), chrome_icon.value());
    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
                                         L"DisplayIcon", chrome_icon.value(),
                                         true);
    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
                                         L"NoModify", static_cast<DWORD>(1),
                                         true);
    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
                                         L"NoRepair", static_cast<DWORD>(1),
                                         true);

    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
                                         L"Publisher",
                                         browser_dist->GetPublisherName(),
                                         true);
    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
                                         L"Version",
                                         UTF8ToWide(new_version.GetString()),
                                         true);
    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
                                         L"DisplayVersion",
                                         UTF8ToWide(new_version.GetString()),
                                         true);
    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
                                         L"InstallDate",
                                         InstallUtil::GetCurrentDate(),
                                         false);
  }
}


void AddUsageStatsWorkItems(const InstallationState& original_state,
                            const InstallerState& installer_state,
                            WorkItemList* install_list) {
  DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL ||
         installer_state.operation() == InstallerState::MULTI_UPDATE);

  HKEY root_key = installer_state.root_key();
  bool value_found = false;
  DWORD usagestats = 0;
  const Products& products = installer_state.products();

  for (Products::const_iterator scan = products.begin(), end = products.end();
       !value_found && scan != end; ++scan) {
    BrowserDistribution* dist = (*scan)->distribution();
    const ProductState* product_state =
        original_state.GetNonVersionedProductState(
            installer_state.system_install(), dist->GetType());
    value_found = product_state->GetUsageStats(&usagestats);
  }

  if (value_found) {
    std::wstring state_key(
        installer_state.multi_package_binaries_distribution()->GetStateKey());
    install_list->AddCreateRegKeyWorkItem(root_key, state_key);
    install_list->AddSetRegValueWorkItem(root_key, state_key,
                                         google_update::kRegUsageStatsField,
                                         usagestats, false);

    for (Products::const_iterator scan = products.begin(), end = products.end();
         scan != end; ++scan) {
      BrowserDistribution* dist = (*scan)->distribution();
      if (installer_state.system_install()) {
        install_list->AddDeleteRegValueWorkItem(
            root_key, dist->GetStateMediumKey(),
            google_update::kRegUsageStatsField);
        install_list->AddDeleteRegValueWorkItem(
            HKEY_CURRENT_USER, dist->GetStateKey(),
            google_update::kRegUsageStatsField);
      }
      install_list->AddDeleteRegValueWorkItem(root_key, dist->GetStateKey(),
          google_update::kRegUsageStatsField);
    }
  }
}


void AddVersionKeyWorkItems(HKEY root,
                            BrowserDistribution* dist,
                            const Version& new_version,
                            WorkItemList* list) {
  std::wstring version_key(dist->GetVersionKey());
  list->AddCreateRegKeyWorkItem(root, version_key);

  std::wstring product_name(dist->GetAppShortCutName());
  list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField,
                               product_name, true);  // overwrite name also
  list->AddSetRegValueWorkItem(root, version_key,
                               google_update::kRegOopcrashesField,
                               static_cast<DWORD>(1),
                               false);  // set during first install
  list->AddSetRegValueWorkItem(root, version_key,
                               google_update::kRegVersionField,
                               UTF8ToWide(new_version.GetString()),
                               true);  // overwrite version
}


bool AppendPostInstallTasks(const InstallerState& installer_state,
                            const FilePath& setup_path,
                            const FilePath& new_chrome_exe,
                            const Version* current_version,
                            const Version& new_version,
                            const FilePath& temp_path,
                            WorkItemList* post_install_task_list) {
  DCHECK(post_install_task_list);

  HKEY root = installer_state.root_key();
  const Products& products = installer_state.products();

  {
    scoped_ptr<WorkItemList> in_use_update_work_items(
        WorkItem::CreateConditionalWorkItemList(
            new ConditionRunIfFileExists(new_chrome_exe)));
    in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");

    FilePath installer_path(installer_state.GetInstallerDirectory(new_version)
        .Append(setup_path.BaseName()));

    CommandLine rename(installer_path);
    rename.AppendSwitch(switches::kRenameChromeExe);
    if (installer_state.system_install())
      rename.AppendSwitch(switches::kSystemLevel);

    if (installer_state.verbose_logging())
      rename.AppendSwitch(switches::kVerboseLogging);

    std::wstring version_key;
    for (size_t i = 0; i < products.size(); ++i) {
      BrowserDistribution* dist = products[i]->distribution();
      version_key = dist->GetVersionKey();

      if (current_version != NULL) {
        in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
            google_update::kRegOldVersionField,
            UTF8ToWide(current_version->GetString()), true);
      }

      CommandLine product_rename_cmd(rename);
      products[i]->AppendRenameFlags(&product_rename_cmd);
      in_use_update_work_items->AddSetRegValueWorkItem(
          root,
          version_key,
          google_update::kRegRenameCmdField,
          product_rename_cmd.command_line_string(),
          true);
    }

    if (current_version != NULL && installer_state.is_multi_install()) {
      BrowserDistribution* dist =
          installer_state.multi_package_binaries_distribution();
      in_use_update_work_items->AddSetRegValueWorkItem(
          root,
          dist->GetVersionKey(),
          google_update::kRegOldVersionField,
          UTF8ToWide(current_version->GetString()),
          true);
    }

    post_install_task_list->AddWorkItem(in_use_update_work_items.release());
  }

  {
    scoped_ptr<WorkItemList> regular_update_work_items(
        WorkItem::CreateConditionalWorkItemList(
            new Not(new ConditionRunIfFileExists(new_chrome_exe))));
    regular_update_work_items->set_log_message("RegularUpdateWorkItemList");

    for (size_t i = 0; i < products.size(); ++i) {
      BrowserDistribution* dist = products[i]->distribution();
      std::wstring version_key(dist->GetVersionKey());
      regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
                                            google_update::kRegOldVersionField);
      regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
                                            google_update::kRegRenameCmdField);
    }

    post_install_task_list->AddWorkItem(regular_update_work_items.release());
  }

  AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
                                       new_version, post_install_task_list);

  if (installer_state.is_msi()) {
    for (size_t i = 0; i < products.size(); ++i) {
      const Product* product = products[i];
      AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
                              post_install_task_list);

      AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
                                                 temp_path,
                                                 post_install_task_list);
    }
    if (installer_state.is_multi_install()) {
      AddSetMsiMarkerWorkItem(installer_state,
          installer_state.multi_package_binaries_distribution(), true,
          post_install_task_list);
    }
  }

  return true;
}


void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
                                     const Product& product,
                                     CommandLine* uninstall_cmd) {
  DCHECK(uninstall_cmd);

  uninstall_cmd->AppendSwitch(installer::switches::kUninstall);

  product.AppendUninstallFlags(uninstall_cmd);
  if (installer_state.is_msi()) {
    uninstall_cmd->AppendSwitch(installer::switches::kMsi);
    if (product.is_chrome_frame()) {
      uninstall_cmd->AppendSwitch(installer::switches::kDeleteProfile);
    }
  }
  if (installer_state.system_install())
    uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
  if (installer_state.verbose_logging())
    uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
}


void RefreshElevationPolicy() {
  const wchar_t kIEFrameDll[] = L"ieframe.dll";
  const char kIERefreshPolicy[] = "IERefreshElevationPolicy";

  HMODULE ieframe = LoadLibrary(kIEFrameDll);
  if (ieframe) {
    typedef HRESULT (__stdcall *IERefreshPolicy)();
    IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
        GetProcAddress(ieframe, kIERefreshPolicy));

    if (ie_refresh_policy) {
      ie_refresh_policy();
    } else {
      VLOG(1) << kIERefreshPolicy << " not supported.";
    }

    FreeLibrary(ieframe);
  } else {
    VLOG(1) << "Cannot load " << kIEFrameDll;
  }
}


bool decode(ArgumentDecoder* decoder, RetainPtr<CFTypeRef>& result)
{
    CFType type;
    if (!decoder->decodeEnum(type))
        return false;

    switch (type) {
    case CFArray: {
        RetainPtr<CFArrayRef> array;
        if (!decode(decoder, array))
            return false;
        result.adoptCF(array.leakRef());
        return true;
    }
    case CFBoolean: {
        RetainPtr<CFBooleanRef> boolean;
        if (!decode(decoder, boolean))
            return false;
        result.adoptCF(boolean.leakRef());
        return true;
    }
    case CFData: {
        RetainPtr<CFDataRef> data;
        if (!decode(decoder, data))
            return false;
        result.adoptCF(data.leakRef());
        return true;
    }
    case CFDate: {
        RetainPtr<CFDateRef> date;
        if (!decode(decoder, date))
            return false;
        result.adoptCF(date.leakRef());
        return true;
    }
    case CFDictionary: {
        RetainPtr<CFDictionaryRef> dictionary;
        if (!decode(decoder, dictionary))
            return false;
        result.adoptCF(dictionary.leakRef());
        return true;
    }
    case CFNull:
        result.adoptCF(kCFNull);
        return true;
    case CFNumber: {
        RetainPtr<CFNumberRef> number;
        if (!decode(decoder, number))
            return false;
        result.adoptCF(number.leakRef());
        return true;
    }
    case CFString: {
        RetainPtr<CFStringRef> string;
        if (!decode(decoder, string))
            return false;
        result.adoptCF(string.leakRef());
        return true;
    }
    case CFURL: {
        RetainPtr<CFURLRef> url;
        if (!decode(decoder, url))
            return false;
        result.adoptCF(url.leakRef());
        return true;
    }
#if PLATFORM(MAC)
    case SecCertificate: {
        RetainPtr<SecCertificateRef> certificate;
        if (!decode(decoder, certificate))
            return false;
        result.adoptCF(certificate.leakRef());
        return true;
    }
    case SecKeychainItem: {
        RetainPtr<SecKeychainItemRef> keychainItem;
        if (!decode(decoder, keychainItem))
            return false;
        result.adoptCF(keychainItem.leakRef());
        return true;
    }
#endif
    case Null:
        result = tokenNullTypeRef();
        return true;
    case Unknown:
        ASSERT_NOT_REACHED();
        return false;
    }

    return false;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<CFArrayRef>& result)
{
    uint64_t size;
    if (!decoder->decodeUInt64(size))
        return false;

    RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));

    for (size_t i = 0; i < size; ++i) {
        RetainPtr<CFTypeRef> element;
        if (!decode(decoder, element))
            return false;

        CFArrayAppendValue(array.get(), element.get());
    }

    result.adoptCF(array.leakRef());
    return true;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<CFBooleanRef>& result)
{
    bool boolean;
    if (!decoder->decode(boolean))
        return false;

    result.adoptCF(boolean ? kCFBooleanTrue : kCFBooleanFalse);
    return true;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<CFDataRef>& result)
{
    CoreIPC::DataReference dataReference;
    if (!decoder->decode(dataReference))
        return false;

    result.adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size()));
    return true;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<CFDateRef>& result)
{
    double absoluteTime;
    if (!decoder->decodeDouble(absoluteTime))
        return false;

    result.adoptCF(CFDateCreate(0, absoluteTime));
    return true;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<CFNumberRef>& result)
{
    CFNumberType numberType;
    if (!decoder->decodeEnum(numberType))
        return false;

    CoreIPC::DataReference dataReference;
    if (!decoder->decode(dataReference))
        return false;

    size_t neededBufferSize = sizeForNumberType(numberType);
    if (!neededBufferSize || dataReference.size() != neededBufferSize)
        return false;

    ASSERT(dataReference.data());
    CFNumberRef number = CFNumberCreate(0, numberType, dataReference.data());
    result.adoptCF(number);

    return true;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<CFStringRef>& result)
{
    CFStringEncoding encoding;
    if (!decoder->decodeEnum(encoding))
        return false;

    if (!CFStringIsEncodingAvailable(encoding))
        return false;

    CoreIPC::DataReference dataReference;
    if (!decoder->decode(dataReference))
        return false;

    CFStringRef string = CFStringCreateWithBytes(0, dataReference.data(), dataReference.size(), encoding, false);
    if (!string)
        return false;

    result.adoptCF(string);
    return true;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<SecCertificateRef>& result)
{
    RetainPtr<CFDataRef> data;
    if (!decode(decoder, data))
        return false;

    result.adoptCF(SecCertificateCreateWithData(0, data.get()));
    return true;
}


bool decode(ArgumentDecoder* decoder, RetainPtr<SecKeychainItemRef>& result)
{
    RetainPtr<CFDataRef> data;
    if (!CoreIPC::decode(decoder, data))
        return false;

    SecKeychainItemRef item;
    if (SecKeychainItemCopyFromPersistentReference(data.get(), &item) != errSecSuccess || !item)
        return false;

    result.adoptCF(item);
    return true;
}


void encode(ArgumentEncoder* encoder, CFTypeRef typeRef)
{
    CFType type = typeFromCFTypeRef(typeRef);
    encoder->encodeEnum(type);

    switch (type) {
    case CFArray:
        encode(encoder, static_cast<CFArrayRef>(typeRef));
        return;
    case CFBoolean:
        encode(encoder, static_cast<CFBooleanRef>(typeRef));
        return;
    case CFData:
        encode(encoder, static_cast<CFDataRef>(typeRef));
        return;
    case CFDate:
        encode(encoder, static_cast<CFDateRef>(typeRef));
        return;
    case CFDictionary:
        encode(encoder, static_cast<CFDictionaryRef>(typeRef));
        return;
    case CFNull:
        return;
    case CFNumber:
        encode(encoder, static_cast<CFNumberRef>(typeRef));
        return;
    case CFString:
        encode(encoder, static_cast<CFStringRef>(typeRef));
        return;
    case CFURL:
        encode(encoder, static_cast<CFURLRef>(typeRef));
        return;
#if PLATFORM(MAC)
    case SecCertificate:
        encode(encoder, (SecCertificateRef)typeRef);
        return;
    case SecKeychainItem:
        encode(encoder, (SecKeychainItemRef)typeRef);
        return;
#endif
    case Null:
        return;
    case Unknown:
        break;
    }

    ASSERT_NOT_REACHED();
}


void encode(ArgumentEncoder* encoder, CFArrayRef array)
{
    CFIndex size = CFArrayGetCount(array);
    Vector<CFTypeRef, 32> values(size);

    CFArrayGetValues(array, CFRangeMake(0, size), values.data());

    encoder->encodeUInt64(size);
    for (CFIndex i = 0; i < size; ++i) {
        ASSERT(values[i]);

        encode(encoder, values[i]);
    }
}


void encode(ArgumentEncoder* encoder, CFBooleanRef boolean)
{
    encoder->encodeBool(CFBooleanGetValue(boolean));
}


void encode(ArgumentEncoder* encoder, CFDataRef data)
{
    CFIndex length = CFDataGetLength(data);
    const UInt8* bytePtr = CFDataGetBytePtr(data);

    encoder->encodeVariableLengthByteArray(CoreIPC::DataReference(bytePtr, length));
}


void encode(ArgumentEncoder* encoder, CFDateRef date)
{
    encoder->encodeDouble(CFDateGetAbsoluteTime(date));
}


void encode(ArgumentEncoder* encoder, CFDictionaryRef dictionary)
{
    CFIndex size = CFDictionaryGetCount(dictionary);
    Vector<CFTypeRef, 32> keys(size);
    Vector<CFTypeRef, 32> values(size);

    CFDictionaryGetKeysAndValues(dictionary, keys.data(), values.data());

    encoder->encodeUInt64(size);

    for (CFIndex i = 0; i < size; ++i) {
        ASSERT(keys[i]);
        ASSERT(CFGetTypeID(keys[i]) == CFStringGetTypeID());
        ASSERT(values[i]);

        if (typeFromCFTypeRef(values[i]) == Unknown)
            continue;

        encode(encoder, static_cast<CFStringRef>(keys[i]));
        encode(encoder, values[i]);
    }
}


void encode(ArgumentEncoder* encoder, CFNumberRef number)
{
    CFNumberType numberType = CFNumberGetType(number);

    Vector<uint8_t> buffer(CFNumberGetByteSize(number));
    bool result = CFNumberGetValue(number, numberType, buffer.data());
    ASSERT_UNUSED(result, result);

    encoder->encodeEnum(numberType);
    encoder->encodeVariableLengthByteArray(buffer);
}


void encode(ArgumentEncoder* encoder, CFURLRef url)
{
    CFURLRef baseURL = CFURLGetBaseURL(url);
    encoder->encodeBool(baseURL);
    if (baseURL)
        encode(encoder, baseURL);

    encode(encoder, CFURLGetString(url));
}


void encode(ArgumentEncoder* encoder, SecCertificateRef certificate)
{
    RetainPtr<CFDataRef> data(AdoptCF, SecCertificateCopyData(certificate));
    encode(encoder, data.get());
}


void encode(ArgumentEncoder* encoder, SecKeychainItemRef keychainItem)
{
    CFDataRef data;
    if (SecKeychainItemCreatePersistentReference(keychainItem, &data) == errSecSuccess) {
        encode(encoder, data);
        CFRelease(data);
    }
}


static size_t sizeForNumberType(CFNumberType numberType)
{
    switch (numberType) {
    case kCFNumberSInt8Type:
        return sizeof(SInt8);
    case kCFNumberSInt16Type:
        return sizeof(SInt16);
    case kCFNumberSInt32Type:
        return sizeof(SInt32);
    case kCFNumberSInt64Type:
        return sizeof(SInt64);
    case kCFNumberFloat32Type:
        return sizeof(Float32);
    case kCFNumberFloat64Type:
        return sizeof(Float64);
    case kCFNumberCharType:
        return sizeof(char);
    case kCFNumberShortType:
        return sizeof(short);
    case kCFNumberIntType:
        return sizeof(int);
    case kCFNumberLongType:
        return sizeof(long);
    case kCFNumberLongLongType:
        return sizeof(long long);
    case kCFNumberFloatType:
        return sizeof(float);
    case kCFNumberDoubleType:
        return sizeof(double);
    case kCFNumberCFIndexType:
        return sizeof(CFIndex);
    case kCFNumberNSIntegerType:
#ifdef __LP64__
        return sizeof(long);
#else
        return sizeof(int);
#endif
    case kCFNumberCGFloatType:
#ifdef __LP64__
        return sizeof(double);
#else
        return sizeof(float);
#endif
    }

    return 0;
}


 CFTypeRef tokenNullTypeRef()
{
    static CFStringRef tokenNullType = CFSTR("WKNull");
    return tokenNullType;
}


static CFType typeFromCFTypeRef(CFTypeRef type)
{
    ASSERT(type);

    if (type == tokenNullTypeRef())
        return Null;

    CFTypeID typeID = CFGetTypeID(type);
    if (typeID == CFArrayGetTypeID())
        return CFArray;
    if (typeID == CFBooleanGetTypeID())
        return CFBoolean;
    if (typeID == CFDataGetTypeID())
        return CFData;
    if (typeID == CFDateGetTypeID())
        return CFDate;
    if (typeID == CFDictionaryGetTypeID())
        return CFDictionary;
    if (typeID == CFNullGetTypeID())
        return CFNull;
    if (typeID == CFNumberGetTypeID())
        return CFNumber;
    if (typeID == CFStringGetTypeID())
        return CFString;
    if (typeID == CFURLGetTypeID())
        return CFURL;
#if PLATFORM(MAC)
    if (typeID == SecCertificateGetTypeID())
        return SecCertificate;
    if (typeID == SecKeychainItemGetTypeID())
        return SecKeychainItem;
#endif

    ASSERT_NOT_REACHED();
    return Unknown;
}


WebProcessProxy::WebProcessProxy(PassRefPtr<WebContext> context)
    : ChildProcessProxy(this)
    , m_responsivenessTimer(this)
    , m_context(context)
    , m_mayHaveUniversalFileReadSandboxExtension(false)
#if ENABLE(CUSTOM_PROTOCOLS)
    , m_customProtocolManagerProxy(this)
#endif
#if PLATFORM(MAC)
    , m_processSuppressionEnabled(false)
#endif
{
    connect();
}


void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& originalURL, const String& url, const String& title, const CoreIPC::DataReference& backForwardData)
{
    MESSAGE_CHECK_URL(originalURL);
    MESSAGE_CHECK_URL(url);

    WebBackForwardListItemMap::AddResult result = m_backForwardListItemMap.add(itemID, 0);
    if (result.isNewEntry) {
        result.iterator->value = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID);
        return;
    }

    result.iterator->value->setOriginalURL(originalURL);
    result.iterator->value->setURL(url);
    result.iterator->value->setTitle(title);
    result.iterator->value->setBackForwardData(backForwardData.data(), backForwardData.size());
}


void WebProcessProxy::addMessageReceiver(CoreIPC::StringReference messageReceiverName, CoreIPC::MessageReceiver* messageReceiver)
{
    m_messageReceiverMap.addMessageReceiver(messageReceiverName, messageReceiver);
}


void WebProcessProxy::addMessageReceiver(CoreIPC::StringReference messageReceiverName, uint64_t destinationID, CoreIPC::MessageReceiver* messageReceiver)
{
    m_messageReceiverMap.addMessageReceiver(messageReceiverName, destinationID, messageReceiver);
}


void WebProcessProxy::assumeReadAccessToBaseURL(const String& urlString)
{
    KURL url(KURL(), urlString);
    if (!url.isLocalFile())
        return;

    KURL baseURL(KURL(), url.baseAsString());

    m_localPathsWithAssumedReadAccess.add(baseURL.fileSystemPath());
}


bool WebProcessProxy::checkURLReceivedFromWebProcess(const String& urlString)
{
    return checkURLReceivedFromWebProcess(KURL(KURL(), urlString));
}


bool WebProcessProxy::checkURLReceivedFromWebProcess(const KURL& url)
{

    if (!url.isLocalFile())
        return true;

    if (m_mayHaveUniversalFileReadSandboxExtension)
        return true;

    String path = url.fileSystemPath();
    for (HashSet<String>::const_iterator iter = m_localPathsWithAssumedReadAccess.begin(); iter != m_localPathsWithAssumedReadAccess.end(); ++iter) {
        if (path.startsWith(*iter))
            return true;
    }

    for (WebBackForwardListItemMap::iterator iter = m_backForwardListItemMap.begin(), end = m_backForwardListItemMap.end(); iter != end; ++iter) {
        if (KURL(KURL(), iter->value->url()).fileSystemPath() == path)
            return true;
        if (KURL(KURL(), iter->value->originalURL()).fileSystemPath() == path)
            return true;
    }

    WTFLogAlways("Received an unexpected URL from the web process: '%s'\n", url.string().utf8().data());
    return false;
}


PassRefPtr<WebProcessProxy> WebProcessProxy::create(PassRefPtr<WebContext> context)
{
    return adoptRef(new WebProcessProxy(context));
}


DownloadProxy* WebProcessProxy::createDownloadProxy()
{
#if ENABLE(NETWORK_PROCESS)
    ASSERT(!m_context->usesNetworkProcess());
#endif

    if (!m_downloadProxyMap)
        m_downloadProxyMap = adoptPtr(new DownloadProxyMap(m_messageReceiverMap));

    return m_downloadProxyMap->createDownloadProxy(m_context.get());
}


PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient* pageClient, WebContext*, WebPageGroup* pageGroup)
{
    uint64_t pageID = generatePageID();
    RefPtr<WebPageProxy> webPage = WebPageProxy::create(pageClient, this, pageGroup, pageID);
    m_pageMap.set(pageID, webPage.get());
    globalPageMap().set(pageID, webPage.get());
#if PLATFORM(MAC)
    if (pageIsProcessSuppressible(webPage.get()))
        m_processSuppressiblePages.add(pageID);
    updateProcessSuppressionState();
#endif
    return webPage.release();
}


void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*)
{
    Vector<RefPtr<WebPageProxy> > pages;
    copyValuesToVector(m_pageMap, pages);
    for (size_t i = 0, size = pages.size(); i < size; ++i)
        pages[i]->processDidBecomeUnresponsive();
}


void WebProcessProxy::didClearPluginSiteData(uint64_t callbackID)
{
    m_context->pluginSiteDataManager()->didClearSiteData(callbackID);
}


void WebProcessProxy::didClose(CoreIPC::Connection*)
{
    RefPtr<WebProcessProxy> protect(this);

    webConnection()->didClose();

    Vector<RefPtr<WebPageProxy> > pages;
    copyValuesToVector(m_pageMap, pages);

    disconnect();

    for (size_t i = 0, size = pages.size(); i < size; ++i)
        pages[i]->processDidCrash();

}


void WebProcessProxy::didDestroyFrame(uint64_t frameID)
{
    ASSERT(isGoodKey<WebFrameProxyMap>(frameID));
    m_frameMap.remove(frameID);
}


void WebProcessProxy::didFinishLaunching(ProcessLauncher* launcher, CoreIPC::Connection::Identifier connectionIdentifier)
{
    ChildProcessProxy::didFinishLaunching(launcher, connectionIdentifier);

#if USE(SECURITY_FRAMEWORK)
    connection()->addQueueClient(&SecItemShimProxy::shared());
#endif

    m_webConnection = WebConnectionToWebProcess::create(this);

    m_context->processDidFinishLaunching(this);

#if PLATFORM(MAC)
    updateProcessSuppressionState();
#endif
}


void WebProcessProxy::didGetSitesWithPluginData(const Vector<String>& sites, uint64_t callbackID)
{
    m_context->pluginSiteDataManager()->didGetSitesWithData(sites, callbackID);
}


void WebProcessProxy::didNavigateWithNavigationData(uint64_t pageID, const WebNavigationDataStore& store, uint64_t frameID)
{
    WebPageProxy* page = webPage(pageID);
    if (!page)
        return;

    WebFrameProxy* frame = webFrame(frameID);
    MESSAGE_CHECK(frame);
    MESSAGE_CHECK(frame->page() == page);

    m_context->historyClient().didNavigateWithNavigationData(m_context.get(), page, store, frame);
}


void WebProcessProxy::didPerformServerRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
{
    WebPageProxy* page = webPage(pageID);
    if (!page)
        return;

    if (sourceURLString.isEmpty() || destinationURLString.isEmpty())
        return;

    WebFrameProxy* frame = webFrame(frameID);
    MESSAGE_CHECK(frame);
    MESSAGE_CHECK(frame->page() == page);
    MESSAGE_CHECK_URL(sourceURLString);
    MESSAGE_CHECK_URL(destinationURLString);

    m_context->historyClient().didPerformServerRedirect(m_context.get(), page, sourceURLString, destinationURLString, frame);
}


void WebProcessProxy::didReceiveInvalidMessage(CoreIPC::Connection* connection, CoreIPC::StringReference messageReceiverName, CoreIPC::StringReference messageName)
{
    WTFLogAlways("Received an invalid message \"%s.%s\" from the web process.\n", messageReceiverName.toString().data(), messageName.toString().data());

    terminate();

    didClose(connection);
}


void WebProcessProxy::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::MessageDecoder& decoder)
{
    if (m_messageReceiverMap.dispatchMessage(connection, messageID, decoder))
        return;

    if (m_context->dispatchMessage(connection, messageID, decoder))
        return;

    if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
        didReceiveWebProcessProxyMessage(connection, messageID, decoder);
        return;
    }

#if ENABLE(CUSTOM_PROTOCOLS)
    if (decoder.messageReceiverName() == Messages::CustomProtocolManagerProxy::messageReceiverName()) {
#if ENABLE(NETWORK_PROCESS)
        ASSERT(!context()->usesNetworkProcess());
#endif
        m_customProtocolManagerProxy.didReceiveMessage(connection, messageID, decoder);
        return;
    }
#endif

    uint64_t pageID = decoder.destinationID();
    if (!pageID)
        return;

    WebPageProxy* pageProxy = webPage(pageID);
    if (!pageProxy)
        return;

    pageProxy->didReceiveMessage(connection, messageID, decoder);
}


void WebProcessProxy::didReceiveMessageOnConnectionWorkQueue(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::MessageDecoder& decoder, bool& didHandleMessage)
{
    if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName())
        didReceiveWebProcessProxyMessageOnConnectionWorkQueue(connection, messageID, decoder, didHandleMessage);
}


void WebProcessProxy::didUpdateHistoryTitle(uint64_t pageID, const String& title, const String& url, uint64_t frameID)
{
    WebPageProxy* page = webPage(pageID);
    if (!page)
        return;

    WebFrameProxy* frame = webFrame(frameID);
    MESSAGE_CHECK(frame);
    MESSAGE_CHECK(frame->page() == page);
    MESSAGE_CHECK_URL(url);

    m_context->historyClient().didUpdateHistoryTitle(m_context.get(), page, title, url, frame);
}


void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page)
{
    Vector<RefPtr<WebFrameProxy> > frames;
    copyValuesToVector(m_frameMap, frames);
    for (size_t i = 0, size = frames.size(); i < size; ++i) {
        if (frames[i]->page() == page)
            frames[i]->disconnect();
    }
}


size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const
{
    size_t result = 0;
    for (HashMap<uint64_t, RefPtr<WebFrameProxy> >::const_iterator iter = m_frameMap.begin(); iter != m_frameMap.end(); ++iter) {
        if (iter->value->page() == page)
            ++result;
    }
    return result;
}


void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy)
{
    ASSERT(canCreateFrame(frameID));
    m_frameMap.set(frameID, frameProxy);
}


bool WebProcessProxy::fullKeyboardAccessEnabled()
{
    return false;
}


static uint64_t generatePageID()
{
    static uint64_t uniquePageID = 1;
    return uniquePageID++;
}


void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
{
    launchOptions.processType = ProcessLauncher::WebProcess;
    platformGetLaunchOptions(launchOptions);
}


void WebProcessProxy::getNetworkProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetNetworkProcessConnection::DelayedReply> reply)
{
    m_context->getNetworkProcessConnection(reply);
}


void WebProcessProxy::getPluginPath(const String& mimeType, const String& urlString, String& pluginPath, uint32_t& pluginLoadPolicy)
{
    MESSAGE_CHECK_URL(urlString);

    String newMimeType = mimeType.lower();

    pluginLoadPolicy = PluginModuleLoadNormally;
    PluginModuleInfo plugin = m_context->pluginInfoStore().findPlugin(newMimeType, KURL(KURL(), urlString));
    if (!plugin.path)
        return;

    pluginLoadPolicy = PluginInfoStore::policyForPlugin(plugin);
    if (pluginLoadPolicy != PluginModuleLoadNormally)
        return;

    pluginPath = plugin.path;
}


void WebProcessProxy::getPluginProcessConnection(const String& pluginPath, uint32_t processType, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
{
    PluginProcessManager::shared().getPluginProcessConnection(m_context->pluginInfoStore(), pluginPath, static_cast<PluginProcess::Type>(processType), reply);
}


void WebProcessProxy::getPlugins(CoreIPC::Connection*, uint64_t requestID, bool refresh)
{
    pluginWorkQueue().dispatch(bind(&WebProcessProxy::handleGetPlugins, this, requestID, refresh));
}


void WebProcessProxy::getSharedWorkerProcessConnection(const String& /* url */, const String& /* name */, PassRefPtr<Messages::WebProcessProxy::GetSharedWorkerProcessConnection::DelayedReply>)
{
}


static WebProcessProxy::WebPageProxyMap& globalPageMap()
{
    ASSERT(isMainThread());
    DEFINE_STATIC_LOCAL(WebProcessProxy::WebPageProxyMap, pageMap, ());
    return pageMap;
}


void WebProcessProxy::interactionOccurredWhileUnresponsive(ResponsivenessTimer*)
{
    Vector<RefPtr<WebPageProxy> > pages;
    copyValuesToVector(m_pageMap, pages);
    for (size_t i = 0, size = pages.size(); i < size; ++i)
        pages[i]->interactionOccurredWhileProcessUnresponsive();
}


static inline bool isGoodKey(const typename HashMap::KeyType& key)
{
    return key != HashTraits<typename HashMap::KeyType>::emptyValue() && !HashTraits<typename HashMap::KeyType>::isDeletedValue(key);
}


void WebProcessProxy::pagePreferencesChanged(WebKit::WebPageProxy *page)
{
#if PLATFORM(MAC)
    if (pageIsProcessSuppressible(page))
        m_processSuppressiblePages.add(page->pageID());
    else
        m_processSuppressiblePages.remove(page->pageID());
    updateProcessSuppressionState();
#else
    UNUSED_PARAM(page);
#endif
}


void WebProcessProxy::pageVisibilityChanged(WebKit::WebPageProxy *page)
{
#if PLATFORM(MAC)
    if (pageIsProcessSuppressible(page))
        m_processSuppressiblePages.add(page->pageID());
    else
        m_processSuppressiblePages.remove(page->pageID());
    updateProcessSuppressionState();
#else
    UNUSED_PARAM(page);
#endif
}


static WorkQueue& pluginWorkQueue()
{
    DEFINE_STATIC_LOCAL(WorkQueue, queue, ("com.apple.CoreIPC.PluginQueue"));
    return queue;
}


void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* item)
{
    ASSERT(!m_backForwardListItemMap.contains(item->itemID()));

    m_backForwardListItemMap.set(item->itemID(), item);
}


void WebProcessProxy::removeMessagePortChannel(uint64_t channelID)
{
    if (!isValid())
        return;

    send(Messages::WebProcess::RemoveMessagePortChannel(channelID), /* destinationID */ 0);
}


void WebProcessProxy::removeMessageReceiver(CoreIPC::StringReference messageReceiverName, uint64_t destinationID)
{
    m_messageReceiverMap.removeMessageReceiver(messageReceiverName, destinationID);
}


void WebProcessProxy::removeWebPage(uint64_t pageID)
{
    m_pageMap.remove(pageID);
    globalPageMap().remove(pageID);
#if PLATFORM(MAC)
    m_processSuppressiblePages.remove(pageID);
    updateProcessSuppressionState();
#endif
}


void WebProcessProxy::sendDidGetPlugins(uint64_t requestID, PassOwnPtr<Vector<PluginInfo> > pluginInfos)
{
    ASSERT(isMainThread());

    OwnPtr<Vector<PluginInfo> > plugins(pluginInfos);

#if PLATFORM(MAC)
    if (!m_context->omitPDFSupport()) {
#if ENABLE(PDFKIT_PLUGIN)
        plugins->append(PDFPlugin::pluginInfo());
#endif
        plugins->append(SimplePDFPlugin::pluginInfo());
    }
#endif

    send(Messages::WebProcess::DidGetPlugins(requestID, *plugins), 0);
}


void WebProcessProxy::shouldTerminate(bool& shouldTerminate)
{
    if (!m_pageMap.isEmpty() || (m_downloadProxyMap && !m_downloadProxyMap->isEmpty()) || !m_context->shouldTerminate(this)) {
        shouldTerminate = false;
        return;
    }

    shouldTerminate = true;

    disconnect();
}


void WebProcessProxy::updateTextCheckerState()
{
    if (canSendMessage())
        send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
}


WebPageProxy* WebProcessProxy::webPage(uint64_t pageID)
{
    return globalPageMap().get(pageID);
}


WebProcessProxy::~WebProcessProxy()
{
    if (m_webConnection)
        m_webConnection->invalidate();
}


v8::Handle<v8::Value> V8ThrowException::createRangeError(v8::Isolate* isolate, const String& message)
{
    return v8::Exception::RangeError(v8String(isolate, message.isNull() ? "Range error" : message));
}


v8::Handle<v8::Value> V8ThrowException::createSyntaxError(v8::Isolate* isolate, const String& message)
{
    return v8::Exception::SyntaxError(v8String(isolate, message.isNull() ? "Syntax error" : message));
}


v8::Handle<v8::Value> V8ThrowException::createTypeError(v8::Isolate* isolate, const String& message)
{
    return v8::Exception::TypeError(v8String(isolate, message.isNull() ? "Type error" : message));
}


static void domExceptionStackGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
{
    ASSERT(info.Data()->IsObject());
    v8SetReturnValue(info, info.Data()->ToObject(info.GetIsolate())->Get(v8AtomicString(info.GetIsolate(), "stack")));
}


static void domExceptionStackSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
{
    ASSERT(info.Data()->IsObject());
    info.Data()->ToObject(info.GetIsolate())->Set(v8AtomicString(info.GetIsolate(), "stack"), value);
}


v8::Handle<v8::Value> V8ThrowException::throwDOMException(int ec, const String& sanitizedMessage, const String& unsanitizedMessage, const v8::Handle<v8::Object>& creationContext, v8::Isolate* isolate)
{
    ASSERT(ec == SecurityError || unsanitizedMessage.isEmpty());
    v8::Handle<v8::Value> exception = createDOMException(isolate, ec, sanitizedMessage, unsanitizedMessage, creationContext);
    if (exception.IsEmpty())
        return v8Undefined();

    return V8ThrowException::throwException(exception, isolate);
}


v8::Handle<v8::Value> V8ThrowException::throwReferenceError(v8::Isolate* isolate, const String& message)
{
    v8::Handle<v8::Value> exception = V8ThrowException::createReferenceError(isolate, message);
    return V8ThrowException::throwException(exception, isolate);
}


v8::Handle<v8::Value> V8ThrowException::throwSyntaxError(v8::Isolate* isolate, const String& message)
{
    v8::Handle<v8::Value> exception = V8ThrowException::createSyntaxError(isolate, message);
    return V8ThrowException::throwException(exception, isolate);
}


v8::Handle<v8::Value> V8ThrowException::throwTypeError(v8::Isolate* isolate, const String& message)
{
    v8::Handle<v8::Value> exception = V8ThrowException::createTypeError(isolate, message);
    return V8ThrowException::throwException(exception, isolate);
}


gfx::Rect OverlayWindowViews::CalculateAndUpdateWindowBounds() {
  gfx::Rect work_area =
      display::Screen::GetScreen()
          ->GetDisplayNearestWindow(
              controller_->GetInitiatorWebContents()->GetTopLevelNativeWindow())
          .work_area();

  max_size_ = gfx::Size(work_area.width() / 2, work_area.height() / 2);

  min_size_ = kMinWindowSize;

  gfx::Size window_size = window_bounds_.size();
  if (!has_been_shown_) {
    window_size = gfx::Size(work_area.width() / 5, work_area.height() / 5);
    window_size.set_width(std::min(
        max_size_.width(), std::max(min_size_.width(), window_size.width())));
    window_size.set_height(
        std::min(max_size_.height(),
                 std::max(min_size_.height(), window_size.height())));
  }

  if (!window_size.IsEmpty() && !natural_size_.IsEmpty()) {
    float aspect_ratio = (float)natural_size_.width() / natural_size_.height();

    gfx::Rect window_rect(GetBounds().origin(), window_size);
    views::WindowResizeUtils::SizeRectToAspectRatio(
        views::HitTest::kBottomRight, aspect_ratio, min_size_, max_size_,
        &window_rect);
    window_size.SetSize(window_rect.width(), window_rect.height());

    UpdateLayerBoundsWithLetterboxing(window_size);
  }

  gfx::Point origin = window_bounds_.origin();

  int window_diff_width = work_area.right() - window_size.width();
  int window_diff_height = work_area.bottom() - window_size.height();

  int buffer = (window_diff_width + window_diff_height) / 2 * 0.02;

  gfx::Point default_origin =
      gfx::Point(window_diff_width - buffer, window_diff_height - buffer);

  if (has_been_shown_) {
    origin.SetToMin(default_origin);
  } else {
    origin = default_origin;
  }

  window_bounds_ = gfx::Rect(origin, window_size);
  return window_bounds_;
}


gfx::Rect OverlayWindowViews::CalculateControlsBounds(int x,
                                                      const gfx::Size& size) {
  return gfx::Rect(
      gfx::Point(x, (GetBounds().size().height() - size.height()) / 2), size);
}


void OverlayWindowViews::Close() {
  views::Widget::Close();
}


std::unique_ptr<content::OverlayWindow> content::OverlayWindow::Create(
    content::PictureInPictureWindowController* controller) {
  return base::WrapUnique(new OverlayWindowViews(controller));
}


void OverlayWindowViews::CreateCustomControl(
    std::unique_ptr<views::ControlImageButton>& control_button,
    const blink::PictureInPictureControlInfo& info,
    ControlPosition position) {
  control_button = std::make_unique<views::ControlImageButton>(this);
  controls_parent_view_->AddChildView(control_button.get());
  control_button->set_id(info.id);
  control_button->set_owned_by_client();

  control_button->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
                                    views::ImageButton::ALIGN_MIDDLE);
  UpdateCustomControlsSize(control_button.get());
  UpdateControlsBounds();

  base::string16 custom_button_label = base::UTF8ToUTF16(info.label);
  control_button->SetAccessibleName(custom_button_label);
  control_button->SetTooltipText(custom_button_label);
  control_button->SetInstallFocusRingOnFocus(true);
  control_button->SetFocusForPlatform();
}


gfx::Rect OverlayWindowViews::GetBounds() const {
  return views::Widget::GetRestoredBounds();
}


gfx::Rect OverlayWindowViews::GetCloseControlsBounds() {
  return close_controls_view_->GetMirroredBounds();
}


ui::Layer* OverlayWindowViews::GetControlsParentLayer() {
  return controls_parent_view_->layer();
}


ui::Layer* OverlayWindowViews::GetControlsScrimLayer() {
  return controls_scrim_view_->layer();
}


OverlayWindowViews::WindowQuadrant GetCurrentWindowQuadrant(
    const gfx::Rect window_bounds,
    content::PictureInPictureWindowController* controller) {
  gfx::Rect work_area =
      display::Screen::GetScreen()
          ->GetDisplayNearestWindow(
              controller->GetInitiatorWebContents()->GetTopLevelNativeWindow())
          .work_area();
  gfx::Point window_center = window_bounds.CenterPoint();

  if (window_center.x() < work_area.width() / 2) {
    return (window_center.y() < work_area.height() / 2)
               ? OverlayWindowViews::WindowQuadrant::kTopLeft
               : OverlayWindowViews::WindowQuadrant::kBottomLeft;
  }
  return (window_center.y() < work_area.height() / 2)
             ? OverlayWindowViews::WindowQuadrant::kTopRight
             : OverlayWindowViews::WindowQuadrant::kBottomRight;
}


gfx::Rect OverlayWindowViews::GetFirstCustomControlsBounds() {
  if (!first_custom_controls_view_)
    return gfx::Rect();
  return first_custom_controls_view_->GetMirroredBounds();
}


ui::Layer* OverlayWindowViews::GetLayer() {
  return views::Widget::GetLayer();
}


gfx::Size OverlayWindowViews::GetMaximumSize() const {
  return max_size_;
}


gfx::Rect OverlayWindowViews::GetPlayPauseControlsBounds() {
  return play_pause_controls_view_->GetMirroredBounds();
}


ui::Layer* OverlayWindowViews::GetResizeHandleLayer() {
  return resize_handle_view_->layer();
}


gfx::Rect OverlayWindowViews::GetSecondCustomControlsBounds() {
  if (!second_custom_controls_view_)
    return gfx::Rect();
  return second_custom_controls_view_->GetMirroredBounds();
}


gfx::Rect OverlayWindowViews::GetVideoBounds() {
  return video_bounds_;
}


ui::Layer* OverlayWindowViews::GetVideoLayer() {
  return video_view_->layer();
}


ui::Layer* OverlayWindowViews::GetWindowBackgroundLayer() {
  return window_background_view_->layer();
}


bool OverlayWindowViews::HasOnlyOneCustomControl() {
  return first_custom_controls_view_ && !second_custom_controls_view_;
}


void OverlayWindowViews::Hide() {
  views::Widget::Hide();
}


bool OverlayWindowViews::IsActive() const {
  return views::Widget::IsActive();
}


bool OverlayWindowViews::IsAlwaysOnTop() const {
  return true;
}


bool OverlayWindowViews::IsVisible() const {
  return views::Widget::IsVisible();
}


void OverlayWindowViews::OnKeyEvent(ui::KeyEvent* event) {
  hide_controls_timer_.Reset();

  if (event->type() == ui::ET_KEY_PRESSED ||
      event->key_code() == ui::VKEY_TAB) {
    UpdateControlsVisibility(true);
  }

#if defined(OS_MACOSX)
  if (event->type() == ui::ET_KEY_PRESSED &&
      event->key_code() == ui::VKEY_SPACE) {
    TogglePlayPause();
    event->SetHandled();
  }
#endif  // OS_MACOSX

#if defined(OS_WIN)
  if (event->type() == ui::ET_KEY_PRESSED && event->IsAltDown() &&
      event->key_code() == ui::VKEY_F4) {
    controller_->Close(true /* should_pause_video */,
                       true /* should_reset_pip_player */);
    event->SetHandled();
  }
#endif  // OS_WIN

  views::Widget::OnKeyEvent(event);
}


void OverlayWindowViews::OnNativeBlur() {
  if (is_initialized_)
    UpdateControlsVisibility(false);

  views::Widget::OnNativeBlur();
}


void OverlayWindowViews::OnNativeWidgetDestroyed() {
  controller_->OnWindowDestroyed();
}


void OverlayWindowViews::OnNativeWidgetMove() {
  if (is_initialized_)
    UpdateControlsVisibility(false);

  window_bounds_ = GetBounds();

#if defined(OS_CHROMEOS)
  WindowQuadrant quadrant = GetCurrentWindowQuadrant(GetBounds(), controller_);
  close_controls_view_->SetPosition(GetBounds().size(), quadrant);
  resize_handle_view_->SetPosition(GetBounds().size(), quadrant);
#endif
}


void OverlayWindowViews::OnNativeWidgetWorkspaceChanged() {
}


explicit OverlayWindowFrameView(views::Widget* widget) : widget_(widget) {}


  explicit OverlayWindowWidgetDelegate(views::Widget* widget)
      : widget_(widget) {
    DCHECK(widget_);
  }


void OverlayWindowViews::SetAlwaysHidePlayPauseButton(bool is_visible) {
  always_hide_play_pause_button_ = !is_visible;
}


void OverlayWindowViews::SetPictureInPictureCustomControls(
    const std::vector<blink::PictureInPictureControlInfo>& controls) {
  first_custom_controls_view_.reset();
  second_custom_controls_view_.reset();

  if (controls.size() > 0)
    CreateCustomControl(first_custom_controls_view_, controls[0],
                        ControlPosition::kLeft);
  if (controls.size() > 1)
    CreateCustomControl(second_custom_controls_view_, controls[1],
                        ControlPosition::kRight);
}


void OverlayWindowViews::SetPlaybackState(PlaybackState playback_state) {
  bool controls_parent_layer_visible = GetControlsParentLayer()->visible();

  playback_state_ = playback_state;

  switch (playback_state_) {
    case kPlaying:
      play_pause_controls_view_->SetToggled(true);
      controls_parent_view_->SetVisible(true);
      video_view_->SetVisible(true);
      GetControlsParentLayer()->SetVisible(controls_parent_layer_visible);
      break;
    case kPaused:
      play_pause_controls_view_->SetToggled(false);
      controls_parent_view_->SetVisible(true);
      video_view_->SetVisible(true);
      GetControlsParentLayer()->SetVisible(controls_parent_layer_visible);
      break;
    case kNoVideo:
      controls_scrim_view_->SetVisible(false);
      controls_parent_view_->SetVisible(false);
      video_view_->SetVisible(false);
      GetControlsParentLayer()->SetVisible(false);
      break;
  }
}


void OverlayWindowViews::SetUpViews() {
  gfx::Rect larger_window_bounds = GetBounds();
  larger_window_bounds.Inset(-1, -1);
  window_background_view_->SetSize(larger_window_bounds.size());
  window_background_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
  GetWindowBackgroundLayer()->SetColor(SK_ColorBLACK);

  controls_scrim_view_->SetSize(GetBounds().size());
  controls_scrim_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
  GetControlsScrimLayer()->SetColor(gfx::kGoogleGrey900);
  GetControlsScrimLayer()->SetOpacity(0.43f);

  controls_parent_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
  controls_parent_view_->SetSize(GetBounds().size());
  controls_parent_view_->layer()->SetFillsBoundsOpaquely(false);
  controls_parent_view_->set_owned_by_client();

  close_controls_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
  close_controls_view_->layer()->SetFillsBoundsOpaquely(false);
  close_controls_view_->set_owned_by_client();

  video_view_->SetPaintToLayer(ui::LAYER_TEXTURED);

  play_pause_controls_view_->SetImageAlignment(
      views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE);
  play_pause_controls_view_->SetToggled(controller_->IsPlayerActive());
  play_pause_controls_view_->set_owned_by_client();

#if defined(OS_CHROMEOS)
  resize_handle_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
  resize_handle_view_->layer()->SetFillsBoundsOpaquely(false);
  resize_handle_view_->set_owned_by_client();
#endif

  play_pause_controls_view_->SetFocusForPlatform();  // Make button focusable.
  const base::string16 play_pause_accessible_button_label(
      l10n_util::GetStringUTF16(
          IDS_PICTURE_IN_PICTURE_PLAY_PAUSE_CONTROL_ACCESSIBLE_TEXT));
  play_pause_controls_view_->SetAccessibleName(
      play_pause_accessible_button_label);
  const base::string16 play_button_label(
      l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_PLAY_CONTROL_TEXT));
  play_pause_controls_view_->SetTooltipText(play_button_label);
  const base::string16 pause_button_label(
      l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_PAUSE_CONTROL_TEXT));
  play_pause_controls_view_->SetToggledTooltipText(pause_button_label);
  play_pause_controls_view_->SetInstallFocusRingOnFocus(true);

  controls_parent_view_->AddChildView(play_pause_controls_view_.get());
  GetContentsView()->AddChildView(controls_scrim_view_.get());
  GetContentsView()->AddChildView(controls_parent_view_.get());
  GetContentsView()->AddChildView(close_controls_view_.get());
#if defined(OS_CHROMEOS)
  GetContentsView()->AddChildView(resize_handle_view_.get());
#endif

  UpdatePlayPauseControlsSize();
  UpdateControlsVisibility(false);
}


void OverlayWindowViews::Show() {
  views::Widget::Show();

  has_been_shown_ = true;
}


void OverlayWindowViews::TogglePlayPause() {
  bool is_active = controller_->TogglePlayPause();
  play_pause_controls_view_->SetToggled(is_active);
}


void OverlayWindowViews::UpdateButtonSize() {
  const gfx::Size window_size = GetBounds().size();
  int scaled_button_dimension =
      window_size.width() < window_size.height()
          ? window_size.width() * kControlRatioToWindow
          : window_size.height() * kControlRatioToWindow;

  int new_button_dimension =
      std::max(kMinControlButtonSize, scaled_button_dimension);

  button_size_.SetSize(new_button_dimension, new_button_dimension);
}


void OverlayWindowViews::UpdateControlsBounds() {
  gfx::Rect larger_window_bounds = GetBounds();
  larger_window_bounds.Inset(-1, -1);
  controls_scrim_view_->SetBoundsRect(
      gfx::Rect(gfx::Point(0, 0), larger_window_bounds.size()));

  WindowQuadrant quadrant = GetCurrentWindowQuadrant(GetBounds(), controller_);
  close_controls_view_->SetPosition(GetBounds().size(), quadrant);
#if defined(OS_CHROMEOS)
  resize_handle_view_->SetPosition(GetBounds().size(), quadrant);
#endif

  controls_parent_view_->SetBoundsRect(
      gfx::Rect(gfx::Point(0, 0), GetBounds().size()));

  UpdateControlsPositions();
}


void OverlayWindowViews::UpdateControlsPositions() {
  int mid_window_x = GetBounds().size().width() / 2;

  if (HasOnlyOneCustomControl()) {
    play_pause_controls_view_->SetBoundsRect(
        CalculateControlsBounds(mid_window_x, button_size_));
    first_custom_controls_view_->SetBoundsRect(CalculateControlsBounds(
        mid_window_x - button_size_.width(), button_size_));
    return;
  }

  play_pause_controls_view_->SetBoundsRect(CalculateControlsBounds(
      mid_window_x - button_size_.width() / 2, button_size_));

  if (first_custom_controls_view_ && second_custom_controls_view_) {
    first_custom_controls_view_->SetBoundsRect(CalculateControlsBounds(
        mid_window_x - button_size_.width() / 2 - button_size_.width(),
        button_size_));
    second_custom_controls_view_->SetBoundsRect(CalculateControlsBounds(
        mid_window_x + button_size_.width() / 2, button_size_));
  }
}


void OverlayWindowViews::UpdateControlsVisibility(bool is_visible) {
  if (always_hide_play_pause_button_ && is_visible)
    play_pause_controls_view_->SetVisible(false);

  GetCloseControlsLayer()->SetVisible(is_visible);

#if defined(OS_CHROMEOS)
  GetResizeHandleLayer()->SetVisible(is_visible);
#endif

  GetControlsScrimLayer()->SetVisible(
      (playback_state_ == kNoVideo) ? false : is_visible);
  GetControlsParentLayer()->SetVisible(
      (playback_state_ == kNoVideo) ? false : is_visible);
}


void OverlayWindowViews::UpdateCustomControlsSize(
    views::ControlImageButton* control_button) {
  if (!control_button)
    return;
  UpdateButtonSize();
  control_button->SetSize(button_size_);
  if (control_button == first_custom_controls_view_.get()) {
    first_custom_controls_view_->SetImage(
        views::Button::STATE_NORMAL,
        gfx::CreateVectorIcon(vector_icons::kPlayArrowIcon,
                              button_size_.width() / 2, kControlIconColor));
  }
  if (control_button == second_custom_controls_view_.get()) {
    second_custom_controls_view_->SetImage(
        views::Button::STATE_NORMAL,
        gfx::CreateVectorIcon(vector_icons::kPauseIcon,
                              button_size_.width() / 2, kControlIconColor));
  }
  const gfx::ImageSkia control_background = gfx::CreateVectorIcon(
      kPictureInPictureControlBackgroundIcon, button_size_.width(), kBgColor);
  control_button->SetBackgroundImage(kBgColor, &control_background,
                                     &control_background);
}


void OverlayWindowViews::UpdateLayerBoundsWithLetterboxing(
    gfx::Size window_size) {
  if (window_bounds_.size().IsEmpty() || natural_size_.IsEmpty())
    return;

  gfx::Rect letterbox_region = media::ComputeLetterboxRegion(
      gfx::Rect(gfx::Point(0, 0), window_size), natural_size_);
  if (letterbox_region.IsEmpty())
    return;

  gfx::Size letterbox_size = letterbox_region.size();
  gfx::Point origin =
      gfx::Point((window_size.width() - letterbox_size.width()) / 2,
                 (window_size.height() - letterbox_size.height()) / 2);

  video_bounds_.set_origin(origin);
  video_bounds_.set_size(letterbox_region.size());

  UpdateControlsBounds();

  controller_->UpdateLayerBounds();
}


void OverlayWindowViews::UpdatePlayPauseControlsSize() {
  UpdateButtonSize();
  play_pause_controls_view_->SetSize(button_size_);
  play_pause_controls_view_->SetImage(
      views::Button::STATE_NORMAL,
      gfx::CreateVectorIcon(vector_icons::kPlayArrowIcon,
                            button_size_.width() / 2, kControlIconColor));
  gfx::ImageSkia pause_icon = gfx::CreateVectorIcon(
      vector_icons::kPauseIcon, button_size_.width() / 2, kControlIconColor);
  play_pause_controls_view_->SetToggledImage(views::Button::STATE_NORMAL,
                                             &pause_icon);
  const gfx::ImageSkia play_pause_background = gfx::CreateVectorIcon(
      kPictureInPictureControlBackgroundIcon, button_size_.width(), kBgColor);
  play_pause_controls_view_->SetBackgroundImage(
      kBgColor, &play_pause_background, &play_pause_background);
}


void OverlayWindowViews::UpdateVideoSize(const gfx::Size& natural_size) {
  DCHECK(!natural_size.IsEmpty());
  natural_size_ = natural_size;
  SetAspectRatio(gfx::SizeF(natural_size_));

  SetBounds(CalculateAndUpdateWindowBounds());
}


views::View* OverlayWindowViews::controls_parent_view_for_testing() const {
  return controls_parent_view_.get();
}


OverlayWindowViews::play_pause_controls_view_for_testing() const {
  return play_pause_controls_view_.get();
}


OverlayWindowViews::playback_state_for_testing() const {
  return playback_state_;
}


gfx::Point OverlayWindowViews::resize_handle_position_for_testing() const {
  return resize_handle_view_->origin();
}


  void UpdateNetworkManagerStatus() {
    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          NewRunnableMethod(this,
                            &NetworkLibraryImpl::UpdateNetworkManagerStatus));
      return;
    }

    SystemInfo* system = GetSystemInfo();
    if (!system)
      return;

    std::string prev_cellular_service_path = cellular_ ?
        cellular_->service_path() : std::string();

    ClearNetworks();

    ParseSystem(system, &ethernet_, &wifi_networks_, &cellular_networks_,
                &remembered_wifi_networks_);

    wifi_ = NULL;
    for (size_t i = 0; i < wifi_networks_.size(); i++) {
      if (wifi_networks_[i]->connecting_or_connected()) {
        wifi_ = wifi_networks_[i];
        break;  // There is only one connected or connecting wifi network.
      }
    }
    cellular_ = NULL;
     for (size_t i = 0; i < cellular_networks_.size(); i++) {
       if (cellular_networks_[i]->connecting_or_connected()) {
         cellular_ = cellular_networks_[i];
        // If new cellular, then request update of the data plan list.
         if (cellular_networks_[i]->service_path() !=
                 prev_cellular_service_path) {
          RefreshCellularDataPlans(cellular_);
         }
         break;  // There is only one connected or connecting cellular network.
       }
    }

    available_devices_ = system->available_technologies;
    enabled_devices_ = system->enabled_technologies;
    connected_devices_ = system->connected_technologies;
    offline_mode_ = system->offline_mode;

    NotifyNetworkManagerChanged();
    FreeSystemInfo(system);
  }


int64 ClientUsageTracker::GetCachedHostUsage(const std::string& host) {
int64 ClientUsageTracker::GetCachedHostUsage(const std::string& host) const {
   HostUsageMap::const_iterator found = cached_usage_.find(host);
   if (found == cached_usage_.end())
     return 0;

  int64 usage = 0;
  const UsageMap& map = found->second;
  for (UsageMap::const_iterator iter = map.begin();
       iter != map.end(); ++iter) {
    usage += iter->second;
  }
  return usage;
}


void AddInstallerCopyTasks(const InstallerState& installer_state,
                           const FilePath& setup_path,
                           const FilePath& archive_path,
                           const FilePath& temp_path,
                           const Version& new_version,
                           WorkItemList* install_list) {
  DCHECK(install_list);
  FilePath installer_dir(installer_state.GetInstallerDirectory(new_version));
  install_list->AddCreateDirWorkItem(installer_dir);

   FilePath exe_dst(installer_dir.Append(setup_path.BaseName()));
   FilePath archive_dst(installer_dir.Append(archive_path.BaseName()));

  install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(),
                                    temp_path.value(), WorkItem::ALWAYS);

  // otherwise), there is no need to do this for the archive.  Setup.exe, on
  // the other hand, is created elsewhere so it must always be copied.
   install_list->AddMoveTreeWorkItem(archive_path.value(), archive_dst.value(),
                                     temp_path.value(), WorkItem::ALWAYS_MOVE);
 }


bool decode(ArgumentDecoder* decoder, RetainPtr<CFURLRef>& result)
{
    RetainPtr<CFURLRef> baseURL;
    bool hasBaseURL;
    if (!decoder->decodeBool(hasBaseURL))
        return false;
    if (hasBaseURL) {
        if (!decode(decoder, baseURL))
            return false;
    }

    RetainPtr<CFStringRef> string;
     if (!decode(decoder, string))
         return false;

#if PLATFORM(MAC)
    // FIXME: Move this to ArgumentCodersCFMac.mm and change this file back to be C++
    // instead of Objective-C++.
    if (!CFStringGetLength(string.get())) {
        // CFURL can't hold an empty URL, unlike NSURL.
        result = reinterpret_cast<CFURLRef>([NSURL URLWithString:@""]);
        return true;
    }
#endif

     CFURLRef url = CFURLCreateWithString(0, string.get(), baseURL.get());
     if (!url)
         return false;

    result.adoptCF(url);
    return true;
}


void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID)
{
     m_pageMap.set(pageID, webPage);
     globalPageMap().set(pageID, webPage);
 #if PLATFORM(MAC)
    if (pageIsProcessSuppressible(webPage))
         m_processSuppressiblePages.add(pageID);
     updateProcessSuppressionState();
 #endif
}


v8::Handle<v8::Value> V8ThrowException::createDOMException(v8::Isolate* isolate, int ec, const String& sanitizedMessage, const String& unsanitizedMessage, const v8::Handle<v8::Object>& creationContext)
{
    if (ec <= 0 || v8::V8::IsExecutionTerminating())
        return v8Undefined();

    ASSERT(ec == SecurityError || unsanitizedMessage.isEmpty());

    if (ec == V8GeneralError)
        return V8ThrowException::createGeneralError(isolate, sanitizedMessage);
    if (ec == V8TypeError)
        return V8ThrowException::createTypeError(isolate, sanitizedMessage);
    if (ec == V8RangeError)
        return V8ThrowException::createRangeError(isolate, sanitizedMessage);
    if (ec == V8SyntaxError)
        return V8ThrowException::createSyntaxError(isolate, sanitizedMessage);
     if (ec == V8ReferenceError)
         return V8ThrowException::createReferenceError(isolate, sanitizedMessage);

    v8::Handle<v8::Object> sanitizedCreationContext = creationContext;

    // FIXME: Is the current context always the right choice?
    Frame* frame = toFrameIfNotDetached(creationContext->CreationContext());
    if (!frame || !BindingSecurity::shouldAllowAccessToFrame(isolate, frame, DoNotReportSecurityError))
        sanitizedCreationContext = isolate->GetCurrentContext()->Global();

     RefPtrWillBeRawPtr<DOMException> domException = DOMException::create(ec, sanitizedMessage, unsanitizedMessage);
    v8::Handle<v8::Value> exception = toV8(domException.get(), sanitizedCreationContext, isolate);

     if (exception.IsEmpty())
         return v8Undefined();

    v8::Handle<v8::Value> error = v8::Exception::Error(v8String(isolate, domException->message()));
    ASSERT(!error.IsEmpty());
    ASSERT(exception->IsObject());
    exception->ToObject(isolate)->SetAccessor(v8AtomicString(isolate, "stack"), domExceptionStackGetter, domExceptionStackSetter, error);
    V8HiddenValue::setHiddenValue(isolate, exception->ToObject(isolate), V8HiddenValue::error(isolate), error);

    return exception;
}


void OverlayWindowViews::OnGestureEvent(ui::GestureEvent* event) {
  if (event->type() != ui::ET_GESTURE_TAP)
    return;

  hide_controls_timer_.Reset();

  if (!GetControlsScrimLayer()->visible()) {
    UpdateControlsVisibility(true);
    return;
  }

  if (GetCloseControlsBounds().Contains(event->location())) {
    controller_->Close(true /* should_pause_video */,
                       true /* should_reset_pip_player */);
    event->SetHandled();
  } else if (GetPlayPauseControlsBounds().Contains(event->location())) {
     TogglePlayPause();
     event->SetHandled();
   }
 }


  virtual void ConnectToCellularNetwork(const CellularNetwork* network) {
    DCHECK(network);
    if (!EnsureCrosLoaded())
      return;
    if (network && ConnectToNetwork(network->service_path().c_str(), NULL)) {
      CellularNetwork* cellular = GetWirelessNetworkByPath(
          cellular_networks_, network->service_path());
      if (cellular) {
        cellular->set_connecting(true);
        cellular_ = cellular;
      }
      NotifyNetworkManagerChanged();
    }
  }


virtual void ConnectToCellularNetwork(const CellularNetwork* network) {}


  static void DataPlanUpdateHandler(void* object,
                                    const char* modem_service_path,
                                    const CellularDataPlanList* dataplan) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    if (!networklib || !networklib->cellular_network()) {
      return;
    }
    if (networklib->cellular_network()->service_path()
        .compare(modem_service_path) == 0) {
      if (dataplan != NULL) {
        networklib->UpdateCellularDataPlan(dataplan);
      }
    }
  }


virtual void EnableCellularNetworkDevice(bool enable) {}


virtual void EnableWifiNetworkDevice(bool enable) {}


static bool EnsureCrosLoaded() {
  if (!CrosLibrary::Get()->EnsureLoaded()) {
    return false;
  } else {
    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
      LOG(ERROR) << "chromeos_library calls made from non UI thread!";
      NOTREACHED();
    }
    return true;
  }
}


  virtual CellularNetwork* FindCellularNetworkByPath(
      const std::string& path) {
    return GetWirelessNetworkByPath(cellular_networks_, path);
  }


virtual void ForgetWifiNetwork(const std::string& service_path) {}


std::string CellularNetwork::GetNetworkTechnologyString() const {
  switch (network_technology_) {
    case NETWORK_TECHNOLOGY_1XRTT:
      return "1xRTT";
      break;
    case NETWORK_TECHNOLOGY_EVDO:
      return "EVDO";
      break;
    case NETWORK_TECHNOLOGY_GPRS:
      return "GPRS";
      break;
    case NETWORK_TECHNOLOGY_EDGE:
      return "EDGE";
      break;
    case NETWORK_TECHNOLOGY_UMTS:
      return "UMTS";
      break;
    case NETWORK_TECHNOLOGY_HSPA:
      return "HSPA";
      break;
    case NETWORK_TECHNOLOGY_HSPA_PLUS:
      return "HSPA Plus";
      break;
    case NETWORK_TECHNOLOGY_LTE:
      return "LTE";
      break;
    case NETWORK_TECHNOLOGY_LTE_ADVANCED:
      return "LTE Advanced";
      break;
    default:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_CELLULAR_TECHNOLOGY_UNKNOWN);
      break;
  }
}


  NetworkLibraryStubImpl()
      : ip_address_("1.1.1.1"),
        ethernet_(new EthernetNetwork()),
        wifi_(NULL),
        cellular_(NULL) {
  }


  static void NetworkManagerStatusChangedHandler(void* object,
                                                 const char* path,
                                                 const char* key,
                                                 const Value* value) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    networklib->UpdateNetworkManagerStatus();
  }


  void NotifyCellularDataPlanChanged() {
    FOR_EACH_OBSERVER(CellularDataPlanObserver,
                      data_plan_observers_,
                      OnCellularDataPlanChanged(this));
  }


  void NotifyNetworkManagerChanged() {
    FOR_EACH_OBSERVER(NetworkManagerObserver,
                      network_manager_observers_,
                      OnNetworkManagerChanged(this));
  }


WifiNetwork::WifiNetwork(const ServiceInfo* service)
    : WirelessNetwork(service) {
  encryption_ = service->security;
  passphrase_ = SafeString(service->passphrase);
  identity_ = SafeString(service->identity);
  cert_path_ = SafeString(service->cert_path);
  type_ = TYPE_WIFI;
}


virtual bool cellular_enabled() const { return false; }


  virtual bool ethernet_available() const {
    return available_devices_ & (1 << TYPE_ETHERNET);
  }


  virtual bool ethernet_connecting() const {
    return ethernet_ ? ethernet_->connecting() : false;
  }


  AvailableSpaceQueryTask(
      QuotaManager* manager,
      scoped_refptr<base::MessageLoopProxy> db_message_loop,
      const FilePath& profile_path,
      AvailableSpaceCallback* callback)
      : QuotaThreadTask(manager, db_message_loop),
        profile_path_(profile_path),
        space_(-1),
        callback_(callback) {}


  explicit DatabaseTaskBase(QuotaManager* manager)
      : QuotaThreadTask(manager, manager->db_thread_),
        manager_(manager),
        database_(manager->database_.get()),
        db_disabled_(false) {
    DCHECK(manager_);
    DCHECK(database_);
  }


void QuotaManager::DeleteOriginFromDatabase(
    const GURL& origin, StorageType type) {
  LazyInitialize();
  if (db_disabled_)
    return;
  scoped_refptr<DeleteOriginInfo> task =
      new DeleteOriginInfo(this, origin, type);
  task->Start();
}


void QuotaManager::DidOriginDataEvicted(
    QuotaStatusCode status) {
  DCHECK(io_thread_->BelongsToCurrentThread());

  if (status != kQuotaStatusOk)
    origins_in_error_[eviction_context_.evicted_origin]++;

  eviction_context_.evict_origin_data_callback->Run(status);
  eviction_context_.evict_origin_data_callback.reset();
}


void QuotaManager::GetPersistentHostQuota(const std::string& host,
                                          HostQuotaCallback* callback_ptr) {
  scoped_ptr<HostQuotaCallback> callback(callback_ptr);
  LazyInitialize();
  if (host.empty()) {
    callback->Run(kQuotaStatusOk, host, kStorageTypePersistent, 0);
    return;
  }
  scoped_refptr<GetPersistentHostQuotaTask> task(
      new GetPersistentHostQuotaTask(this, host, callback.release()));
  task->Start();
}


bool QuotaManager::ResetUsageTracker(StorageType type) {
  switch (type) {
    case kStorageTypeTemporary:
      if (temporary_usage_tracker_->IsWorking())
        return false;
      temporary_usage_tracker_.reset(
          new UsageTracker(clients_, kStorageTypeTemporary,
                           special_storage_policy_));
      return true;
    case kStorageTypePersistent:
      if (persistent_usage_tracker_->IsWorking())
        return false;
      persistent_usage_tracker_.reset(
          new UsageTracker(clients_, kStorageTypePersistent,
                           special_storage_policy_));
      return true;
    default:
      NOTREACHED();
  }
  return true;
}


  UpdateTemporaryGlobalQuotaTask(
      QuotaManager* manager,
      int64 new_quota,
      QuotaCallback* callback)
      : DatabaseTaskBase(manager),
        new_quota_(new_quota),
        callback_(callback) {
    DCHECK_GE(new_quota, 0);
  }


CallbackList& unlimited_callbacks() { return unlimited_callbacks_; }


  void DumpQuotaTable() {
    quota_entries_.clear();
    quota_manager_->DumpQuotaTable(
        callback_factory_.NewCallback(
            &QuotaManagerTest::DidDumpQuotaTable));
  }


  base::Time IncrementMockTime() {
    ++mock_time_counter_;
    return base::Time::FromDoubleT(mock_time_counter_ * 10.0);
  }


const std::string& host() const { return host_; }


  const OriginInfoTableEntries& origin_info_entries() const {
    return origin_info_entries_;
  }


FilePath profile_path() const { return data_dir_.path(); }


const QuotaTableEntries& quota_entries() const { return quota_entries_; }


  void set_quota_manager(QuotaManager* quota_manager) {
    quota_manager_ = quota_manager;
  }


QuotaStatusCode status() const { return quota_status_; }


  GatherGlobalUsageTask(
      UsageTracker* tracker,
      QuotaClient* client)
      : GatherUsageTaskBase(tracker, client),
        client_(client),
        callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
    DCHECK(tracker);
    DCHECK(client);
  }


  GatherUsageTaskBase(
      UsageTracker* tracker,
      QuotaClient* client)
      : QuotaTask(tracker),
        client_(client),
        tracker_(tracker),
        callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
    DCHECK(tracker_);
    DCHECK(client_);
    client_tracker_ = tracker_->GetClientTracker(client_->id());
    DCHECK(client_tracker_);
  }


void ClientUsageTracker::GetHostUsage(
    const std::string& host, HostUsageCallback* callback) {
  HostSet::const_iterator found = cached_hosts_.find(host);
  if (found != cached_hosts_.end()) {
    callback->Run(host, type_, GetCachedHostUsage(host));
    delete callback;
    return;
  }
  if (!host_usage_callbacks_.Add(host, callback) || global_usage_task_)
    return;
  GatherHostUsageTask* task = new GatherHostUsageTask(tracker_, client_, host);
  host_usage_tasks_[host] = task;
  task->Start();
}


void AddDeleteUninstallShortcutsForMSIWorkItems(
    const InstallerState& installer_state,
    const Product& product,
    const FilePath& temp_path,
    WorkItemList* work_item_list) {
  DCHECK(installer_state.is_msi())
      << "This must only be called for MSI installations!";

  HKEY reg_root = installer_state.root_key();
  std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath());

  WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
      reg_root, uninstall_reg);
  delete_reg_key->set_ignore_failure(true);

  FilePath uninstall_link;
  if (installer_state.system_install()) {
    PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link);
  } else {
    PathService::Get(base::DIR_START_MENU, &uninstall_link);
  }

  if (uninstall_link.empty()) {
    LOG(ERROR) << "Failed to get location for shortcut.";
  } else {
    uninstall_link = uninstall_link.Append(
        product.distribution()->GetAppShortCutName());
    uninstall_link = uninstall_link.Append(
        product.distribution()->GetUninstallLinkName() + L".lnk");
    VLOG(1) << "Deleting old uninstall shortcut (if present): "
            << uninstall_link.value();
    WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
        uninstall_link, temp_path);
    delete_link->set_ignore_failure(true);
    delete_link->set_log_message(
        "Failed to delete old uninstall shortcut.");
  }
}


void AddQuickEnableWorkItems(const InstallerState& installer_state,
                             const InstallationState& machine_state,
                             const FilePath* setup_path,
                             const Version* new_version,
                             WorkItemList* work_item_list) {
  DCHECK(setup_path ||
         installer_state.operation() == InstallerState::UNINSTALL);
  DCHECK(new_version ||
         installer_state.operation() == InstallerState::UNINSTALL);
  DCHECK(work_item_list);

  const bool system_install = installer_state.system_install();
  bool have_multi_chrome = false;
  bool have_chrome_frame = false;

  const ProductState* product_state = NULL;

  product_state =
      machine_state.GetProductState(system_install,
                                    BrowserDistribution::CHROME_BROWSER);
  if (product_state != NULL && product_state->is_multi_install())
    have_multi_chrome = true;

  product_state =
      machine_state.GetProductState(system_install,
                                    BrowserDistribution::CHROME_FRAME);
  if (product_state != NULL &&
      !product_state->uninstall_command().HasSwitch(
          switches::kChromeFrameReadyMode))
    have_chrome_frame = true;

  const Product* product = NULL;

  if (installer_state.operation() == InstallerState::UNINSTALL) {
    product =
        installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
    if (product != NULL && installer_state.is_multi_install())
      have_multi_chrome = false;

    if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME) != NULL)
      have_chrome_frame = false;
  } else {
    product =
        installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
    if (product != NULL && installer_state.is_multi_install())
      have_multi_chrome = true;

    product = installer_state.FindProduct(BrowserDistribution::CHROME_FRAME);
    if (product != NULL && !product->HasOption(kOptionReadyMode))
      have_chrome_frame = true;
  }

  enum QuickEnableOperation {
    DO_NOTHING,
    ADD_COMMAND,
    REMOVE_COMMAND
  } operation = DO_NOTHING;
  FilePath binaries_setup_path;

  if (have_chrome_frame) {
    operation = REMOVE_COMMAND;
  } else if (have_multi_chrome) {
    operation = ADD_COMMAND;
    if (installer_state.operation() == InstallerState::UNINSTALL) {
      product_state =
          machine_state.GetProductState(system_install,
                                        BrowserDistribution::CHROME_BROWSER);
      DCHECK(product_state);
      binaries_setup_path = product_state->uninstall_command().GetProgram();
    } else {
      DCHECK(installer_state.is_multi_install());
      binaries_setup_path =
          installer_state.GetInstallerDirectory(*new_version).Append(
              setup_path->BaseName());
    }
  }

  if (operation != DO_NOTHING) {
    BrowserDistribution* binaries =
        BrowserDistribution::GetSpecificDistribution(
            BrowserDistribution::CHROME_BINARIES);
    std::wstring cmd_key(binaries->GetVersionKey());
    cmd_key.append(1, L'\\').append(google_update::kRegCommandsKey)
        .append(1, L'\\').append(kCmdQuickEnableCf);

    if (operation == ADD_COMMAND) {
      DCHECK(!binaries_setup_path.empty());
      CommandLine cmd_line(binaries_setup_path);
      cmd_line.AppendSwitch(switches::kMultiInstall);
      if (installer_state.system_install())
        cmd_line.AppendSwitch(switches::kSystemLevel);
      if (installer_state.verbose_logging())
        cmd_line.AppendSwitch(switches::kVerboseLogging);
      cmd_line.AppendSwitch(switches::kChromeFrameQuickEnable);
      AppCommand cmd(cmd_line.command_line_string(), true, true);
      cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
    } else {
      DCHECK(operation == REMOVE_COMMAND);
      work_item_list->AddDeleteRegKeyWorkItem(installer_state.root_key(),
                                              cmd_key)->set_log_message(
          "removing quick-enable-cf command");
    }
  }
}


bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
{
    return isGoodKey<WebFrameProxyMap>(frameID) && !m_frameMap.contains(frameID);
}


void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*)
{
    Vector<RefPtr<WebPageProxy> > pages;
    copyValuesToVector(m_pageMap, pages);
    for (size_t i = 0, size = pages.size(); i < size; ++i)
        pages[i]->processDidBecomeResponsive();
}


void WebProcessProxy::didPerformClientRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
{
    WebPageProxy* page = webPage(pageID);
    if (!page)
        return;

    if (sourceURLString.isEmpty() || destinationURLString.isEmpty())
        return;

    WebFrameProxy* frame = webFrame(frameID);
    MESSAGE_CHECK(frame);
    MESSAGE_CHECK(frame->page() == page);
    MESSAGE_CHECK_URL(sourceURLString);
    MESSAGE_CHECK_URL(destinationURLString);

    m_context->historyClient().didPerformClientRedirect(m_context.get(), page, sourceURLString, destinationURLString, frame);
}


void WebProcessProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::MessageDecoder& decoder, OwnPtr<CoreIPC::MessageEncoder>& replyEncoder)
{
    if (m_messageReceiverMap.dispatchSyncMessage(connection, messageID, decoder, replyEncoder))
        return;

    if (m_context->dispatchSyncMessage(connection, messageID, decoder, replyEncoder))
        return;

    if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
        didReceiveSyncWebProcessProxyMessage(connection, messageID, decoder, replyEncoder);
        return;
    }

    uint64_t pageID = decoder.destinationID();
    if (!pageID)
        return;

    WebPageProxy* pageProxy = webPage(pageID);
    if (!pageProxy)
        return;

    pageProxy->didReceiveSyncMessage(connection, messageID, decoder, replyEncoder);
}


void WebProcessProxy::disconnect()
{
    clearConnection();

    if (m_webConnection) {
        m_webConnection->invalidate();
        m_webConnection = nullptr;
    }

    m_responsivenessTimer.stop();

    Vector<RefPtr<WebFrameProxy> > frames;
    copyValuesToVector(m_frameMap, frames);

    for (size_t i = 0, size = frames.size(); i < size; ++i)
        frames[i]->disconnect();
    m_frameMap.clear();

    if (m_downloadProxyMap)
        m_downloadProxyMap->processDidClose();

    m_context->disconnectProcess(this);
}


Vector<WebPageProxy*> WebProcessProxy::pages() const
{
    Vector<WebPageProxy*> result;
    copyValuesToVector(m_pageMap, result);
    return result;
}


WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const
{
    return m_backForwardListItemMap.get(itemID).get();
}


WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
{
    return isGoodKey<WebFrameProxyMap>(frameID) ? m_frameMap.get(frameID).get() : 0;
}