<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension_toolbar_model.h"

#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"

ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service)
    : service_(service),
      prefs_(service->profile()->GetPrefs()),
      extensions_initialized_(false) {
  DCHECK(service_);

  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this, NotificationType::EXTENSIONS_READY,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this,
                 NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
                 NotificationService::AllSources());

  visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize);
}

ExtensionToolbarModel::~ExtensionToolbarModel() {
}

void ExtensionToolbarModel::DestroyingProfile() {
  registrar_.RemoveAll();
}

void ExtensionToolbarModel::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
                                              int index) {
  ExtensionList::iterator pos = std::find(begin(), end(), extension);
  if (pos == end()) {
    NOTREACHED();
    return;
  }
  toolitems_.erase(pos);

  int i = 0;
  bool inserted = false;
  for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) {
    if (i == index) {
      toolitems_.insert(iter, make_scoped_refptr(extension));
      inserted = true;
      break;
    }
  }

  if (!inserted) {
    DCHECK_EQ(index, static_cast<int>(toolitems_.size()));
    index = toolitems_.size();

    toolitems_.push_back(make_scoped_refptr(extension));
  }

  FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));

  UpdatePrefs();
}

void ExtensionToolbarModel::SetVisibleIconCount(int count) {
  visible_icon_count_ = count == static_cast<int>(size()) ? -1 : count;
  prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_);
  prefs_->ScheduleSavePersistentPrefs();
}

void ExtensionToolbarModel::Observe(NotificationType type,
                                    const NotificationSource& source,
                                    const NotificationDetails& details) {
  if (type == NotificationType::EXTENSIONS_READY) {
    InitializeExtensionList();
    return;
  }

  if (!service_->is_ready())
    return;

  const Extension* extension = Details<const Extension>(details).ptr();
  if (type == NotificationType::EXTENSION_LOADED) {
    // We don't want to add the same extension twice. It may have already been
    // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
    // hides the browser action and then disables and enables the extension.
    for (size_t i = 0; i < toolitems_.size(); i++) {
      if (toolitems_[i].get() == extension)
        return;  // Already exists.
    }
    AddExtension(extension);
  } else if (type == NotificationType::EXTENSION_UNLOADED ||
             type == NotificationType::EXTENSION_UNLOADED_DISABLED) {
    RemoveExtension(extension);
  } else if (type ==
             NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
    if (service_->GetBrowserActionVisibility(extension))
      AddExtension(extension);
    else
      RemoveExtension(extension);
  } else {
    NOTREACHED() << "Received unexpected notification";
  }
}

void ExtensionToolbarModel::AddExtension(const Extension* extension) {
  // We only care about extensions with browser actions.
  if (!extension->browser_action())
    return;

  if (extension->id() == last_extension_removed_ &&
      last_extension_removed_index_ < toolitems_.size()) {
    toolitems_.insert(begin() + last_extension_removed_index_,
                      make_scoped_refptr(extension));
    FOR_EACH_OBSERVER(Observer, observers_,
        BrowserActionAdded(extension, last_extension_removed_index_));
  } else {
    toolitems_.push_back(make_scoped_refptr(extension));
    FOR_EACH_OBSERVER(Observer, observers_,
                      BrowserActionAdded(extension, toolitems_.size() - 1));
  }

  last_extension_removed_ = "";
  last_extension_removed_index_ = -1;

  UpdatePrefs();
}

void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
  ExtensionList::iterator pos = std::find(begin(), end(), extension);
  if (pos == end()) {
    return;
  }

  last_extension_removed_ = extension->id();
  last_extension_removed_index_ = pos - begin();

  toolitems_.erase(pos);
  FOR_EACH_OBSERVER(Observer, observers_,
                    BrowserActionRemoved(extension));

  UpdatePrefs();
}

// Combine the currently enabled extensions that have browser actions (which
// we get from the ExtensionService) with the ordering we get from the
// pref service. For robustness we use a somewhat inefficient process:
// 1. Create a vector of extensions sorted by their pref values. This vector may
// have holes.
// 2. Create a vector of extensions that did not have a pref value.
// 3. Remove holes from the sorted vector and append the unsorted vector.
void ExtensionToolbarModel::InitializeExtensionList() {
  DCHECK(service_->is_ready());

  std::vector<std::string> pref_order = service_->extension_prefs()->
      GetToolbarOrder();
  // Items that have a pref for their position.
  ExtensionList sorted;
  sorted.resize(pref_order.size(), NULL);
  // The items that don't have a pref for their position.
  ExtensionList unsorted;

  // Create the lists.
  for (size_t i = 0; i < service_->extensions()->size(); ++i) {
    const Extension* extension = service_->extensions()->at(i);
    if (!extension->browser_action())
      continue;
    if (!service_->GetBrowserActionVisibility(extension))
      continue;

    std::vector<std::string>::iterator pos =
        std::find(pref_order.begin(), pref_order.end(), extension->id());
    if (pos != pref_order.end()) {
      int index = std::distance(pref_order.begin(), pos);
      sorted[index] = extension;
    } else {
      unsorted.push_back(make_scoped_refptr(extension));
    }
  }

  // Merge the lists.
  toolitems_.reserve(sorted.size() + unsorted.size());
  for (ExtensionList::iterator iter = sorted.begin();
       iter != sorted.end(); ++iter) {
    if (*iter != NULL)
      toolitems_.push_back(*iter);
  }
  toolitems_.insert(toolitems_.end(), unsorted.begin(), unsorted.end());

  // Inform observers.
  for (size_t i = 0; i < toolitems_.size(); i++) {
    FOR_EACH_OBSERVER(Observer, observers_,
                      BrowserActionAdded(toolitems_[i], i));
  }

  UpdatePrefs();

  extensions_initialized_ = true;
  FOR_EACH_OBSERVER(Observer, observers_, ModelLoaded());
}

void ExtensionToolbarModel::UpdatePrefs() {
  if (!service_->extension_prefs())
    return;

  std::vector<std::string> ids;
  ids.reserve(toolitems_.size());
  for (ExtensionList::iterator iter = begin(); iter != end(); ++iter)
    ids.push_back((*iter)->id());
  service_->extension_prefs()->SetToolbarOrder(ids);
}

const Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const {
  return toolitems_[index];
}

int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
  int original_index = 0, i = 0;
  for (ExtensionList::iterator iter = begin(); iter != end();
       ++iter, ++original_index) {
    if (service_->IsIncognitoEnabled(*iter)) {
      if (incognito_index == i)
        break;
      ++i;
    }
  }
  return original_index;
}

int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
  int incognito_index = 0, i = 0;
  for (ExtensionList::iterator iter = begin(); iter != end();
       ++iter, ++i) {
    if (original_index == i)
      break;
    if (service_->IsIncognitoEnabled(*iter))
      ++incognito_index;
  }
  return incognito_index;
}
<commit_msg>Fix a crash when enabling hidden browser actions in incognito and then selecting Show button from the extension management page.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension_toolbar_model.h"

#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"

ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service)
    : service_(service),
      prefs_(service->profile()->GetPrefs()),
      extensions_initialized_(false) {
  DCHECK(service_);

  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this, NotificationType::EXTENSIONS_READY,
                 Source<Profile>(service_->profile()));
  registrar_.Add(this,
                 NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
                 NotificationService::AllSources());

  visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize);
}

ExtensionToolbarModel::~ExtensionToolbarModel() {
}

void ExtensionToolbarModel::DestroyingProfile() {
  registrar_.RemoveAll();
}

void ExtensionToolbarModel::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
                                              int index) {
  ExtensionList::iterator pos = std::find(begin(), end(), extension);
  if (pos == end()) {
    NOTREACHED();
    return;
  }
  toolitems_.erase(pos);

  int i = 0;
  bool inserted = false;
  for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) {
    if (i == index) {
      toolitems_.insert(iter, make_scoped_refptr(extension));
      inserted = true;
      break;
    }
  }

  if (!inserted) {
    DCHECK_EQ(index, static_cast<int>(toolitems_.size()));
    index = toolitems_.size();

    toolitems_.push_back(make_scoped_refptr(extension));
  }

  FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));

  UpdatePrefs();
}

void ExtensionToolbarModel::SetVisibleIconCount(int count) {
  visible_icon_count_ = count == static_cast<int>(size()) ? -1 : count;
  prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_);
  prefs_->ScheduleSavePersistentPrefs();
}

void ExtensionToolbarModel::Observe(NotificationType type,
                                    const NotificationSource& source,
                                    const NotificationDetails& details) {
  if (type == NotificationType::EXTENSIONS_READY) {
    InitializeExtensionList();
    return;
  }

  if (!service_->is_ready())
    return;

  const Extension* extension = Details<const Extension>(details).ptr();
  if (type == NotificationType::EXTENSION_LOADED) {
    // We don't want to add the same extension twice. It may have already been
    // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
    // hides the browser action and then disables and enables the extension.
    for (size_t i = 0; i < toolitems_.size(); i++) {
      if (toolitems_[i].get() == extension)
        return;  // Already exists.
    }
    if (service_->GetBrowserActionVisibility(extension))
      AddExtension(extension);
  } else if (type == NotificationType::EXTENSION_UNLOADED ||
             type == NotificationType::EXTENSION_UNLOADED_DISABLED) {
    RemoveExtension(extension);
  } else if (type ==
             NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
    if (service_->GetBrowserActionVisibility(extension))
      AddExtension(extension);
    else
      RemoveExtension(extension);
  } else {
    NOTREACHED() << "Received unexpected notification";
  }
}

void ExtensionToolbarModel::AddExtension(const Extension* extension) {
  // We only care about extensions with browser actions.
  if (!extension->browser_action())
    return;

  if (extension->id() == last_extension_removed_ &&
      last_extension_removed_index_ < toolitems_.size()) {
    toolitems_.insert(begin() + last_extension_removed_index_,
                      make_scoped_refptr(extension));
    FOR_EACH_OBSERVER(Observer, observers_,
        BrowserActionAdded(extension, last_extension_removed_index_));
  } else {
    toolitems_.push_back(make_scoped_refptr(extension));
    FOR_EACH_OBSERVER(Observer, observers_,
                      BrowserActionAdded(extension, toolitems_.size() - 1));
  }

  last_extension_removed_ = "";
  last_extension_removed_index_ = -1;

  UpdatePrefs();
}

void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
  ExtensionList::iterator pos = std::find(begin(), end(), extension);
  if (pos == end()) {
    return;
  }

  last_extension_removed_ = extension->id();
  last_extension_removed_index_ = pos - begin();

  toolitems_.erase(pos);
  FOR_EACH_OBSERVER(Observer, observers_,
                    BrowserActionRemoved(extension));

  UpdatePrefs();
}

// Combine the currently enabled extensions that have browser actions (which
// we get from the ExtensionService) with the ordering we get from the
// pref service. For robustness we use a somewhat inefficient process:
// 1. Create a vector of extensions sorted by their pref values. This vector may
// have holes.
// 2. Create a vector of extensions that did not have a pref value.
// 3. Remove holes from the sorted vector and append the unsorted vector.
void ExtensionToolbarModel::InitializeExtensionList() {
  DCHECK(service_->is_ready());

  std::vector<std::string> pref_order = service_->extension_prefs()->
      GetToolbarOrder();
  // Items that have a pref for their position.
  ExtensionList sorted;
  sorted.resize(pref_order.size(), NULL);
  // The items that don't have a pref for their position.
  ExtensionList unsorted;

  // Create the lists.
  for (size_t i = 0; i < service_->extensions()->size(); ++i) {
    const Extension* extension = service_->extensions()->at(i);
    if (!extension->browser_action())
      continue;
    if (!service_->GetBrowserActionVisibility(extension))
      continue;

    std::vector<std::string>::iterator pos =
        std::find(pref_order.begin(), pref_order.end(), extension->id());
    if (pos != pref_order.end()) {
      int index = std::distance(pref_order.begin(), pos);
      sorted[index] = extension;
    } else {
      unsorted.push_back(make_scoped_refptr(extension));
    }
  }

  // Merge the lists.
  toolitems_.reserve(sorted.size() + unsorted.size());
  for (ExtensionList::iterator iter = sorted.begin();
       iter != sorted.end(); ++iter) {
    if (*iter != NULL)
      toolitems_.push_back(*iter);
  }
  toolitems_.insert(toolitems_.end(), unsorted.begin(), unsorted.end());

  // Inform observers.
  for (size_t i = 0; i < toolitems_.size(); i++) {
    FOR_EACH_OBSERVER(Observer, observers_,
                      BrowserActionAdded(toolitems_[i], i));
  }

  UpdatePrefs();

  extensions_initialized_ = true;
  FOR_EACH_OBSERVER(Observer, observers_, ModelLoaded());
}

void ExtensionToolbarModel::UpdatePrefs() {
  if (!service_->extension_prefs())
    return;

  std::vector<std::string> ids;
  ids.reserve(toolitems_.size());
  for (ExtensionList::iterator iter = begin(); iter != end(); ++iter)
    ids.push_back((*iter)->id());
  service_->extension_prefs()->SetToolbarOrder(ids);
}

const Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const {
  return toolitems_[index];
}

int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
  int original_index = 0, i = 0;
  for (ExtensionList::iterator iter = begin(); iter != end();
       ++iter, ++original_index) {
    if (service_->IsIncognitoEnabled(*iter)) {
      if (incognito_index == i)
        break;
      ++i;
    }
  }
  return original_index;
}

int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
  int incognito_index = 0, i = 0;
  for (ExtensionList::iterator iter = begin(); iter != end();
       ++iter, ++i) {
    if (original_index == i)
      break;
    if (service_->IsIncognitoEnabled(*iter))
      ++incognito_index;
  }
  return incognito_index;
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/extensions/extension_error_reporter.h"
#include "chrome/browser/extensions/extension_view.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"

namespace {

// How long to wait for the extension to put up a javascript alert before giving
// up.
const int kAlertTimeoutMs = 10000;

// How long to wait for the extension to load before giving up.
const int kLoadTimeoutMs = 5000;

// The extension we're using as our test case.
const char* kExtensionId = "00123456789abcdef0123456789abcdef0123456";

// This class starts up an extension process and waits until it tries to put
// up a javascript alert.
class MockExtensionView : public ExtensionView {
 public:
  MockExtensionView(const GURL& url, Profile* profile)
      : ExtensionView(url, profile), got_message_(false) {
    InitHidden();
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
        new MessageLoop::QuitTask, kAlertTimeoutMs);
    ui_test_utils::RunMessageLoop();
  }

  bool got_message() { return got_message_; }
 private:
  virtual void RunJavaScriptMessage(
      const std::wstring& message,
      const std::wstring& default_prompt,
      const GURL& frame_url,
      const int flags,
      IPC::Message* reply_msg,
      bool* did_suppress_message) {
    got_message_ = true;
    MessageLoopForUI::current()->Quit();

    // Call super, otherwise we'll leak reply_msg.
    ExtensionView::RunJavaScriptMessage(
        message, default_prompt, frame_url, flags,
        reply_msg, did_suppress_message);
  }

  bool got_message_;
};

// This class waits for a specific extension to be loaded.
class ExtensionLoadedObserver : public NotificationObserver {
 public:
  explicit ExtensionLoadedObserver() : extension_(NULL)  {
    registrar_.Add(this, NotificationType::EXTENSIONS_LOADED,
        NotificationService::AllSources());
  }

  Extension* WaitForExtension() {
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
        new MessageLoop::QuitTask, kLoadTimeoutMs);
    ui_test_utils::RunMessageLoop();
    return extension_;
  }

 private:
  virtual void Observe(NotificationType type, const NotificationSource& source,
                       const NotificationDetails& details) {
    if (type == NotificationType::EXTENSIONS_LOADED) {
      ExtensionList* extensions = Details<ExtensionList>(details).ptr();
      for (size_t i = 0; i < (*extensions).size(); i++) {
        if ((*extensions)[i]->id() == kExtensionId) {
          extension_ = (*extensions)[i];
          MessageLoopForUI::current()->Quit();
          break;
        }
      }
    } else {
      NOTREACHED();
    }
  }

  NotificationRegistrar registrar_;
  Extension* extension_;
};

}  // namespace

class ExtensionViewTest : public InProcessBrowserTest {
 public:
  virtual void SetUp() {
    // Initialize the error reporter here, otherwise BrowserMain will create it
    // with the wrong MessageLoop.
    ExtensionErrorReporter::Init(false);

    InProcessBrowserTest::SetUp();
  }
};

// Tests that ExtensionView starts an extension process and runs the script
// contained in the extension's "index.html" file.
IN_PROC_BROWSER_TEST_F(ExtensionViewTest, Index) {
  // Create an observer first to be sure we have the notification registered
  // before it's sent.
  ExtensionLoadedObserver observer;

  // Get the path to our extension.
  FilePath path;
  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
  path = path.AppendASCII("extensions").
      AppendASCII("good").AppendASCII("extension1").AppendASCII("1");
  ASSERT_TRUE(file_util::DirectoryExists(path));  // sanity check

  // Load it.
  Profile* profile = browser()->profile();
  profile->GetExtensionsService()->Init();
  profile->GetExtensionsService()->LoadExtension(path);

  // Now wait for it to load, and grab a pointer to it.
  Extension* extension = observer.WaitForExtension();
  ASSERT_TRUE(extension);
  GURL url = Extension::GetResourceURL(extension->url(), "toolstrip.html");

  // Start the extension process and wait for it to show a javascript alert.
  MockExtensionView view(url, profile);
  EXPECT_TRUE(view.got_message());
}
<commit_msg>Disable ExtensionViewTest.Index while I fix it.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/extensions/extension_error_reporter.h"
#include "chrome/browser/extensions/extension_view.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"

namespace {

// How long to wait for the extension to put up a javascript alert before giving
// up.
const int kAlertTimeoutMs = 10000;

// How long to wait for the extension to load before giving up.
const int kLoadTimeoutMs = 5000;

// The extension we're using as our test case.
const char* kExtensionId = "00123456789abcdef0123456789abcdef0123456";

// This class starts up an extension process and waits until it tries to put
// up a javascript alert.
class MockExtensionView : public ExtensionView {
 public:
  MockExtensionView(const GURL& url, Profile* profile)
      : ExtensionView(url, profile), got_message_(false) {
    InitHidden();
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
        new MessageLoop::QuitTask, kAlertTimeoutMs);
    ui_test_utils::RunMessageLoop();
  }

  bool got_message() { return got_message_; }
 private:
  virtual void RunJavaScriptMessage(
      const std::wstring& message,
      const std::wstring& default_prompt,
      const GURL& frame_url,
      const int flags,
      IPC::Message* reply_msg,
      bool* did_suppress_message) {
    got_message_ = true;
    MessageLoopForUI::current()->Quit();

    // Call super, otherwise we'll leak reply_msg.
    ExtensionView::RunJavaScriptMessage(
        message, default_prompt, frame_url, flags,
        reply_msg, did_suppress_message);
  }

  bool got_message_;
};

// This class waits for a specific extension to be loaded.
class ExtensionLoadedObserver : public NotificationObserver {
 public:
  explicit ExtensionLoadedObserver() : extension_(NULL)  {
    registrar_.Add(this, NotificationType::EXTENSIONS_LOADED,
        NotificationService::AllSources());
  }

  Extension* WaitForExtension() {
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
        new MessageLoop::QuitTask, kLoadTimeoutMs);
    ui_test_utils::RunMessageLoop();
    return extension_;
  }

 private:
  virtual void Observe(NotificationType type, const NotificationSource& source,
                       const NotificationDetails& details) {
    if (type == NotificationType::EXTENSIONS_LOADED) {
      ExtensionList* extensions = Details<ExtensionList>(details).ptr();
      for (size_t i = 0; i < (*extensions).size(); i++) {
        if ((*extensions)[i]->id() == kExtensionId) {
          extension_ = (*extensions)[i];
          MessageLoopForUI::current()->Quit();
          break;
        }
      }
    } else {
      NOTREACHED();
    }
  }

  NotificationRegistrar registrar_;
  Extension* extension_;
};

}  // namespace

class ExtensionViewTest : public InProcessBrowserTest {
 public:
  virtual void SetUp() {
    // Initialize the error reporter here, otherwise BrowserMain will create it
    // with the wrong MessageLoop.
    ExtensionErrorReporter::Init(false);

    InProcessBrowserTest::SetUp();
  }
};

// Tests that ExtensionView starts an extension process and runs the script
// contained in the extension's "index.html" file.
IN_PROC_BROWSER_TEST_F(ExtensionViewTest, DISABLED_Index) {
  // Create an observer first to be sure we have the notification registered
  // before it's sent.
  ExtensionLoadedObserver observer;

  // Get the path to our extension.
  FilePath path;
  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
  path = path.AppendASCII("extensions").
      AppendASCII("good").AppendASCII("extension1").AppendASCII("1");
  ASSERT_TRUE(file_util::DirectoryExists(path));  // sanity check

  // Load it.
  Profile* profile = browser()->profile();
  profile->GetExtensionsService()->Init();
  profile->GetExtensionsService()->LoadExtension(path);

  // Now wait for it to load, and grab a pointer to it.
  Extension* extension = observer.WaitForExtension();
  ASSERT_TRUE(extension);
  GURL url = Extension::GetResourceURL(extension->url(), "toolstrip.html");

  // Start the extension process and wait for it to show a javascript alert.
  MockExtensionView view(url, profile);
  EXPECT_TRUE(view.got_message());
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file implements a mock location provider and the factory functions for
// various ways of creating it.

#include "chrome/browser/geolocation/mock_location_provider.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/task.h"

MockLocationProvider* MockLocationProvider::instance_ = NULL;

MockLocationProvider::MockLocationProvider(MockLocationProvider** self_ref)
    : started_count_(0),
      self_ref_(self_ref) {
  CHECK(self_ref_);
  CHECK(*self_ref_ == NULL);
  *self_ref_ = this;
}

MockLocationProvider::~MockLocationProvider() {
  CHECK(*self_ref_ == this);
  *self_ref_ = NULL;
}

bool MockLocationProvider::StartProvider() {
  ++started_count_;
  return true;
}

void MockLocationProvider::GetPosition(Geoposition* position) {
  *position = position_;
}

void MockLocationProvider::OnPermissionGranted(const GURL& requesting_frame) {
  permission_granted_url_ = requesting_frame;
}

// Mock location provider that automatically calls back it's client when
// StartProvider is called.
class AutoMockLocationProvider : public MockLocationProvider {
 public:
  AutoMockLocationProvider(bool has_valid_location,
                           bool requires_permission_to_start)
      : MockLocationProvider(&instance_),
        ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
        requires_permission_to_start_(requires_permission_to_start) {
    if (has_valid_location) {
      position_.accuracy = 3;
      position_.latitude = 4.3;
      position_.longitude = -7.8;
      position_.timestamp = base::Time::FromDoubleT(4567.8);
    } else {
      position_.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
    }
  }
  virtual bool StartProvider() {
    MockLocationProvider::StartProvider();
    if (!requires_permission_to_start_) {
      MessageLoop::current()->PostTask(
          FROM_HERE, task_factory_.NewRunnableMethod(
              &MockLocationProvider::UpdateListeners));
    }
    return true;
  }

  void OnPermissionGranted(const GURL& requesting_frame) {
    MockLocationProvider::OnPermissionGranted(requesting_frame);
    if (requires_permission_to_start_) {
      MessageLoop::current()->PostTask(
          FROM_HERE, task_factory_.NewRunnableMethod(
              &MockLocationProvider::UpdateListeners));
    }
  }

  ScopedRunnableMethodFactory<MockLocationProvider> task_factory_;
  const bool requires_permission_to_start_;
};

LocationProviderBase* NewMockLocationProvider() {
  return new MockLocationProvider(&MockLocationProvider::instance_);
}

LocationProviderBase* NewAutoSuccessMockLocationProvider() {
  return new AutoMockLocationProvider(true, false);
}

LocationProviderBase* NewAutoFailMockLocationProvider() {
  return new AutoMockLocationProvider(false, false);
}

LocationProviderBase* NewAutoSuccessMockNetworkLocationProvider() {
  return new AutoMockLocationProvider(true, true);
}
<commit_msg>Removes spurious JS callback triggered by MockLocationProvider::OnPermissionGranted. (this generated an extra alert, which is closed when test finishes on gtk/win, but hangs on mac).<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file implements a mock location provider and the factory functions for
// various ways of creating it.

#include "chrome/browser/geolocation/mock_location_provider.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/task.h"

MockLocationProvider* MockLocationProvider::instance_ = NULL;

MockLocationProvider::MockLocationProvider(MockLocationProvider** self_ref)
    : started_count_(0),
      self_ref_(self_ref) {
  CHECK(self_ref_);
  CHECK(*self_ref_ == NULL);
  *self_ref_ = this;
}

MockLocationProvider::~MockLocationProvider() {
  CHECK(*self_ref_ == this);
  *self_ref_ = NULL;
}

bool MockLocationProvider::StartProvider() {
  ++started_count_;
  return true;
}

void MockLocationProvider::GetPosition(Geoposition* position) {
  *position = position_;
}

void MockLocationProvider::OnPermissionGranted(const GURL& requesting_frame) {
  permission_granted_url_ = requesting_frame;
}

// Mock location provider that automatically calls back it's client at most
// once, when StartProvider or OnPermissionGranted is called. Use
// |requires_permission_to_start| to select which event triggers the callback.
class AutoMockLocationProvider : public MockLocationProvider {
 public:
  AutoMockLocationProvider(bool has_valid_location,
                           bool requires_permission_to_start)
      : MockLocationProvider(&instance_),
        ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
        requires_permission_to_start_(requires_permission_to_start),
        listeners_updated_(false) {
    if (has_valid_location) {
      position_.accuracy = 3;
      position_.latitude = 4.3;
      position_.longitude = -7.8;
      position_.timestamp = base::Time::FromDoubleT(4567.8);
    } else {
      position_.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
    }
  }
  virtual bool StartProvider() {
    MockLocationProvider::StartProvider();
    if (!requires_permission_to_start_) {
      UpdateListenersIfNeeded();
    }
    return true;
  }

  void OnPermissionGranted(const GURL& requesting_frame) {
    MockLocationProvider::OnPermissionGranted(requesting_frame);
    if (requires_permission_to_start_) {
      UpdateListenersIfNeeded();
    }
  }

  void UpdateListenersIfNeeded() {
    if (!listeners_updated_) {
      listeners_updated_ = true;
      MessageLoop::current()->PostTask(
          FROM_HERE, task_factory_.NewRunnableMethod(
              &MockLocationProvider::UpdateListeners));
    }
  }

  ScopedRunnableMethodFactory<MockLocationProvider> task_factory_;
  const bool requires_permission_to_start_;
  bool listeners_updated_;
};

LocationProviderBase* NewMockLocationProvider() {
  return new MockLocationProvider(&MockLocationProvider::instance_);
}

LocationProviderBase* NewAutoSuccessMockLocationProvider() {
  return new AutoMockLocationProvider(true, false);
}

LocationProviderBase* NewAutoFailMockLocationProvider() {
  return new AutoMockLocationProvider(false, false);
}

LocationProviderBase* NewAutoSuccessMockNetworkLocationProvider() {
  return new AutoMockLocationProvider(true, true);
}
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve CurrentSenseValveMotorDirect tests.
 */

#include <gtest/gtest.h>
#include <cstdint>
#include <cstdio>

#include <OTRadValve.h>

// Test calibration calculations in CurrentSenseValveMotorDirect for REV7/DORM1/TRV1 board.
// Also check some of the use of those calculations.
// In particular test the logic in ModelledRadValveState for starting from extreme positions.
//
// Adapted 2016/10/18 from test_VALVEMODEL.ino testCSVMDC().
TEST(CurrentSenseValveMotorDirect,REV7CSVMDC)
{
    // Compute minimum dead-reckoning ticks as for V0p2/REV7 20161018.
    const uint8_t subcycleTicksRoundedDown_ms = 7;
    const uint8_t minTicks = OTRadValve::CurrentSenseValveMotorDirect::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms);
    EXPECT_EQ(35, minTicks);
    // Compute maximum sub-cycle time to start valve movement as for V0p2/REV7 20161018.
    const uint8_t gst_max = 255;
    const uint8_t minimumMotorRunupTicks = 4; // OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks as at 20161018.
    const uint8_t sctAbsLimit = OTRadValve::CurrentSenseValveMotorDirect::computeSctAbsLimit(subcycleTicksRoundedDown_ms, gst_max, minimumMotorRunupTicks);
    EXPECT_EQ(230, sctAbsLimit);

    // Create calibration parameters with values that happen to work for V0p2/REV7.
    OTRadValve::CurrentSenseValveMotorDirect::CalibrationParameters cp;
    // Test the calculations with one plausible calibration data set.
    EXPECT_TRUE(cp.updateAndCompute(1601U, 1105U, minTicks)); // Must not fail...
    EXPECT_EQ(4, cp.getApproxPrecisionPC());
    EXPECT_EQ(25, cp.getTfotcSmall());
    EXPECT_EQ(17, cp.getTfctoSmall());
    // Check that a calibration instance can be reused correctly.
    const uint16_t tfo2 = 1803U;
    const uint16_t tfc2 = 1373U;
    EXPECT_TRUE(cp.updateAndCompute(tfo2, tfc2, minTicks)); // Must not fail...
    EXPECT_EQ(3, cp.getApproxPrecisionPC());
    EXPECT_EQ(28, cp.getTfotcSmall());
    EXPECT_EQ(21, cp.getTfctoSmall());

    // Check that computing position works...
    // Simple case: fully closed, no accumulated reverse ticks.
    volatile uint16_t ticksFromOpen, ticksReverse;
    ticksFromOpen = tfo2;
    ticksReverse = 0;
    EXPECT_EQ(0, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2, ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
    // Simple case: fully open, no accumulated reverse ticks.
    ticksFromOpen = 0;
    ticksReverse = 0;
    EXPECT_EQ(100, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(0, ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
    // Try at half-way mark, no reverse ticks.
    ticksFromOpen = tfo2 / 2;
    ticksReverse = 0;
    EXPECT_EQ(50, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2/2, ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
    // Try at half-way mark with just one reverse tick (nothing should change).
    ticksFromOpen = tfo2 / 2;
    ticksReverse = 1;
    EXPECT_EQ(50, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2/2, ticksFromOpen);
    EXPECT_EQ(1, ticksReverse);
    // Try at half-way mark with a big-enough block of reverse ticks to be significant.
    ticksFromOpen = tfo2 / 2;
    ticksReverse = cp.getTfctoSmall();
    EXPECT_EQ(51, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2/2 - cp.getTfotcSmall(), ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
  // DHD20151025: one set of actual measurements during calibration.
  //    ticksFromOpenToClosed: 1529
  //    ticksFromClosedToOpen: 1295
}

class DummyHardwareDriver : public OTRadValve::HardwareMotorDriverInterface
  {
  public:
    // Detect if end-stop is reached or motor current otherwise very high.
    bool currentHigh = false;
    virtual bool isCurrentHigh(OTRadValve::HardwareMotorDriverInterface::motor_drive mdir = motorDriveOpening) const override { return(currentHigh); }
    virtual void motorRun(uint8_t maxRunTicks, motor_drive dir, OTRadValve::HardwareMotorDriverInterfaceCallbackHandler &callback) override
      { }
  };

// Always claims to be at the start of a major cycle.
static uint8_t dummyGetSubCycleTime() { return(0); }

// Test that direct abstract motor drive logic is constructable and minimally sane..
//
// Adapted 2016/10/18 from test_VALVEMODEL.ino testCurrentSenseValveMotorDirect().
static void basics(OTRadValve::CurrentSenseValveMotorDirectBase *const csbp)
    {
    // POWER UP
    // Whitebox test of internal state: should be init.
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::init, csbp->getState());
    // Verify NOT marked as in normal run state immediately upon initialisation.
    EXPECT_TRUE(!csbp->isInNormalRunState());
    // Verify NOT marked as in error state immediately upon initialisation.
    EXPECT_TRUE(!csbp->isInErrorState());
    // Target % open must start off in a sensible state.
    EXPECT_GE(100, csbp->getTargetPC());
    // Current % open must start off in a sensible state.
    EXPECT_GE(100, csbp->getCurrentPC());
    }
TEST(CurrentSenseValveMotorDirect,basics)
{
    const uint8_t subcycleTicksRoundedDown_ms = 7; // For REV7: OTV0P2BASE::SUBCYCLE_TICK_MS_RD.
    const uint8_t gsct_max = 255; // For REV7: OTV0P2BASE::GSCT_MAX.
    const uint8_t minimumMotorRunupTicks = 4; // For REV7: OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks.

    DummyHardwareDriver dhw0;
    OTRadValve::CurrentSenseValveMotorDirectBinaryOnly csvmdbo1(&dhw0, dummyGetSubCycleTime,
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                     gsct_max,
                                                                     minimumMotorRunupTicks));
    basics(&csvmdbo1);

    DummyHardwareDriver dhw1;
    OTRadValve::CurrentSenseValveMotorDirect csvmd1(&dhw1, dummyGetSubCycleTime,
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                     gsct_max,
                                                                     minimumMotorRunupTicks));
    basics(&csvmd1);
    // Until calibration has been successfully run, this should be in non-proportional mode.
    EXPECT_TRUE(csvmd1.inNonProprtionalMode());
    // Nothing passed in requires deferral of (re)calibration.
    EXPECT_FALSE(csvmd1.shouldDeferCalibration());
}

class SVL final : public OTV0P2BASE::SupplyVoltageLow
    {
    public:
      SVL() { setAllLowFlags(false); }
      void setAllLowFlags(const bool f) { isLow = f; isVeryLow = f; }
      virtual uint16_t get() const override { return(0); }
      virtual uint16_t read() override { return(0); }
    };
// State of ambient lighting.
static bool _isDark;
static bool isDark() { return(_isDark); }

// Test logic for potentially deferring (re)calibration.
TEST(CurrentSenseValveMotorDirect,calibrationDeferral)
{
    const uint8_t subcycleTicksRoundedDown_ms = 7; // For REV7: OTV0P2BASE::SUBCYCLE_TICK_MS_RD.
    const uint8_t gsct_max = 255; // For REV7: OTV0P2BASE::GSCT_MAX.
    const uint8_t minimumMotorRunupTicks = 4; // For REV7: OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks.
    DummyHardwareDriver dhw;
    SVL svl;
    svl.setAllLowFlags(false);
    _isDark = false;
    OTRadValve::CurrentSenseValveMotorDirect csvmd1(&dhw, dummyGetSubCycleTime,
        OTRadValve::CurrentSenseValveMotorDirect::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
        OTRadValve::CurrentSenseValveMotorDirect::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                     gsct_max,
                                                                     minimumMotorRunupTicks),
        &svl,
        isDark);
    // Nothing yet requires deferral of (re)calibration.
    ASSERT_FALSE(csvmd1.shouldDeferCalibration());
    svl.setAllLowFlags(true);
    // Low supply voltage requires deferral of (re)calibration.
    ASSERT_TRUE(csvmd1.shouldDeferCalibration());
    svl.setAllLowFlags(false);
    ASSERT_FALSE(csvmd1.shouldDeferCalibration());
    _isDark = true;
    // Low light level requires deferral of (re)calibration.
    ASSERT_TRUE(csvmd1.shouldDeferCalibration());
    svl.setAllLowFlags(true);
    ASSERT_TRUE(csvmd1.shouldDeferCalibration());
    _isDark = false;
    svl.setAllLowFlags(false);
    // Nothing requires deferral of (re)calibration.
    ASSERT_FALSE(csvmd1.shouldDeferCalibration());
}

// This hits the end stops (current is high) immediately that the motor is driven.
class DummyHardwareDriverHitEndstop : public OTRadValve::HardwareMotorDriverInterface
  {
    // Detect if end-stop is reached or motor current otherwise very high.
    bool currentHigh = false;
  public:
    virtual bool isCurrentHigh(OTRadValve::HardwareMotorDriverInterface::motor_drive mdir = motorDriveOpening) const override { return(currentHigh); }
    virtual void motorRun(uint8_t maxRunTicks, motor_drive dir, OTRadValve::HardwareMotorDriverInterfaceCallbackHandler &callback) override
      {
      currentHigh = (OTRadValve::HardwareMotorDriverInterface::motorOff != dir);
      callback.signalHittingEndStop(true);
      }
  };

// Test initial state walk-through without and with calibration deferral.
TEST(CurrentSenseValveMotorDirect,initStateWalkthrough)
{
    const bool verbose = false;

    const uint8_t subcycleTicksRoundedDown_ms = 7; // For REV7: OTV0P2BASE::SUBCYCLE_TICK_MS_RD.
    const uint8_t gsct_max = 255; // For REV7: OTV0P2BASE::GSCT_MAX.
    const uint8_t minimumMotorRunupTicks = 4; // For REV7: OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks.
    for(int s = 0; s < 2; ++s)
        {
        const bool low = (1 == s); // Is battery low...
        if(verbose) { printf("Battery low %s...\n", low ? "true" : "false"); }
        DummyHardwareDriverHitEndstop dhw;
        SVL svl;
        svl.setAllLowFlags(low);
        OTRadValve::CurrentSenseValveMotorDirect csvmd1(&dhw, dummyGetSubCycleTime,
            OTRadValve::CurrentSenseValveMotorDirect::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
            OTRadValve::CurrentSenseValveMotorDirect::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                         gsct_max,
                                                                         minimumMotorRunupTicks),
            &svl,
            [](){return(false);});
        // Whitebox test of internal state: should be init.
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::init, csvmd1.getState());
        // Check deferral of (re)calibration.
        EXPECT_EQ(low, csvmd1.shouldDeferCalibration());
        // Verify NOT marked as in normal run state immediately upon initialisation.
        EXPECT_TRUE(!csvmd1.isInNormalRunState());
        // Verify NOT marked as in error state immediately upon initialisation.
        EXPECT_TRUE(!csvmd1.isInErrorState());
        csvmd1.poll();
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::initWaiting, csvmd1.getState());
        // Within a reasonable time to (10s of seconds) should move to new state, but not instantly.
        csvmd1.poll();
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::initWaiting, csvmd1.getState());
        csvmd1.poll();
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::initWaiting, csvmd1.getState());
        for(int i = 100; --i > 0 && OTRadValve::CurrentSenseValveMotorDirect::initWaiting == csvmd1.getState(); ) { csvmd1.poll(); }
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valvePinWithdrawing, csvmd1.getState());
        // Fake hardware hits end-stop immediate, so leaves 'withdrawing' state.
        csvmd1.poll();
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valvePinWithdrawn, csvmd1.getState());
        EXPECT_EQ(100, csvmd1.getCurrentPC()) << "valve must now be fully open";
        // Waiting for value to be signalled that it has been fitted...
        csvmd1.poll();
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valvePinWithdrawn, csvmd1.getState());
        csvmd1.signalValveFitted();
        csvmd1.poll();
        EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valveCalibrating, csvmd1.getState());
        csvmd1.poll();
        // Check deferral of (re)calibration.
        EXPECT_EQ(low, csvmd1.shouldDeferCalibration());
        // Valve should now start calibrating, but calibration is skipped with low battery...
        EXPECT_EQ(low ? OTRadValve::CurrentSenseValveMotorDirect::valveNormal :
                        OTRadValve::CurrentSenseValveMotorDirect::valveCalibrating, csvmd1.getState());
        }
}
<commit_msg>TODO-1038: WIP<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve CurrentSenseValveMotorDirect tests.
 */

#include <gtest/gtest.h>
#include <cstdint>
#include <cstdio>

#include <OTRadValve.h>

// Test calibration calculations in CurrentSenseValveMotorDirect for REV7/DORM1/TRV1 board.
// Also check some of the use of those calculations.
// In particular test the logic in ModelledRadValveState for starting from extreme positions.
//
// Adapted 2016/10/18 from test_VALVEMODEL.ino testCSVMDC().
TEST(CurrentSenseValveMotorDirect,REV7CSVMDC)
{
    // Compute minimum dead-reckoning ticks as for V0p2/REV7 20161018.
    const uint8_t subcycleTicksRoundedDown_ms = 7;
    const uint8_t minTicks = OTRadValve::CurrentSenseValveMotorDirect::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms);
    EXPECT_EQ(35, minTicks);
    // Compute maximum sub-cycle time to start valve movement as for V0p2/REV7 20161018.
    const uint8_t gst_max = 255;
    const uint8_t minimumMotorRunupTicks = 4; // OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks as at 20161018.
    const uint8_t sctAbsLimit = OTRadValve::CurrentSenseValveMotorDirect::computeSctAbsLimit(subcycleTicksRoundedDown_ms, gst_max, minimumMotorRunupTicks);
    EXPECT_EQ(230, sctAbsLimit);

    // Create calibration parameters with values that happen to work for V0p2/REV7.
    OTRadValve::CurrentSenseValveMotorDirect::CalibrationParameters cp;
    // Test the calculations with one plausible calibration data set.
    EXPECT_TRUE(cp.updateAndCompute(1601U, 1105U, minTicks)); // Must not fail...
    EXPECT_EQ(4, cp.getApproxPrecisionPC());
    EXPECT_EQ(25, cp.getTfotcSmall());
    EXPECT_EQ(17, cp.getTfctoSmall());
    // Check that a calibration instance can be reused correctly.
    const uint16_t tfo2 = 1803U;
    const uint16_t tfc2 = 1373U;
    EXPECT_TRUE(cp.updateAndCompute(tfo2, tfc2, minTicks)); // Must not fail...
    EXPECT_EQ(3, cp.getApproxPrecisionPC());
    EXPECT_EQ(28, cp.getTfotcSmall());
    EXPECT_EQ(21, cp.getTfctoSmall());

    // Check that computing position works...
    // Simple case: fully closed, no accumulated reverse ticks.
    volatile uint16_t ticksFromOpen, ticksReverse;
    ticksFromOpen = tfo2;
    ticksReverse = 0;
    EXPECT_EQ(0, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2, ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
    // Simple case: fully open, no accumulated reverse ticks.
    ticksFromOpen = 0;
    ticksReverse = 0;
    EXPECT_EQ(100, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(0, ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
    // Try at half-way mark, no reverse ticks.
    ticksFromOpen = tfo2 / 2;
    ticksReverse = 0;
    EXPECT_EQ(50, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2/2, ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
    // Try at half-way mark with just one reverse tick (nothing should change).
    ticksFromOpen = tfo2 / 2;
    ticksReverse = 1;
    EXPECT_EQ(50, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2/2, ticksFromOpen);
    EXPECT_EQ(1, ticksReverse);
    // Try at half-way mark with a big-enough block of reverse ticks to be significant.
    ticksFromOpen = tfo2 / 2;
    ticksReverse = cp.getTfctoSmall();
    EXPECT_EQ(51, cp.computePosition(ticksFromOpen, ticksReverse));
    EXPECT_EQ(tfo2/2 - cp.getTfotcSmall(), ticksFromOpen);
    EXPECT_EQ(0, ticksReverse);
  // DHD20151025: one set of actual measurements during calibration.
  //    ticksFromOpenToClosed: 1529
  //    ticksFromClosedToOpen: 1295
}

class DummyHardwareDriver : public OTRadValve::HardwareMotorDriverInterface
  {
  public:
    // Detect if end-stop is reached or motor current otherwise very high.
    bool currentHigh = false;
    virtual bool isCurrentHigh(OTRadValve::HardwareMotorDriverInterface::motor_drive mdir = motorDriveOpening) const override { return(currentHigh); }
    virtual void motorRun(uint8_t maxRunTicks, motor_drive dir, OTRadValve::HardwareMotorDriverInterfaceCallbackHandler &callback) override
      { }
  };

// Always claims to be at the start of a major cycle.
static uint8_t dummyGetSubCycleTime() { return(0); }

// Test that direct abstract motor drive logic is constructable and minimally sane..
//
// Adapted 2016/10/18 from test_VALVEMODEL.ino testCurrentSenseValveMotorDirect().
static void basics(OTRadValve::CurrentSenseValveMotorDirectBase *const csbp)
    {
    // POWER UP
    // Whitebox test of internal state: should be init.
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::init, csbp->getState());
    // Verify NOT marked as in normal run state immediately upon initialisation.
    EXPECT_TRUE(!csbp->isInNormalRunState());
    // Verify NOT marked as in error state immediately upon initialisation.
    EXPECT_TRUE(!csbp->isInErrorState());
    // Target % open must start off in a sensible state.
    EXPECT_GE(100, csbp->getTargetPC());
    // Current % open must start off in a sensible state.
    EXPECT_GE(100, csbp->getCurrentPC());
    }
TEST(CurrentSenseValveMotorDirect,basics)
{
    const uint8_t subcycleTicksRoundedDown_ms = 7; // For REV7: OTV0P2BASE::SUBCYCLE_TICK_MS_RD.
    const uint8_t gsct_max = 255; // For REV7: OTV0P2BASE::GSCT_MAX.
    const uint8_t minimumMotorRunupTicks = 4; // For REV7: OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks.

    DummyHardwareDriver dhw0;
    OTRadValve::CurrentSenseValveMotorDirectBinaryOnly csvmdbo1(&dhw0, dummyGetSubCycleTime,
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                     gsct_max,
                                                                     minimumMotorRunupTicks));
    basics(&csvmdbo1);

    DummyHardwareDriver dhw1;
    OTRadValve::CurrentSenseValveMotorDirect csvmd1(&dhw1, dummyGetSubCycleTime,
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
        OTRadValve::CurrentSenseValveMotorDirectBinaryOnly::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                     gsct_max,
                                                                     minimumMotorRunupTicks));
    basics(&csvmd1);
    // Until calibration has been successfully run, this should be in non-proportional mode.
    EXPECT_TRUE(csvmd1.inNonProprtionalMode());
    // Nothing passed in requires deferral of (re)calibration.
    EXPECT_FALSE(csvmd1.shouldDeferCalibration());
}

class SVL final : public OTV0P2BASE::SupplyVoltageLow
    {
    public:
      SVL() { setAllLowFlags(false); }
      void setAllLowFlags(const bool f) { isLow = f; isVeryLow = f; }
      virtual uint16_t get() const override { return(0); }
      virtual uint16_t read() override { return(0); }
    };
// State of ambient lighting.
static bool _isDark;
static bool isDark() { return(_isDark); }

// Test logic for potentially deferring (re)calibration.
TEST(CurrentSenseValveMotorDirect,calibrationDeferral)
{
    const uint8_t subcycleTicksRoundedDown_ms = 7; // For REV7: OTV0P2BASE::SUBCYCLE_TICK_MS_RD.
    const uint8_t gsct_max = 255; // For REV7: OTV0P2BASE::GSCT_MAX.
    const uint8_t minimumMotorRunupTicks = 4; // For REV7: OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks.
    DummyHardwareDriver dhw;
    SVL svl;
    svl.setAllLowFlags(false);
    _isDark = false;
    OTRadValve::CurrentSenseValveMotorDirect csvmd1(&dhw, dummyGetSubCycleTime,
        OTRadValve::CurrentSenseValveMotorDirect::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
        OTRadValve::CurrentSenseValveMotorDirect::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                     gsct_max,
                                                                     minimumMotorRunupTicks),
        &svl,
        isDark);
    // Nothing yet requires deferral of (re)calibration.
    ASSERT_FALSE(csvmd1.shouldDeferCalibration());
    svl.setAllLowFlags(true);
    // Low supply voltage requires deferral of (re)calibration.
    ASSERT_TRUE(csvmd1.shouldDeferCalibration());
    svl.setAllLowFlags(false);
    ASSERT_FALSE(csvmd1.shouldDeferCalibration());
    _isDark = true;
    // Low light level requires deferral of (re)calibration.
    ASSERT_TRUE(csvmd1.shouldDeferCalibration());
    svl.setAllLowFlags(true);
    ASSERT_TRUE(csvmd1.shouldDeferCalibration());
    _isDark = false;
    svl.setAllLowFlags(false);
    // Nothing requires deferral of (re)calibration.
    ASSERT_FALSE(csvmd1.shouldDeferCalibration());
}

// This hits the end stops (current is high) immediately that the motor is driven.
class DummyHardwareDriverHitEndstop : public OTRadValve::HardwareMotorDriverInterface
  {
    // Detect if end-stop is reached or motor current otherwise very high.
    bool currentHigh = false;
  public:
    virtual bool isCurrentHigh(OTRadValve::HardwareMotorDriverInterface::motor_drive mdir = motorDriveOpening) const override { return(currentHigh); }
    virtual void motorRun(uint8_t maxRunTicks, motor_drive dir, OTRadValve::HardwareMotorDriverInterfaceCallbackHandler &callback) override
      {
      currentHigh = (OTRadValve::HardwareMotorDriverInterface::motorOff != dir);
      callback.signalHittingEndStop(true);
      }
  };

// Test initial state walk-through without and with calibration deferral.
static void initStateWalkthrough(OTRadValve::CurrentSenseValveMotorDirect *const csv, const bool batteryLow)
    {
    // Whitebox test of internal state: should be init.
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirectBase::init, csv->getState());
    // Check deferral of (re)calibration.
    EXPECT_EQ(batteryLow, csv->shouldDeferCalibration());
    // Verify NOT marked as in normal run state immediately upon initialisation.
    EXPECT_TRUE(!csv->isInNormalRunState());
    // Verify NOT marked as in error state immediately upon initialisation.
    EXPECT_TRUE(!csv->isInErrorState());
    csv->poll();
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::initWaiting, csv->getState());
    // Within a reasonable time to (10s of seconds) should move to new state, but not instantly.
    csv->poll();
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::initWaiting, csv->getState());
    csv->poll();
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::initWaiting, csv->getState());
    for(int i = 100; --i > 0 && OTRadValve::CurrentSenseValveMotorDirect::initWaiting == csv->getState(); ) { csv->poll(); }
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valvePinWithdrawing, csv->getState());
    // Fake hardware hits end-stop immediate, so leaves 'withdrawing' state.
    csv->poll();
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valvePinWithdrawn, csv->getState());
    EXPECT_EQ(100, csv->getCurrentPC()) << "valve must now be fully open";
    // Waiting for value to be signalled that it has been fitted...
    csv->poll();
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valvePinWithdrawn, csv->getState());
    csv->signalValveFitted();
    csv->poll();
    EXPECT_EQ(OTRadValve::CurrentSenseValveMotorDirect::valveCalibrating, csv->getState());
    csv->poll();
    // Check deferral of (re)calibration.
    EXPECT_EQ(batteryLow, csv->shouldDeferCalibration());
    // Valve should now start calibrating, but calibration is skipped with low battery...
    EXPECT_EQ(batteryLow ? OTRadValve::CurrentSenseValveMotorDirect::valveNormal :
                           OTRadValve::CurrentSenseValveMotorDirect::valveCalibrating, csv->getState());
    }
TEST(CurrentSenseValveMotorDirect,initStateWalkthrough)
{
    const bool verbose = false;

    const uint8_t subcycleTicksRoundedDown_ms = 7; // For REV7: OTV0P2BASE::SUBCYCLE_TICK_MS_RD.
    const uint8_t gsct_max = 255; // For REV7: OTV0P2BASE::GSCT_MAX.
    const uint8_t minimumMotorRunupTicks = 4; // For REV7: OTRadValve::ValveMotorDirectV1HardwareDriverBase::minMotorRunupTicks.
    for(int s = 0; s < 2; ++s)
        {
        const bool low = (1 == s); // Is battery low...
        if(verbose) { printf("Battery low %s...\n", low ? "true" : "false"); }
        SVL svl;
        svl.setAllLowFlags(low);

        // Test full impl.
        DummyHardwareDriverHitEndstop dhw2;
        OTRadValve::CurrentSenseValveMotorDirect csvmd1(&dhw2, dummyGetSubCycleTime,
            OTRadValve::CurrentSenseValveMotorDirect::computeMinMotorDRTicks(subcycleTicksRoundedDown_ms),
            OTRadValve::CurrentSenseValveMotorDirect::computeSctAbsLimit(subcycleTicksRoundedDown_ms,
                                                                         gsct_max,
                                                                         minimumMotorRunupTicks),
            &svl,
            [](){return(false);});
        initStateWalkthrough(&csvmd1, low);
        }
}
<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright (c) 2012, Dougal J. Sutherland (dsutherl@cs.cmu.edu).             *
 * All rights reserved.                                                        *
 *                                                                             *
 * Redistribution and use in source and binary forms, with or without          *
 * modification, are permitted provided that the following conditions are met: *
 *                                                                             *
 *     * Redistributions of source code must retain the above copyright        *
 *       notice, this list of conditions and the following disclaimer.         *
 *                                                                             *
 *     * Redistributions in binary form must reproduce the above copyright     *
 *       notice, this list of conditions and the following disclaimer in the   *
 *       documentation and/or other materials provided with the distribution.  *
 *                                                                             *
 *     * Neither the name of Carnegie Mellon University nor the                *
 *       names of the contributors may be used to endorse or promote products  *
 *       derived from this software without specific prior written permission. *
 *                                                                             *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  *
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE   *
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR         *
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF        *
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN     *
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)     *
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  *
 * POSSIBILITY OF SUCH DAMAGE.                                                 *
 ******************************************************************************/
#ifndef SDM_KERNELS_POLYNOMIAL_HPP_
#define SDM_KERNELS_POLYNOMIAL_HPP_
#include "sdm/basics.hpp"
#include "sdm/kernels/kernel.hpp"

#include <vector>

#include <boost/ptr_container/ptr_vector.hpp>

namespace sdm {

class PolynomialKernel : public Kernel {
    typedef Kernel super;

protected:
    size_t degree;
    double coef0;

public:
    PolynomialKernel(size_t degree, double coef0 = 1)
        : super(), degree(degree), coef0(coef0)
    {}

    virtual std::string name() const;

    virtual double transformDivergence(double div) const;

    size_t getDegree() const;
    double getCoef0() const;

private:
    virtual PolynomialKernel* do_clone() const;
};

////////////////////////////////////////////////////////////////////////////////

namespace detail {
    const size_t degs[7] = { 1, 2, 3, 4, 5, 7, 9 };
    const double coef0s[1] = { 1 };
}
const std::vector<size_t> default_degrees(detail::degs, detail::degs + 7);
const std::vector<double> default_coef0s(detail::coef0s, detail::coef0s + 2);

class PolynomialKernelGroup : public KernelGroup {
protected:
    const std::vector<size_t> degrees;
    const std::vector<double> coef0s;

public:
    typedef PolynomialKernel KernelType;

    PolynomialKernelGroup(
            const std::vector<size_t> &degrees = default_degrees,
            const std::vector<double> &coef0s = default_coef0s)
        : degrees(degrees), coef0s(coef0s)
    {}

    virtual const boost::ptr_vector<Kernel>* getTuningVector(
            double* divs, size_t n) const;

private:
    virtual PolynomialKernelGroup* do_clone() const;
};

} // end namespace
#endif
<commit_msg>ugh fix a dumb mistake<commit_after>/*******************************************************************************
 * Copyright (c) 2012, Dougal J. Sutherland (dsutherl@cs.cmu.edu).             *
 * All rights reserved.                                                        *
 *                                                                             *
 * Redistribution and use in source and binary forms, with or without          *
 * modification, are permitted provided that the following conditions are met: *
 *                                                                             *
 *     * Redistributions of source code must retain the above copyright        *
 *       notice, this list of conditions and the following disclaimer.         *
 *                                                                             *
 *     * Redistributions in binary form must reproduce the above copyright     *
 *       notice, this list of conditions and the following disclaimer in the   *
 *       documentation and/or other materials provided with the distribution.  *
 *                                                                             *
 *     * Neither the name of Carnegie Mellon University nor the                *
 *       names of the contributors may be used to endorse or promote products  *
 *       derived from this software without specific prior written permission. *
 *                                                                             *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  *
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE   *
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR         *
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF        *
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN     *
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)     *
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  *
 * POSSIBILITY OF SUCH DAMAGE.                                                 *
 ******************************************************************************/
#ifndef SDM_KERNELS_POLYNOMIAL_HPP_
#define SDM_KERNELS_POLYNOMIAL_HPP_
#include "sdm/basics.hpp"
#include "sdm/kernels/kernel.hpp"

#include <vector>

#include <boost/ptr_container/ptr_vector.hpp>

namespace sdm {

class PolynomialKernel : public Kernel {
    typedef Kernel super;

protected:
    size_t degree;
    double coef0;

public:
    PolynomialKernel(size_t degree, double coef0 = 1)
        : super(), degree(degree), coef0(coef0)
    {}

    virtual std::string name() const;

    virtual double transformDivergence(double div) const;

    size_t getDegree() const;
    double getCoef0() const;

private:
    virtual PolynomialKernel* do_clone() const;
};

////////////////////////////////////////////////////////////////////////////////

namespace detail {
    const size_t degs[7] = { 1, 2, 3, 4, 5, 7, 9 };
    const double coef0s[1] = { 1 };
}
const std::vector<size_t> default_degrees(detail::degs, detail::degs + 7);
const std::vector<double> default_coef0s(detail::coef0s, detail::coef0s + 1);

class PolynomialKernelGroup : public KernelGroup {
protected:
    const std::vector<size_t> degrees;
    const std::vector<double> coef0s;

public:
    typedef PolynomialKernel KernelType;

    PolynomialKernelGroup(
            const std::vector<size_t> &degrees = default_degrees,
            const std::vector<double> &coef0s = default_coef0s)
        : degrees(degrees), coef0s(coef0s)
    {}

    virtual const boost::ptr_vector<Kernel>* getTuningVector(
            double* divs, size_t n) const;

private:
    virtual PolynomialKernelGroup* do_clone() const;
};

} // end namespace
#endif
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/set_as_default_browser_ui.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/prefs/pref_service.h"
#include "base/win/win_util.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/installer/util/install_util.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_font_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/font.h"
#include "ui/views/widget/widget.h"
#include "ui/web_dialogs/web_dialog_delegate.h"

using content::BrowserThread;
using content::WebContents;
using content::WebUIMessageHandler;

namespace {

const char kSetAsDefaultBrowserHistogram[] = "DefaultBrowser.InteractionResult";

// The enum permits registering in UMA the three possible outcomes.
// ACCEPTED: user pressed Next and made Chrome default.
// DECLINED: user simply closed the dialog without making Chrome default.
// REGRETTED: user pressed Next but then elected a different default browser.
// ACCEPTED_IMMERSE: as above with a switch to metro mode.
enum MakeChromeDefaultResult {
  MAKE_CHROME_DEFAULT_ACCEPTED,
  MAKE_CHROME_DEFAULT_DECLINED,
  MAKE_CHROME_DEFAULT_REGRETTED,
  MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE,
  MAKE_CHROME_DEFAULT_MAX
};

content::WebUIDataSource* CreateSetAsDefaultBrowserUIHTMLSource() {
  content::WebUIDataSource* data_source = content::WebUIDataSource::Create(
      chrome::kChromeUIMetroFlowHost);
  data_source->AddLocalizedString("page-title", IDS_METRO_FLOW_TAB_TITLE);
  data_source->AddLocalizedString("flowTitle", IDS_METRO_FLOW_TITLE_SHORT);
  data_source->AddLocalizedString("flowDescription",
                                  IDS_METRO_FLOW_DESCRIPTION);
  data_source->AddLocalizedString("flowNext",
                                  IDS_METRO_FLOW_SET_DEFAULT);
  data_source->AddLocalizedString("chromeLogoString",
                                  IDS_METRO_FLOW_LOGO_STRING_ALT);
  data_source->SetJsonPath("strings.js");
  data_source->AddResourcePath("set_as_default_browser.js",
      IDR_SET_AS_DEFAULT_BROWSER_JS);
  data_source->SetDefaultResource(IDR_SET_AS_DEFAULT_BROWSER_HTML);
  return data_source;
}

// A simple class serving as a delegate for passing down the result of the
// interaction.
class ResponseDelegate {
 public:
  virtual void SetDialogInteractionResult(MakeChromeDefaultResult result) = 0;

 protected:
  virtual ~ResponseDelegate() { }
};

// Event handler for SetAsDefaultBrowserUI. Capable of setting Chrome as the
// default browser on button click, closing itself and triggering Chrome
// restart.
class SetAsDefaultBrowserHandler
    : public WebUIMessageHandler,
      public base::SupportsWeakPtr<SetAsDefaultBrowserHandler>,
      public ShellIntegration::DefaultWebClientObserver {
 public:
  explicit SetAsDefaultBrowserHandler(ResponseDelegate* response_delegate);
  virtual ~SetAsDefaultBrowserHandler();

  // WebUIMessageHandler implementation.
  virtual void RegisterMessages() OVERRIDE;

  // ShellIntegration::DefaultWebClientObserver implementation.
  virtual void SetDefaultWebClientUIState(
      ShellIntegration::DefaultWebClientUIState state) OVERRIDE;
  virtual void OnSetAsDefaultConcluded(bool close_chrome)  OVERRIDE;
  virtual bool IsInteractiveSetDefaultPermitted() OVERRIDE;

 private:
  // Handler for the 'Next' (or 'make Chrome the Metro browser') button.
  void HandleLaunchSetDefaultBrowserFlow(const ListValue* args);

  // Close this web ui.
  void ConcludeInteraction(MakeChromeDefaultResult interaction_result);

  // Returns true if Chrome should be restarted in immersive mode upon being
  // made the default browser.
  bool ShouldAttemptImmersiveRestart();

  scoped_refptr<ShellIntegration::DefaultBrowserWorker> default_browser_worker_;
  bool set_default_returned_;
  bool set_default_result_;
  ResponseDelegate* response_delegate_;

  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserHandler);
};

SetAsDefaultBrowserHandler::SetAsDefaultBrowserHandler(
    ResponseDelegate* response_delegate)
    : ALLOW_THIS_IN_INITIALIZER_LIST(default_browser_worker_(
          new ShellIntegration::DefaultBrowserWorker(this))),
      set_default_returned_(false), set_default_result_(false),
      response_delegate_(response_delegate) {
}

SetAsDefaultBrowserHandler::~SetAsDefaultBrowserHandler() {
  default_browser_worker_->ObserverDestroyed();
}

void SetAsDefaultBrowserHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "SetAsDefaultBrowser:LaunchSetDefaultBrowserFlow",
      base::Bind(&SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow,
                 base::Unretained(this)));
}

void SetAsDefaultBrowserHandler::SetDefaultWebClientUIState(
    ShellIntegration::DefaultWebClientUIState state) {
  // The callback is expected to be invoked once the procedure has completed.
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!set_default_returned_)
    return;

  if (state == ShellIntegration::STATE_NOT_DEFAULT && set_default_result_) {
    // The operation concluded, but Chrome is still not the default.
    // If the call has succeeded, this suggests user has decided not to make
    // chrome the default.
    ConcludeInteraction(MAKE_CHROME_DEFAULT_REGRETTED);
  } else if (state == ShellIntegration::STATE_IS_DEFAULT) {
    ConcludeInteraction(ShouldAttemptImmersiveRestart() ?
        MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE : MAKE_CHROME_DEFAULT_ACCEPTED);
  }

  // Otherwise, keep the dialog open since the user probably didn't make a
  // choice.
}

void SetAsDefaultBrowserHandler::OnSetAsDefaultConcluded(bool call_result) {
  set_default_returned_ = true;
  set_default_result_ = call_result;
}

bool SetAsDefaultBrowserHandler::IsInteractiveSetDefaultPermitted() {
  return true;
}

void SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow(
    const ListValue* args) {
  set_default_returned_ = false;
  set_default_result_ = false;
  default_browser_worker_->StartSetAsDefault();
}

void SetAsDefaultBrowserHandler::ConcludeInteraction(
    MakeChromeDefaultResult interaction_result) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (response_delegate_)
    response_delegate_->SetDialogInteractionResult(interaction_result);

  WebContents* contents = web_ui()->GetWebContents();

  if (contents) {
    content::WebContentsDelegate* delegate = contents->GetDelegate();
    if (delegate)
      delegate->CloseContents(contents);
  }
}

bool SetAsDefaultBrowserHandler::ShouldAttemptImmersiveRestart() {
  return (base::win::IsMachineATablet() &&
          !Profile::FromWebUI(web_ui())->GetPrefs()->GetBoolean(
              prefs::kSuppressSwitchToMetroModeOnSetDefault));
}

// A web dialog delegate implementation for when 'Make Chrome Metro' UI
// is displayed on a dialog.
class SetAsDefaultBrowserDialogImpl : public ui::WebDialogDelegate,
                                      public ResponseDelegate {
 public:
  SetAsDefaultBrowserDialogImpl(Profile* profile, Browser* browser);
  virtual ~SetAsDefaultBrowserDialogImpl();
  // Show a modal web dialog with kChromeUIMetroFlowURL page.
  void ShowDialog();

 protected:
  // Overridden from WebDialogDelegate:
  virtual ui::ModalType GetDialogModalType() const OVERRIDE;
  virtual string16 GetDialogTitle() const OVERRIDE;
  virtual GURL GetDialogContentURL() const OVERRIDE;
  virtual void GetWebUIMessageHandlers(
      std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE;
  virtual void GetDialogSize(gfx::Size* size) const OVERRIDE;
  virtual std::string GetDialogArgs() const OVERRIDE;
  virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE;
  virtual void OnCloseContents(WebContents* source,
                               bool* out_close_dialog) OVERRIDE;
  virtual bool ShouldShowDialogTitle() const OVERRIDE;
  virtual bool HandleContextMenu(
      const content::ContextMenuParams& params) OVERRIDE;

  // Overridden from ResponseDelegate:
  virtual void SetDialogInteractionResult(MakeChromeDefaultResult result);

 private:
  // Reset the first-run sentinel file, so must be called on the FILE thread.
  // This is needed if the browser should be restarted in immersive mode.
  // The method is static because the dialog could be destroyed
  // before the task arrives on the FILE thread.
  static void AttemptImmersiveFirstRunRestartOnFileThread();

  Profile* profile_;
  Browser* browser_;
  mutable bool owns_handler_;
  SetAsDefaultBrowserHandler* handler_;
  MakeChromeDefaultResult dialog_interaction_result_;

  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserDialogImpl);
};

SetAsDefaultBrowserDialogImpl::SetAsDefaultBrowserDialogImpl(Profile* profile,
                                                             Browser* browser)
    : profile_(profile),
      browser_(browser),
      owns_handler_(true),
      handler_(new SetAsDefaultBrowserHandler(this)),
      dialog_interaction_result_(MAKE_CHROME_DEFAULT_DECLINED) {
}

SetAsDefaultBrowserDialogImpl::~SetAsDefaultBrowserDialogImpl() {
  if (owns_handler_)
    delete handler_;
}

void SetAsDefaultBrowserDialogImpl::ShowDialog() {
  // Use a NULL parent window to make sure that the dialog will have an item
  // in the Windows task bar. The code below will make it highlight if the
  // dialog is not in the foreground.
  gfx::NativeWindow native_window = chrome::ShowWebDialog(NULL,
                                                          browser_->profile(),
                                                          this);
  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
      native_window);
  widget->FlashFrame(true);
}

ui::ModalType SetAsDefaultBrowserDialogImpl::GetDialogModalType() const {
  return ui::MODAL_TYPE_SYSTEM;
}

string16 SetAsDefaultBrowserDialogImpl::GetDialogTitle() const {
  return l10n_util::GetStringUTF16(IDS_METRO_FLOW_TAB_TITLE);
}

GURL SetAsDefaultBrowserDialogImpl::GetDialogContentURL() const {
  std::string url_string(chrome::kChromeUIMetroFlowURL);
  return GURL(url_string);
}

void SetAsDefaultBrowserDialogImpl::GetWebUIMessageHandlers(
    std::vector<WebUIMessageHandler*>* handlers) const {
  handlers->push_back(handler_);
  owns_handler_ = false;
}

void SetAsDefaultBrowserDialogImpl::GetDialogSize(gfx::Size* size) const {
  PrefService* prefs = profile_->GetPrefs();
  gfx::Font approximate_web_font(
      prefs->GetString(prefs::kWebKitSansSerifFontFamily),
      prefs->GetInteger(prefs::kWebKitDefaultFontSize));

  *size = ui::GetLocalizedContentsSizeForFont(
      IDS_METRO_FLOW_WIDTH_CHARS, IDS_METRO_FLOW_HEIGHT_LINES,
      approximate_web_font);
}

std::string SetAsDefaultBrowserDialogImpl::GetDialogArgs() const {
  return "[]";
}

void SetAsDefaultBrowserDialogImpl::OnDialogClosed(
    const std::string& json_retval) {
  // Register the user's response in UMA.
  UMA_HISTOGRAM_ENUMERATION(kSetAsDefaultBrowserHistogram,
                            dialog_interaction_result_,
                            MAKE_CHROME_DEFAULT_MAX);

  if (dialog_interaction_result_ == MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE) {
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        base::Bind(&SetAsDefaultBrowserDialogImpl::
            AttemptImmersiveFirstRunRestartOnFileThread));
  } else {
    // If the user explicitly elected *not to* make Chrome default, we won't
    // ask again.
    if (dialog_interaction_result_ == MAKE_CHROME_DEFAULT_REGRETTED) {
      PrefService* prefs = profile_->GetPrefs();
      prefs->SetBoolean(prefs::kCheckDefaultBrowser, false);
    }

    // Carry on with a normal chrome session. For the purpose of surfacing this
    // dialog the actual browser window had to remain hidden. Now it's time to
    // show it.
    BrowserWindow* window = browser_->window();
    WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
    window->Show();
    if (contents)
      contents->GetView()->SetInitialFocus();
  }

  delete this;
}

void SetAsDefaultBrowserDialogImpl::OnCloseContents(WebContents* source,
                                                    bool* out_close_dialog) {
  *out_close_dialog = true;
}

bool SetAsDefaultBrowserDialogImpl::ShouldShowDialogTitle() const {
  return true;
}

bool SetAsDefaultBrowserDialogImpl::HandleContextMenu(
    const content::ContextMenuParams& params) {
  return true;
}

void SetAsDefaultBrowserDialogImpl::SetDialogInteractionResult(
    MakeChromeDefaultResult result) {
  dialog_interaction_result_ = result;
}

void SetAsDefaultBrowserDialogImpl::
    AttemptImmersiveFirstRunRestartOnFileThread() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  // If the sentinel was created for this launch, remove it before restarting
  // in immersive mode so that the user is taken through the full first-run
  // flow there.
  if (first_run::IsChromeFirstRun())
    first_run::RemoveSentinel();

  // Do a straight-up restart rather than a mode-switch restart.
  // delegate_execute.exe will choose an immersive launch on the basis of the
  // same IsMachineATablet check, but will not store this as the user's
  // choice.
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                          base::Bind(&chrome::AttemptRestart));
}

}  // namespace

SetAsDefaultBrowserUI::SetAsDefaultBrowserUI(content::WebUI* web_ui)
    : ui::WebDialogUI(web_ui) {
  content::WebUIDataSource::Add(
      Profile::FromWebUI(web_ui), CreateSetAsDefaultBrowserUIHTMLSource());
}

// static
void SetAsDefaultBrowserUI::Show(Profile* profile, Browser* browser) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  SetAsDefaultBrowserDialogImpl* dialog =
      new SetAsDefaultBrowserDialogImpl(profile, browser);
  dialog->ShowDialog();
}
<commit_msg>The "You're almost done" dialog ahould not try to show the initial browser window if it has somehow gone away.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/set_as_default_browser_ui.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/prefs/pref_service.h"
#include "base/win/win_util.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/installer/util/install_util.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_font_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/font.h"
#include "ui/views/widget/widget.h"
#include "ui/web_dialogs/web_dialog_delegate.h"

using content::BrowserThread;
using content::WebContents;
using content::WebUIMessageHandler;

namespace {

const char kSetAsDefaultBrowserHistogram[] = "DefaultBrowser.InteractionResult";

// The enum permits registering in UMA the three possible outcomes.
// ACCEPTED: user pressed Next and made Chrome default.
// DECLINED: user simply closed the dialog without making Chrome default.
// REGRETTED: user pressed Next but then elected a different default browser.
// ACCEPTED_IMMERSE: as above with a switch to metro mode.
enum MakeChromeDefaultResult {
  MAKE_CHROME_DEFAULT_ACCEPTED,
  MAKE_CHROME_DEFAULT_DECLINED,
  MAKE_CHROME_DEFAULT_REGRETTED,
  MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE,
  MAKE_CHROME_DEFAULT_MAX
};

content::WebUIDataSource* CreateSetAsDefaultBrowserUIHTMLSource() {
  content::WebUIDataSource* data_source = content::WebUIDataSource::Create(
      chrome::kChromeUIMetroFlowHost);
  data_source->AddLocalizedString("page-title", IDS_METRO_FLOW_TAB_TITLE);
  data_source->AddLocalizedString("flowTitle", IDS_METRO_FLOW_TITLE_SHORT);
  data_source->AddLocalizedString("flowDescription",
                                  IDS_METRO_FLOW_DESCRIPTION);
  data_source->AddLocalizedString("flowNext",
                                  IDS_METRO_FLOW_SET_DEFAULT);
  data_source->AddLocalizedString("chromeLogoString",
                                  IDS_METRO_FLOW_LOGO_STRING_ALT);
  data_source->SetJsonPath("strings.js");
  data_source->AddResourcePath("set_as_default_browser.js",
      IDR_SET_AS_DEFAULT_BROWSER_JS);
  data_source->SetDefaultResource(IDR_SET_AS_DEFAULT_BROWSER_HTML);
  return data_source;
}

// A simple class serving as a delegate for passing down the result of the
// interaction.
class ResponseDelegate {
 public:
  virtual void SetDialogInteractionResult(MakeChromeDefaultResult result) = 0;

 protected:
  virtual ~ResponseDelegate() { }
};

// Event handler for SetAsDefaultBrowserUI. Capable of setting Chrome as the
// default browser on button click, closing itself and triggering Chrome
// restart.
class SetAsDefaultBrowserHandler
    : public WebUIMessageHandler,
      public base::SupportsWeakPtr<SetAsDefaultBrowserHandler>,
      public ShellIntegration::DefaultWebClientObserver {
 public:
  explicit SetAsDefaultBrowserHandler(ResponseDelegate* response_delegate);
  virtual ~SetAsDefaultBrowserHandler();

  // WebUIMessageHandler implementation.
  virtual void RegisterMessages() OVERRIDE;

  // ShellIntegration::DefaultWebClientObserver implementation.
  virtual void SetDefaultWebClientUIState(
      ShellIntegration::DefaultWebClientUIState state) OVERRIDE;
  virtual void OnSetAsDefaultConcluded(bool close_chrome)  OVERRIDE;
  virtual bool IsInteractiveSetDefaultPermitted() OVERRIDE;

 private:
  // Handler for the 'Next' (or 'make Chrome the Metro browser') button.
  void HandleLaunchSetDefaultBrowserFlow(const ListValue* args);

  // Close this web ui.
  void ConcludeInteraction(MakeChromeDefaultResult interaction_result);

  // Returns true if Chrome should be restarted in immersive mode upon being
  // made the default browser.
  bool ShouldAttemptImmersiveRestart();

  scoped_refptr<ShellIntegration::DefaultBrowserWorker> default_browser_worker_;
  bool set_default_returned_;
  bool set_default_result_;
  ResponseDelegate* response_delegate_;

  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserHandler);
};

SetAsDefaultBrowserHandler::SetAsDefaultBrowserHandler(
    ResponseDelegate* response_delegate)
    : ALLOW_THIS_IN_INITIALIZER_LIST(default_browser_worker_(
          new ShellIntegration::DefaultBrowserWorker(this))),
      set_default_returned_(false), set_default_result_(false),
      response_delegate_(response_delegate) {
}

SetAsDefaultBrowserHandler::~SetAsDefaultBrowserHandler() {
  default_browser_worker_->ObserverDestroyed();
}

void SetAsDefaultBrowserHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "SetAsDefaultBrowser:LaunchSetDefaultBrowserFlow",
      base::Bind(&SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow,
                 base::Unretained(this)));
}

void SetAsDefaultBrowserHandler::SetDefaultWebClientUIState(
    ShellIntegration::DefaultWebClientUIState state) {
  // The callback is expected to be invoked once the procedure has completed.
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!set_default_returned_)
    return;

  if (state == ShellIntegration::STATE_NOT_DEFAULT && set_default_result_) {
    // The operation concluded, but Chrome is still not the default.
    // If the call has succeeded, this suggests user has decided not to make
    // chrome the default.
    ConcludeInteraction(MAKE_CHROME_DEFAULT_REGRETTED);
  } else if (state == ShellIntegration::STATE_IS_DEFAULT) {
    ConcludeInteraction(ShouldAttemptImmersiveRestart() ?
        MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE : MAKE_CHROME_DEFAULT_ACCEPTED);
  }

  // Otherwise, keep the dialog open since the user probably didn't make a
  // choice.
}

void SetAsDefaultBrowserHandler::OnSetAsDefaultConcluded(bool call_result) {
  set_default_returned_ = true;
  set_default_result_ = call_result;
}

bool SetAsDefaultBrowserHandler::IsInteractiveSetDefaultPermitted() {
  return true;
}

void SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow(
    const ListValue* args) {
  set_default_returned_ = false;
  set_default_result_ = false;
  default_browser_worker_->StartSetAsDefault();
}

void SetAsDefaultBrowserHandler::ConcludeInteraction(
    MakeChromeDefaultResult interaction_result) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (response_delegate_)
    response_delegate_->SetDialogInteractionResult(interaction_result);

  WebContents* contents = web_ui()->GetWebContents();

  if (contents) {
    content::WebContentsDelegate* delegate = contents->GetDelegate();
    if (delegate)
      delegate->CloseContents(contents);
  }
}

bool SetAsDefaultBrowserHandler::ShouldAttemptImmersiveRestart() {
  return (base::win::IsMachineATablet() &&
          !Profile::FromWebUI(web_ui())->GetPrefs()->GetBoolean(
              prefs::kSuppressSwitchToMetroModeOnSetDefault));
}

// A web dialog delegate implementation for when 'Make Chrome Metro' UI
// is displayed on a dialog.
class SetAsDefaultBrowserDialogImpl : public ui::WebDialogDelegate,
                                      public ResponseDelegate,
                                      public chrome::BrowserListObserver {
 public:
  SetAsDefaultBrowserDialogImpl(Profile* profile, Browser* browser);
  virtual ~SetAsDefaultBrowserDialogImpl();
  // Show a modal web dialog with kChromeUIMetroFlowURL page.
  void ShowDialog();

 protected:
  // Overridden from WebDialogDelegate:
  virtual ui::ModalType GetDialogModalType() const OVERRIDE;
  virtual string16 GetDialogTitle() const OVERRIDE;
  virtual GURL GetDialogContentURL() const OVERRIDE;
  virtual void GetWebUIMessageHandlers(
      std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE;
  virtual void GetDialogSize(gfx::Size* size) const OVERRIDE;
  virtual std::string GetDialogArgs() const OVERRIDE;
  virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE;
  virtual void OnCloseContents(WebContents* source,
                               bool* out_close_dialog) OVERRIDE;
  virtual bool ShouldShowDialogTitle() const OVERRIDE;
  virtual bool HandleContextMenu(
      const content::ContextMenuParams& params) OVERRIDE;

  // Overridden from ResponseDelegate:
  virtual void SetDialogInteractionResult(MakeChromeDefaultResult result);

  // Overridden from BrowserListObserver:
  virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;

 private:
  // Reset the first-run sentinel file, so must be called on the FILE thread.
  // This is needed if the browser should be restarted in immersive mode.
  // The method is static because the dialog could be destroyed
  // before the task arrives on the FILE thread.
  static void AttemptImmersiveFirstRunRestartOnFileThread();

  Profile* profile_;
  Browser* browser_;
  mutable bool owns_handler_;
  SetAsDefaultBrowserHandler* handler_;
  MakeChromeDefaultResult dialog_interaction_result_;

  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserDialogImpl);
};

SetAsDefaultBrowserDialogImpl::SetAsDefaultBrowserDialogImpl(Profile* profile,
                                                             Browser* browser)
    : profile_(profile),
      browser_(browser),
      owns_handler_(true),
      handler_(new SetAsDefaultBrowserHandler(this)),
      dialog_interaction_result_(MAKE_CHROME_DEFAULT_DECLINED) {
  BrowserList::AddObserver(this);
}

SetAsDefaultBrowserDialogImpl::~SetAsDefaultBrowserDialogImpl() {
  if (browser_)
    BrowserList::RemoveObserver(this);
  if (owns_handler_)
    delete handler_;
}

void SetAsDefaultBrowserDialogImpl::ShowDialog() {
  // Use a NULL parent window to make sure that the dialog will have an item
  // in the Windows task bar. The code below will make it highlight if the
  // dialog is not in the foreground.
  gfx::NativeWindow native_window = chrome::ShowWebDialog(NULL, profile_, this);
  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
      native_window);
  widget->FlashFrame(true);
}

ui::ModalType SetAsDefaultBrowserDialogImpl::GetDialogModalType() const {
  return ui::MODAL_TYPE_SYSTEM;
}

string16 SetAsDefaultBrowserDialogImpl::GetDialogTitle() const {
  return l10n_util::GetStringUTF16(IDS_METRO_FLOW_TAB_TITLE);
}

GURL SetAsDefaultBrowserDialogImpl::GetDialogContentURL() const {
  std::string url_string(chrome::kChromeUIMetroFlowURL);
  return GURL(url_string);
}

void SetAsDefaultBrowserDialogImpl::GetWebUIMessageHandlers(
    std::vector<WebUIMessageHandler*>* handlers) const {
  handlers->push_back(handler_);
  owns_handler_ = false;
}

void SetAsDefaultBrowserDialogImpl::GetDialogSize(gfx::Size* size) const {
  PrefService* prefs = profile_->GetPrefs();
  gfx::Font approximate_web_font(
      prefs->GetString(prefs::kWebKitSansSerifFontFamily),
      prefs->GetInteger(prefs::kWebKitDefaultFontSize));

  *size = ui::GetLocalizedContentsSizeForFont(
      IDS_METRO_FLOW_WIDTH_CHARS, IDS_METRO_FLOW_HEIGHT_LINES,
      approximate_web_font);
}

std::string SetAsDefaultBrowserDialogImpl::GetDialogArgs() const {
  return "[]";
}

void SetAsDefaultBrowserDialogImpl::OnDialogClosed(
    const std::string& json_retval) {
  // Register the user's response in UMA.
  UMA_HISTOGRAM_ENUMERATION(kSetAsDefaultBrowserHistogram,
                            dialog_interaction_result_,
                            MAKE_CHROME_DEFAULT_MAX);

  if (dialog_interaction_result_ == MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE) {
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        base::Bind(&SetAsDefaultBrowserDialogImpl::
            AttemptImmersiveFirstRunRestartOnFileThread));
  } else {
    // If the user explicitly elected *not to* make Chrome default, we won't
    // ask again.
    if (dialog_interaction_result_ == MAKE_CHROME_DEFAULT_REGRETTED) {
      PrefService* prefs = profile_->GetPrefs();
      prefs->SetBoolean(prefs::kCheckDefaultBrowser, false);
    }

    // Carry on with a normal chrome session. For the purpose of surfacing this
    // dialog the actual browser window had to remain hidden. Now it's time to
    // show it.
    if (browser_) {
      BrowserWindow* window = browser_->window();
      WebContents* contents =
          browser_->tab_strip_model()->GetActiveWebContents();
      window->Show();
      if (contents)
        contents->GetView()->SetInitialFocus();
    }
  }

  delete this;
}

void SetAsDefaultBrowserDialogImpl::OnCloseContents(WebContents* source,
                                                    bool* out_close_dialog) {
  *out_close_dialog = true;
}

bool SetAsDefaultBrowserDialogImpl::ShouldShowDialogTitle() const {
  return true;
}

bool SetAsDefaultBrowserDialogImpl::HandleContextMenu(
    const content::ContextMenuParams& params) {
  return true;
}

void SetAsDefaultBrowserDialogImpl::SetDialogInteractionResult(
    MakeChromeDefaultResult result) {
  dialog_interaction_result_ = result;
}

void SetAsDefaultBrowserDialogImpl::OnBrowserRemoved(Browser* browser) {
  if (browser_ == browser) {
    browser_ = NULL;
    BrowserList::RemoveObserver(this);
  }
}

void SetAsDefaultBrowserDialogImpl::
    AttemptImmersiveFirstRunRestartOnFileThread() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  // If the sentinel was created for this launch, remove it before restarting
  // in immersive mode so that the user is taken through the full first-run
  // flow there.
  if (first_run::IsChromeFirstRun())
    first_run::RemoveSentinel();

  // Do a straight-up restart rather than a mode-switch restart.
  // delegate_execute.exe will choose an immersive launch on the basis of the
  // same IsMachineATablet check, but will not store this as the user's
  // choice.
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                          base::Bind(&chrome::AttemptRestart));
}

}  // namespace

SetAsDefaultBrowserUI::SetAsDefaultBrowserUI(content::WebUI* web_ui)
    : ui::WebDialogUI(web_ui) {
  content::WebUIDataSource::Add(
      Profile::FromWebUI(web_ui), CreateSetAsDefaultBrowserUIHTMLSource());
}

// static
void SetAsDefaultBrowserUI::Show(Profile* profile, Browser* browser) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  SetAsDefaultBrowserDialogImpl* dialog =
      new SetAsDefaultBrowserDialogImpl(profile, browser);
  dialog->ShowDialog();
}
<|endoftext|>