<commit_before>// Copyright (c) 2009 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/command_line.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"

namespace {

// Used to block until a dev tools client window's browser is closed.
class BrowserClosedObserver : public NotificationObserver {
 public:
  explicit BrowserClosedObserver(Browser* browser) {
    registrar_.Add(this, NotificationType::BROWSER_CLOSED,
                   Source<Browser>(browser));
    ui_test_utils::RunMessageLoop();
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    MessageLoopForUI::current()->Quit();
  }

 private:
  NotificationRegistrar registrar_;
  DISALLOW_COPY_AND_ASSIGN(BrowserClosedObserver);
};

// The delay waited in some cases where we don't have a notifications for an
// action we take.
const int kActionDelayMs = 500;

const wchar_t kConsoleTestPage[] = L"files/devtools/console_test_page.html";
const wchar_t kDebuggerTestPage[] = L"files/devtools/debugger_test_page.html";
const wchar_t kEvalTestPage[] = L"files/devtools/eval_test_page.html";
const wchar_t kJsPage[] = L"files/devtools/js_page.html";
const wchar_t kPauseOnExceptionTestPage[] =
    L"files/devtools/pause_on_exception.html";
const wchar_t kPauseWhenLoadingDevTools[] =
    L"files/devtools/pause_when_loading_devtools.html";
const wchar_t kPauseWhenScriptIsRunning[] =
    L"files/devtools/pause_when_script_is_running.html";
const wchar_t kResourceContentLengthTestPage[] = L"files/devtools/image.html";
const wchar_t kResourceTestPage[] = L"files/devtools/resource_test_page.html";
const wchar_t kSimplePage[] = L"files/devtools/simple_page.html";
const wchar_t kSyntaxErrorTestPage[] =
    L"files/devtools/script_syntax_error.html";
const wchar_t kDebuggerStepTestPage[] =
    L"files/devtools/debugger_step.html";
const wchar_t kDebuggerClosurePage[] =
    L"files/devtools/debugger_closure.html";
const wchar_t kDebuggerIntrinsicPropertiesPage[] =
    L"files/devtools/debugger_intrinsic_properties.html";
const wchar_t kCompletionOnPause[] =
    L"files/devtools/completion_on_pause.html";
const wchar_t kPageWithContentScript[] =
    L"files/devtools/page_with_content_script.html";

class DevToolsSanityTest : public InProcessBrowserTest {
 public:
  DevToolsSanityTest() {
    set_show_window(true);
    EnableDOMAutomation();
  }

 protected:
  void RunTest(const std::string& test_name, const std::wstring& test_page) {
    OpenDevToolsWindow(test_page);
    std::string result;

    // At first check that JavaScript part of the front-end is loaded by
    // checking that global variable uiTests exists(it's created after all js
    // files have been loaded) and has runTest method.
    ASSERT_TRUE(
        ui_test_utils::ExecuteJavaScriptAndExtractString(
            client_contents_->render_view_host(),
            L"",
            L"window.domAutomationController.send("
            L"'' + (window.uiTests && (typeof uiTests.runTest)));",
            &result));

    if (result == "function") {
      ASSERT_TRUE(
          ui_test_utils::ExecuteJavaScriptAndExtractString(
              client_contents_->render_view_host(),
              L"",
              UTF8ToWide(StringPrintf("uiTests.runTest('%s')",
                                      test_name.c_str())),
              &result));
      EXPECT_EQ("[OK]", result);
    } else {
      FAIL() << "DevTools front-end is broken.";
    }
    CloseDevToolsWindow();
  }

  void OpenDevToolsWindow(const std::wstring& test_page) {
    HTTPTestServer* server = StartHTTPServer();
    GURL url = server->TestServerPageW(test_page);
    ui_test_utils::NavigateToURL(browser(), url);

    inspected_rvh_ = GetInspectedTab()->render_view_host();
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    devtools_manager->OpenDevToolsWindow(inspected_rvh_);

    DevToolsClientHost* client_host =
        devtools_manager->GetDevToolsClientHostFor(inspected_rvh_);
    window_ = client_host->AsDevToolsWindow();
    RenderViewHost* client_rvh = window_->GetRenderViewHost();
    client_contents_ = client_rvh->delegate()->GetAsTabContents();
    ui_test_utils::WaitForNavigation(&client_contents_->controller());
  }

  TabContents* GetInspectedTab() {
    return browser()->GetTabContentsAt(0);
  }

  void CloseDevToolsWindow() {
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    // UnregisterDevToolsClientHostFor may destroy window_ so store the browser
    // first.
    Browser* browser = window_->browser();
    devtools_manager->UnregisterDevToolsClientHostFor(inspected_rvh_);
    BrowserClosedObserver close_observer(browser);
  }

  TabContents* client_contents_;
  DevToolsWindow* window_;
  RenderViewHost* inspected_rvh_;
};

class CancelableQuitTask : public Task {
 public:
  explicit CancelableQuitTask(const std::string& timeout_message)
      : timeout_message_(timeout_message),
        cancelled_(false) {
  }

  void cancel() {
    cancelled_ = true;
  }

  virtual void Run() {
    if (cancelled_) {
      return;
    }
    FAIL() << timeout_message_;
    MessageLoop::current()->Quit();
  }

 private:
  std::string timeout_message_;
  bool cancelled_;
};

// Base class for DevTools tests that test devtools functionality for
// extensions and content scripts.
class DevToolsExtensionDebugTest : public DevToolsSanityTest,
                                   public NotificationObserver {
 public:
  DevToolsExtensionDebugTest() : DevToolsSanityTest() {
    PathService::Get(chrome::DIR_TEST_DATA, &test_extensions_dir_);
    test_extensions_dir_ = test_extensions_dir_.AppendASCII("devtools");
    test_extensions_dir_ = test_extensions_dir_.AppendASCII("extensions");
  }

 protected:
  // Load an extention from test\data\devtools\extensions\<extension_name>
  void LoadExtension(const char* extension_name) {
    FilePath path = test_extensions_dir_.AppendASCII(extension_name);
    ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension.";
  }

 private:
  bool LoadExtensionFromPath(const FilePath& path) {
    ExtensionsService* service = browser()->profile()->GetExtensionsService();
    size_t num_before = service->extensions()->size();
    {
      NotificationRegistrar registrar;
      registrar.Add(this, NotificationType::EXTENSION_LOADED,
                    NotificationService::AllSources());
      CancelableQuitTask* delayed_quit =
          new CancelableQuitTask("Extension load timed out.");
      MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit,
          4*1000);
      service->LoadExtension(path);
      ui_test_utils::RunMessageLoop();
      delayed_quit->cancel();
    }
    size_t num_after = service->extensions()->size();
    if (num_after != (num_before + 1))
      return false;

    return WaitForExtensionHostsToLoad();
  }

  bool WaitForExtensionHostsToLoad() {
    // Wait for all the extension hosts that exist to finish loading.
    // NOTE: This assumes that the extension host list is not changing while
    // this method is running.

    NotificationRegistrar registrar;
    registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
                  NotificationService::AllSources());
    CancelableQuitTask* delayed_quit =
        new CancelableQuitTask("Extension host load timed out.");
    MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit,
        4*1000);

    ExtensionProcessManager* manager =
          browser()->profile()->GetExtensionProcessManager();
    for (ExtensionProcessManager::const_iterator iter = manager->begin();
         iter != manager->end();) {
      if ((*iter)->did_stop_loading())
        ++iter;
      else
        ui_test_utils::RunMessageLoop();
    }

    delayed_quit->cancel();
    return true;
  }

  void Observe(NotificationType type,
               const NotificationSource& source,
               const NotificationDetails& details) {
    switch (type.value) {
      case NotificationType::EXTENSION_LOADED:
      case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
        MessageLoopForUI::current()->Quit();
        break;
      default:
        NOTREACHED();
        break;
    }
  }

  FilePath test_extensions_dir_;
};

// WebInspector opens.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestHostIsPresent) {
  RunTest("testHostIsPresent", kSimplePage);
}

// Tests elements panel basics.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestElementsTreeRoot) {
  RunTest("testElementsTreeRoot", kSimplePage);
}

// Tests main resource load.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestMainResource) {
  RunTest("testMainResource", kSimplePage);
}

// Tests resources panel enabling.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEnableResourcesTab) {
  RunTest("testEnableResourcesTab", kSimplePage);
}

// Tests resources have correct sizes.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestResourceContentLength) {
  RunTest("testResourceContentLength", kResourceContentLengthTestPage);
}

// Tests resource headers.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestResourceHeaders) {
  RunTest("testResourceHeaders", kResourceTestPage);
}

// Tests cached resource mime type.
// @see http://crbug.com/27364
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestCachedResourceMimeType) {
  RunTest("testCachedResourceMimeType", kResourceTestPage);
}

// Tests profiler panel.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestProfilerTab) {
  RunTest("testProfilerTab", kJsPage);
}

// Tests scripts panel showing.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) {
  RunTest("testShowScriptsTab", kDebuggerTestPage);
}

// Tests that scripts tab is populated with inspected scripts even if it
// hadn't been shown by the moment inspected paged refreshed.
// @see http://crbug.com/26312
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
                       TestScriptsTabIsPopulatedOnInspectedPageRefresh) {
  // Reset inspector settings to defaults to ensure that Elements will be
  // current panel when DevTools window is open.
  GetInspectedTab()->render_view_host()->delegate()->UpdateInspectorSettings(
      WebPreferences().inspector_settings);
  RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh",
          kDebuggerTestPage);
}

// Tests that a content script is in the scripts list.
// This test is disabled, see bug 28961.
IN_PROC_BROWSER_TEST_F(DevToolsExtensionDebugTest,
                       TestContentScriptIsPresent) {
  LoadExtension("simple_content_script");
  RunTest("testContentScriptIsPresent", kPageWithContentScript);
}

// Tests that scripts are not duplicated after Scripts Panel switch.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
                       TestNoScriptDuplicatesOnPanelSwitch) {
  RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage);
}

// Tests set breakpoint.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestSetBreakpoint) {
  RunTest("testSetBreakpoint", kDebuggerTestPage);
}

// Tests pause on exception.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseOnException) {
  RunTest("testPauseOnException", kPauseOnExceptionTestPage);
}

// Tests that debugger works correctly if pause event occurs when DevTools
// frontend is being loaded.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenLoadingDevTools) {
  RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools);
}

// Tests that pressing 'Pause' will pause script execution if the script
// is already running.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenScriptIsRunning) {
  RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning);
}

// Tests eval on call frame.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEvalOnCallFrame) {
  RunTest("testEvalOnCallFrame", kDebuggerTestPage);
}

// Tests step over functionality in the debugger.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestStepOver) {
  RunTest("testStepOver", kDebuggerStepTestPage);
}

// Tests step out functionality in the debugger.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestStepOut) {
  RunTest("testStepOut", kDebuggerStepTestPage);
}

// Tests step in functionality in the debugger.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestStepIn) {
  RunTest("testStepIn", kDebuggerStepTestPage);
}

// Tests that scope can be expanded and contains expected variables.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestExpandScope) {
  RunTest("testExpandScope", kDebuggerClosurePage);
}

// Tests that intrinsic properties(__proto__, prototype, constructor) are
// present.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDebugIntrinsicProperties) {
  RunTest("testDebugIntrinsicProperties", kDebuggerIntrinsicPropertiesPage);
}

// Tests that execution continues automatically when there is a syntax error in
// script and DevTools are open.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestAutoContinueOnSyntaxError) {
  RunTest("testAutoContinueOnSyntaxError", kSyntaxErrorTestPage);
}

IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestCompletionOnPause) {
  RunTest("testCompletionOnPause", kCompletionOnPause);
}

// Tests that 'Pause' button works for eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestPauseInEval) {
  RunTest("testPauseInEval", kDebuggerTestPage);
}

// Tests console eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestConsoleEval) {
  RunTest("testConsoleEval", kConsoleTestPage);
}

// Tests console log.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestConsoleLog) {
  RunTest("testConsoleLog", kConsoleTestPage);
}

// Tests eval global values.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEvalGlobal) {
  RunTest("testEvalGlobal", kEvalTestPage);
}

// Test that Storage panel can be shown.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowStoragePanel) {
  RunTest("testShowStoragePanel", kDebuggerTestPage);
}

}  // namespace
<commit_msg>Revert enabled DevToolsSanityTest.TestConsoleLog test. Fails on Linux<commit_after>// Copyright (c) 2009 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/command_line.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"

namespace {

// Used to block until a dev tools client window's browser is closed.
class BrowserClosedObserver : public NotificationObserver {
 public:
  explicit BrowserClosedObserver(Browser* browser) {
    registrar_.Add(this, NotificationType::BROWSER_CLOSED,
                   Source<Browser>(browser));
    ui_test_utils::RunMessageLoop();
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    MessageLoopForUI::current()->Quit();
  }

 private:
  NotificationRegistrar registrar_;
  DISALLOW_COPY_AND_ASSIGN(BrowserClosedObserver);
};

// The delay waited in some cases where we don't have a notifications for an
// action we take.
const int kActionDelayMs = 500;

const wchar_t kConsoleTestPage[] = L"files/devtools/console_test_page.html";
const wchar_t kDebuggerTestPage[] = L"files/devtools/debugger_test_page.html";
const wchar_t kEvalTestPage[] = L"files/devtools/eval_test_page.html";
const wchar_t kJsPage[] = L"files/devtools/js_page.html";
const wchar_t kPauseOnExceptionTestPage[] =
    L"files/devtools/pause_on_exception.html";
const wchar_t kPauseWhenLoadingDevTools[] =
    L"files/devtools/pause_when_loading_devtools.html";
const wchar_t kPauseWhenScriptIsRunning[] =
    L"files/devtools/pause_when_script_is_running.html";
const wchar_t kResourceContentLengthTestPage[] = L"files/devtools/image.html";
const wchar_t kResourceTestPage[] = L"files/devtools/resource_test_page.html";
const wchar_t kSimplePage[] = L"files/devtools/simple_page.html";
const wchar_t kSyntaxErrorTestPage[] =
    L"files/devtools/script_syntax_error.html";
const wchar_t kDebuggerStepTestPage[] =
    L"files/devtools/debugger_step.html";
const wchar_t kDebuggerClosurePage[] =
    L"files/devtools/debugger_closure.html";
const wchar_t kDebuggerIntrinsicPropertiesPage[] =
    L"files/devtools/debugger_intrinsic_properties.html";
const wchar_t kCompletionOnPause[] =
    L"files/devtools/completion_on_pause.html";
const wchar_t kPageWithContentScript[] =
    L"files/devtools/page_with_content_script.html";

class DevToolsSanityTest : public InProcessBrowserTest {
 public:
  DevToolsSanityTest() {
    set_show_window(true);
    EnableDOMAutomation();
  }

 protected:
  void RunTest(const std::string& test_name, const std::wstring& test_page) {
    OpenDevToolsWindow(test_page);
    std::string result;

    // At first check that JavaScript part of the front-end is loaded by
    // checking that global variable uiTests exists(it's created after all js
    // files have been loaded) and has runTest method.
    ASSERT_TRUE(
        ui_test_utils::ExecuteJavaScriptAndExtractString(
            client_contents_->render_view_host(),
            L"",
            L"window.domAutomationController.send("
            L"'' + (window.uiTests && (typeof uiTests.runTest)));",
            &result));

    if (result == "function") {
      ASSERT_TRUE(
          ui_test_utils::ExecuteJavaScriptAndExtractString(
              client_contents_->render_view_host(),
              L"",
              UTF8ToWide(StringPrintf("uiTests.runTest('%s')",
                                      test_name.c_str())),
              &result));
      EXPECT_EQ("[OK]", result);
    } else {
      FAIL() << "DevTools front-end is broken.";
    }
    CloseDevToolsWindow();
  }

  void OpenDevToolsWindow(const std::wstring& test_page) {
    HTTPTestServer* server = StartHTTPServer();
    GURL url = server->TestServerPageW(test_page);
    ui_test_utils::NavigateToURL(browser(), url);

    inspected_rvh_ = GetInspectedTab()->render_view_host();
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    devtools_manager->OpenDevToolsWindow(inspected_rvh_);

    DevToolsClientHost* client_host =
        devtools_manager->GetDevToolsClientHostFor(inspected_rvh_);
    window_ = client_host->AsDevToolsWindow();
    RenderViewHost* client_rvh = window_->GetRenderViewHost();
    client_contents_ = client_rvh->delegate()->GetAsTabContents();
    ui_test_utils::WaitForNavigation(&client_contents_->controller());
  }

  TabContents* GetInspectedTab() {
    return browser()->GetTabContentsAt(0);
  }

  void CloseDevToolsWindow() {
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    // UnregisterDevToolsClientHostFor may destroy window_ so store the browser
    // first.
    Browser* browser = window_->browser();
    devtools_manager->UnregisterDevToolsClientHostFor(inspected_rvh_);
    BrowserClosedObserver close_observer(browser);
  }

  TabContents* client_contents_;
  DevToolsWindow* window_;
  RenderViewHost* inspected_rvh_;
};

class CancelableQuitTask : public Task {
 public:
  explicit CancelableQuitTask(const std::string& timeout_message)
      : timeout_message_(timeout_message),
        cancelled_(false) {
  }

  void cancel() {
    cancelled_ = true;
  }

  virtual void Run() {
    if (cancelled_) {
      return;
    }
    FAIL() << timeout_message_;
    MessageLoop::current()->Quit();
  }

 private:
  std::string timeout_message_;
  bool cancelled_;
};

// Base class for DevTools tests that test devtools functionality for
// extensions and content scripts.
class DevToolsExtensionDebugTest : public DevToolsSanityTest,
                                   public NotificationObserver {
 public:
  DevToolsExtensionDebugTest() : DevToolsSanityTest() {
    PathService::Get(chrome::DIR_TEST_DATA, &test_extensions_dir_);
    test_extensions_dir_ = test_extensions_dir_.AppendASCII("devtools");
    test_extensions_dir_ = test_extensions_dir_.AppendASCII("extensions");
  }

 protected:
  // Load an extention from test\data\devtools\extensions\<extension_name>
  void LoadExtension(const char* extension_name) {
    FilePath path = test_extensions_dir_.AppendASCII(extension_name);
    ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension.";
  }

 private:
  bool LoadExtensionFromPath(const FilePath& path) {
    ExtensionsService* service = browser()->profile()->GetExtensionsService();
    size_t num_before = service->extensions()->size();
    {
      NotificationRegistrar registrar;
      registrar.Add(this, NotificationType::EXTENSION_LOADED,
                    NotificationService::AllSources());
      CancelableQuitTask* delayed_quit =
          new CancelableQuitTask("Extension load timed out.");
      MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit,
          4*1000);
      service->LoadExtension(path);
      ui_test_utils::RunMessageLoop();
      delayed_quit->cancel();
    }
    size_t num_after = service->extensions()->size();
    if (num_after != (num_before + 1))
      return false;

    return WaitForExtensionHostsToLoad();
  }

  bool WaitForExtensionHostsToLoad() {
    // Wait for all the extension hosts that exist to finish loading.
    // NOTE: This assumes that the extension host list is not changing while
    // this method is running.

    NotificationRegistrar registrar;
    registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
                  NotificationService::AllSources());
    CancelableQuitTask* delayed_quit =
        new CancelableQuitTask("Extension host load timed out.");
    MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit,
        4*1000);

    ExtensionProcessManager* manager =
          browser()->profile()->GetExtensionProcessManager();
    for (ExtensionProcessManager::const_iterator iter = manager->begin();
         iter != manager->end();) {
      if ((*iter)->did_stop_loading())
        ++iter;
      else
        ui_test_utils::RunMessageLoop();
    }

    delayed_quit->cancel();
    return true;
  }

  void Observe(NotificationType type,
               const NotificationSource& source,
               const NotificationDetails& details) {
    switch (type.value) {
      case NotificationType::EXTENSION_LOADED:
      case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
        MessageLoopForUI::current()->Quit();
        break;
      default:
        NOTREACHED();
        break;
    }
  }

  FilePath test_extensions_dir_;
};

// WebInspector opens.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestHostIsPresent) {
  RunTest("testHostIsPresent", kSimplePage);
}

// Tests elements panel basics.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestElementsTreeRoot) {
  RunTest("testElementsTreeRoot", kSimplePage);
}

// Tests main resource load.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestMainResource) {
  RunTest("testMainResource", kSimplePage);
}

// Tests resources panel enabling.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEnableResourcesTab) {
  RunTest("testEnableResourcesTab", kSimplePage);
}

// Tests resources have correct sizes.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestResourceContentLength) {
  RunTest("testResourceContentLength", kResourceContentLengthTestPage);
}

// Tests resource headers.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestResourceHeaders) {
  RunTest("testResourceHeaders", kResourceTestPage);
}

// Tests cached resource mime type.
// @see http://crbug.com/27364
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestCachedResourceMimeType) {
  RunTest("testCachedResourceMimeType", kResourceTestPage);
}

// Tests profiler panel.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestProfilerTab) {
  RunTest("testProfilerTab", kJsPage);
}

// Tests scripts panel showing.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) {
  RunTest("testShowScriptsTab", kDebuggerTestPage);
}

// Tests that scripts tab is populated with inspected scripts even if it
// hadn't been shown by the moment inspected paged refreshed.
// @see http://crbug.com/26312
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
                       TestScriptsTabIsPopulatedOnInspectedPageRefresh) {
  // Reset inspector settings to defaults to ensure that Elements will be
  // current panel when DevTools window is open.
  GetInspectedTab()->render_view_host()->delegate()->UpdateInspectorSettings(
      WebPreferences().inspector_settings);
  RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh",
          kDebuggerTestPage);
}

// Tests that a content script is in the scripts list.
// This test is disabled, see bug 28961.
IN_PROC_BROWSER_TEST_F(DevToolsExtensionDebugTest,
                       TestContentScriptIsPresent) {
  LoadExtension("simple_content_script");
  RunTest("testContentScriptIsPresent", kPageWithContentScript);
}

// Tests that scripts are not duplicated after Scripts Panel switch.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
                       TestNoScriptDuplicatesOnPanelSwitch) {
  RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage);
}

// Tests set breakpoint.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestSetBreakpoint) {
  RunTest("testSetBreakpoint", kDebuggerTestPage);
}

// Tests pause on exception.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseOnException) {
  RunTest("testPauseOnException", kPauseOnExceptionTestPage);
}

// Tests that debugger works correctly if pause event occurs when DevTools
// frontend is being loaded.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenLoadingDevTools) {
  RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools);
}

// Tests that pressing 'Pause' will pause script execution if the script
// is already running.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenScriptIsRunning) {
  RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning);
}

// Tests eval on call frame.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEvalOnCallFrame) {
  RunTest("testEvalOnCallFrame", kDebuggerTestPage);
}

// Tests step over functionality in the debugger.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestStepOver) {
  RunTest("testStepOver", kDebuggerStepTestPage);
}

// Tests step out functionality in the debugger.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestStepOut) {
  RunTest("testStepOut", kDebuggerStepTestPage);
}

// Tests step in functionality in the debugger.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestStepIn) {
  RunTest("testStepIn", kDebuggerStepTestPage);
}

// Tests that scope can be expanded and contains expected variables.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestExpandScope) {
  RunTest("testExpandScope", kDebuggerClosurePage);
}

// Tests that intrinsic properties(__proto__, prototype, constructor) are
// present.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDebugIntrinsicProperties) {
  RunTest("testDebugIntrinsicProperties", kDebuggerIntrinsicPropertiesPage);
}

// Tests that execution continues automatically when there is a syntax error in
// script and DevTools are open.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestAutoContinueOnSyntaxError) {
  RunTest("testAutoContinueOnSyntaxError", kSyntaxErrorTestPage);
}

IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestCompletionOnPause) {
  RunTest("testCompletionOnPause", kCompletionOnPause);
}

// Tests that 'Pause' button works for eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestPauseInEval) {
  RunTest("testPauseInEval", kDebuggerTestPage);
}

// Tests console eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestConsoleEval) {
  RunTest("testConsoleEval", kConsoleTestPage);
}

// Tests console log.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestConsoleLog) {
  RunTest("testConsoleLog", kConsoleTestPage);
}

// Tests eval global values.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEvalGlobal) {
  RunTest("testEvalGlobal", kEvalTestPage);
}

// Test that Storage panel can be shown.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowStoragePanel) {
  RunTest("testShowStoragePanel", kDebuggerTestPage);
}

}  // namespace
<|endoftext|>


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

#include "chrome/browser/extensions/extension_apitest.h"
#include "net/base/mock_host_resolver.h"

class ExecuteScriptApiTest : public ExtensionApiTest {
 protected:
  void SetupDelayedHostResolver() {
    // We need a.com to be a little bit slow to trigger a race condition.
    host_resolver()->AddRuleWithLatency("a.com", "127.0.0.1", 500);
    host_resolver()->AddRule("b.com", "127.0.0.1");
    host_resolver()->AddRule("c.com", "127.0.0.1");
  }
};

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptBasic) {
  SetupDelayedHostResolver();
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/basic")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptInFrame) {
  SetupDelayedHostResolver();
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/in_frame")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptPermissions) {
  SetupDelayedHostResolver();
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/permissions")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptFileAfterClose) {
  host_resolver()->AddRule("b.com", "127.0.0.1");
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/file_after_close")) << message_;
}

// Crashy, http://crbug.com/67774.
IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest,
                       DISABLED_ExecuteScriptFragmentNavigation) {
  ASSERT_TRUE(StartTestServer());
  const char* extension_name = "executescript/fragment";
  ASSERT_TRUE(RunExtensionTest(extension_name)) << message_;
}

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, NavigationRace) {
  host_resolver()->AddRule("a.com", "127.0.0.1");
  host_resolver()->AddRule("b.com", "127.0.0.1");
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionSubtest("executescript/navigation_race",
                                  "execute_script.html")) << message_;
  ASSERT_TRUE(RunExtensionSubtest("executescript/navigation_race",
                                  "javascript_url.html")) << message_;
}
<commit_msg>Disable ExecuteScriptApiTest.NavigationRace on Linux<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension_apitest.h"
#include "net/base/mock_host_resolver.h"

class ExecuteScriptApiTest : public ExtensionApiTest {
 protected:
  void SetupDelayedHostResolver() {
    // We need a.com to be a little bit slow to trigger a race condition.
    host_resolver()->AddRuleWithLatency("a.com", "127.0.0.1", 500);
    host_resolver()->AddRule("b.com", "127.0.0.1");
    host_resolver()->AddRule("c.com", "127.0.0.1");
  }
};

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptBasic) {
  SetupDelayedHostResolver();
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/basic")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptInFrame) {
  SetupDelayedHostResolver();
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/in_frame")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptPermissions) {
  SetupDelayedHostResolver();
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/permissions")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptFileAfterClose) {
  host_resolver()->AddRule("b.com", "127.0.0.1");
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionTest("executescript/file_after_close")) << message_;
}

// Crashy, http://crbug.com/67774.
IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest,
                       DISABLED_ExecuteScriptFragmentNavigation) {
  ASSERT_TRUE(StartTestServer());
  const char* extension_name = "executescript/fragment";
  ASSERT_TRUE(RunExtensionTest(extension_name)) << message_;
}

#if defined(OS_LINUX)
// Flakily times out: http://crbug.com/78802
#define MAYBE_NavigationRace DISABLED_NavigationRace
#else
#define MAYBE_NavigationRace NavigationRace
#endif
IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, MAYBE_NavigationRace) {
  host_resolver()->AddRule("a.com", "127.0.0.1");
  host_resolver()->AddRule("b.com", "127.0.0.1");
  ASSERT_TRUE(StartTestServer());
  ASSERT_TRUE(RunExtensionSubtest("executescript/navigation_race",
                                  "execute_script.html")) << message_;
  ASSERT_TRUE(RunExtensionSubtest("executescript/navigation_race",
                                  "javascript_url.html")) << 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.

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

#include "chrome/browser/browser.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"

// Tabs is flaky on chromeos and linux views debug build.
// http://crbug.com/48920
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) && !defined(NDEBUG)
#define MAYBE_Tabs FLAKY_Tabs
#else
#define MAYBE_Tabs Tabs
#endif

// TabOnRemoved is flaky on chromeos and linux views debug build.
// http://crbug.com/49258
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) && !defined(NDEBUG)
#define MAYBE_TabOnRemoved FLAKY_TabOnRemoved
#else
#define MAYBE_TabOnRemoved TabOnRemoved
#endif

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) {
  ASSERT_TRUE(StartHTTPServer());

  // The test creates a tab and checks that the URL of the new tab
  // is that of the new tab page.  Make sure the pref that controls
  // this is set.
  browser()->profile()->GetPrefs()->SetBoolean(
      prefs::kHomePageIsNewTabPage, true);

  ASSERT_TRUE(RunExtensionTest("tabs/basics")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabGetCurrent) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/get_current")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabConnect) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/connect")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_TabOnRemoved) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/on_removed")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CaptureVisibleTabJpeg) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_jpeg.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CaptureVisibleTabPng) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_png.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabsOnUpdated) {
  ASSERT_TRUE(StartHTTPServer());

  ASSERT_TRUE(RunExtensionTest("tabs/on_updated")) << message_;
}
<commit_msg>Suppress ExtensionApiTest.CaptureVisibleTabPng<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_apitest.h"

#include "chrome/browser/browser.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"

// Tabs is flaky on chromeos and linux views debug build.
// http://crbug.com/48920
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) && !defined(NDEBUG)
#define MAYBE_Tabs FLAKY_Tabs
#else
#define MAYBE_Tabs Tabs
#endif

// TabOnRemoved is flaky on chromeos and linux views debug build.
// http://crbug.com/49258
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) && !defined(NDEBUG)
#define MAYBE_TabOnRemoved FLAKY_TabOnRemoved
#else
#define MAYBE_TabOnRemoved TabOnRemoved
#endif

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) {
  ASSERT_TRUE(StartHTTPServer());

  // The test creates a tab and checks that the URL of the new tab
  // is that of the new tab page.  Make sure the pref that controls
  // this is set.
  browser()->profile()->GetPrefs()->SetBoolean(
      prefs::kHomePageIsNewTabPage, true);

  ASSERT_TRUE(RunExtensionTest("tabs/basics")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabGetCurrent) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/get_current")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabConnect) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/connect")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_TabOnRemoved) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/on_removed")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CaptureVisibleTabJpeg) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_jpeg.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FAILS_CaptureVisibleTabPng) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_png.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabsOnUpdated) {
  ASSERT_TRUE(StartHTTPServer());

  ASSERT_TRUE(RunExtensionTest("tabs/on_updated")) << message_;
}
<|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/protector/protected_prefs_watcher.h"

#include "base/base64.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/pref_set_observer.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/protector/histograms.h"
#include "chrome/browser/protector/protector_utils.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"

namespace protector {

namespace {

// Prefix added to names of backup entries.
const char kBackupPrefsPrefix[] = "backup.";

// Names of prefs that are backed up.
const char* const kProtectedPrefNames[] = {
  prefs::kHomePage,
  prefs::kHomePageIsNewTabPage,
  prefs::kShowHomeButton,
  prefs::kRestoreOnStartup,
  prefs::kURLsToRestoreOnStartup,
  prefs::kPinnedTabs
};

// Backup pref names.
const char kBackupHomePage[] = "backup.homepage";
const char kBackupHomePageIsNewTabPage[] = "backup.homepage_is_newtabpage";
const char kBackupShowHomeButton[] = "backup.browser.show_home_button";
const char kBackupRestoreOnStartup[] = "backup.session.restore_on_startup";
const char kBackupURLsToRestoreOnStartup[] =
    "backup.session.urls_to_restore_on_startup";
const char kBackupPinnedTabs[] = "backup.pinned_tabs";

// Special backup entries.
const char kBackupExtensionsIDs[] = "backup.extensions.ids";
const char kBackupSignature[] = "backup._signature";
const char kBackupVersion[] = "backup._version";

// Returns name of the backup entry for pref |pref_name|.
std::string GetBackupNameFor(const std::string& pref_name) {
  return kBackupPrefsPrefix + pref_name;
}

// Appends a list of strings to |out|.
void StringAppendStringList(const base::ListValue* list, std::string* out) {
  for (base::ListValue::const_iterator it = list->begin(); it != list->end();
       ++it) {
    std::string item;
    if (!(*it)->GetAsString(&item))
      NOTREACHED();
    base::StringAppendF(out, "|%s", item.c_str());
  }
}

// Appends a dictionary with string values to |out|.
void StringAppendStringDictionary(const base::DictionaryValue* dict,
                                  std::string* out) {
  for (base::DictionaryValue::Iterator it(*dict); it.HasNext(); it.Advance()) {
    std::string value;
    if (!it.value().GetAsString(&value))
      NOTREACHED();
    base::StringAppendF(out, "|%s|%s", it.key().c_str(), value.c_str());
  }
}

void StringAppendBoolean(PrefService* prefs,
                         const char* path,
                         std::string* out) {
  if (prefs->HasPrefPath(path))
    base::StringAppendF(out, "|%d", prefs->GetBoolean(path) ? 1 : 0);
  else
    base::StringAppendF(out, "|");
}

void StringAppendInteger(PrefService* prefs,
                         const char* path,
                         std::string* out) {
  if (prefs->HasPrefPath(path))
    base::StringAppendF(out, "|%d", prefs->GetInteger(path));
  else
    base::StringAppendF(out, "|");
}

}  // namespace

// static
const int ProtectedPrefsWatcher::kCurrentVersionNumber = 4;

ProtectedPrefsWatcher::ProtectedPrefsWatcher(Profile* profile)
    : is_backup_valid_(true),
      profile_(profile) {
  // Perform necessary pref migrations before actually starting to observe
  // pref changes, otherwise the migration would affect the backup data as well.
  EnsurePrefsMigration();
  pref_observer_.reset(PrefSetObserver::CreateProtectedPrefSetObserver(
      profile->GetPrefs(), this));
  UpdateCachedPrefs();
  ValidateBackup();
  VLOG(1) << "Initialized pref watcher";
}

ProtectedPrefsWatcher::~ProtectedPrefsWatcher() {
}

// static
void ProtectedPrefsWatcher::RegisterUserPrefs(PrefService* prefs) {
  prefs->RegisterStringPref(kBackupHomePage, "",
                            PrefService::UNSYNCABLE_PREF);
  prefs->RegisterBooleanPref(kBackupHomePageIsNewTabPage, false,
                             PrefService::UNSYNCABLE_PREF);
  prefs->RegisterBooleanPref(kBackupShowHomeButton, false,
                             PrefService::UNSYNCABLE_PREF);
  prefs->RegisterIntegerPref(kBackupRestoreOnStartup, 0,
                             PrefService::UNSYNCABLE_PREF);
  prefs->RegisterListPref(kBackupURLsToRestoreOnStartup,
                          PrefService::UNSYNCABLE_PREF);
  prefs->RegisterListPref(kBackupPinnedTabs,
                          PrefService::UNSYNCABLE_PREF);
  prefs->RegisterListPref(kBackupExtensionsIDs,
                          PrefService::UNSYNCABLE_PREF);
  prefs->RegisterStringPref(kBackupSignature, "",
                            PrefService::UNSYNCABLE_PREF);
  prefs->RegisterIntegerPref(kBackupVersion, 1,
                             PrefService::UNSYNCABLE_PREF);
}

bool ProtectedPrefsWatcher::DidPrefChange(const std::string& path) const {
  std::string backup_path = GetBackupNameFor(path);
  PrefService* prefs = profile_->GetPrefs();
  const PrefService::Preference* new_pref = prefs->FindPreference(path.c_str());
  DCHECK(new_pref);
  const PrefService::Preference* backup_pref =
      profile_->GetPrefs()->FindPreference(backup_path.c_str());
  DCHECK(backup_pref);
  if (new_pref->IsDefaultValue())
    return !backup_pref->IsDefaultValue();
  if (!new_pref->IsUserControlled())
    return false;
  return !backup_pref->GetValue()->Equals(new_pref->GetValue());
}

const base::Value* ProtectedPrefsWatcher::GetBackupForPref(
    const std::string& path) const {
  if (!is_backup_valid_)
    return NULL;
  std::string backup_path = GetBackupNameFor(path);
  // These do not directly correspond to any real preference.
  DCHECK(backup_path != kBackupExtensionsIDs &&
         backup_path != kBackupSignature);
  PrefService* prefs = profile_->GetPrefs();
  // If backup is not set, return the default value of the actual pref.
  // TODO(ivankr): return NULL instead and handle appropriately in SettingChange
  // classes.
  if (!prefs->HasPrefPath(backup_path.c_str()))
    return prefs->GetDefaultPrefValue(path.c_str());
  const PrefService::Preference* backup_pref =
      profile_->GetPrefs()->FindPreference(backup_path.c_str());
  DCHECK(backup_pref);
  return backup_pref->GetValue();
}

void ProtectedPrefsWatcher::ForceUpdateBackup() {
  UMA_HISTOGRAM_ENUMERATION(
      kProtectorHistogramPrefs,
      kProtectorErrorForcedUpdate,
      kProtectorErrorCount);
  InitBackup();
}

void ProtectedPrefsWatcher::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  DCHECK(type == chrome::NOTIFICATION_PREF_CHANGED);
  const std::string* pref_name = content::Details<std::string>(details).ptr();
  DCHECK(pref_name && pref_observer_->IsObserved(*pref_name));
  if (UpdateBackupEntry(*pref_name))
    UpdateBackupSignature();
}

void ProtectedPrefsWatcher::EnsurePrefsMigration() {
  SessionStartupPref::MigrateIfNecessary(profile_->GetPrefs());
}

bool ProtectedPrefsWatcher::UpdateCachedPrefs() {
  // Direct access to the extensions prefs is required becase ExtensionService
  // may not yet have been initialized.
  const base::DictionaryValue* extension_prefs;
  const base::Value* extension_prefs_value =
      profile_->GetPrefs()->GetUserPrefValue(ExtensionPrefs::kExtensionsPref);
  if (!extension_prefs_value ||
      !extension_prefs_value->GetAsDictionary(&extension_prefs)) {
    return false;
  }
  ExtensionPrefs::ExtensionIdSet extension_ids =
      ExtensionPrefs::GetExtensionsFrom(extension_prefs);
  if (extension_ids == cached_extension_ids_)
    return false;
  cached_extension_ids_.swap(extension_ids);
  return true;
}

bool ProtectedPrefsWatcher::HasBackup() const {
  // TODO(ivankr): as soon as some irreversible change to Preferences happens,
  // add a condition that this change has occured as well (otherwise it's
  // possible to simply clear the "backup" dictionary to make settings
  // unprotected).
  return profile_->GetPrefs()->HasPrefPath(kBackupSignature);
}

void ProtectedPrefsWatcher::InitBackup() {
  PrefService* prefs = profile_->GetPrefs();
  for (size_t i = 0; i < arraysize(kProtectedPrefNames); ++i) {
    if (prefs->HasPrefPath(kProtectedPrefNames[i])) {
      prefs->Set(GetBackupNameFor(kProtectedPrefNames[i]).c_str(),
                 *prefs->GetUserPrefValue(kProtectedPrefNames[i]));
    }
  }
  ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs);
  base::ListValue* extension_ids = extension_ids_update.Get();
  extension_ids->Clear();
  for (ExtensionPrefs::ExtensionIdSet::const_iterator it =
           cached_extension_ids_.begin();
       it != cached_extension_ids_.end(); ++it) {
    extension_ids->Append(base::Value::CreateStringValue(*it));
  }
  prefs->SetInteger(kBackupVersion, kCurrentVersionNumber);
  UpdateBackupSignature();
}

void ProtectedPrefsWatcher::MigrateOldBackupIfNeeded() {
  PrefService* prefs = profile_->GetPrefs();

  int current_version = prefs->GetInteger(kBackupVersion);
  VLOG(1) << "Backup version: " << current_version;
  if (current_version == kCurrentVersionNumber)
    return;

  switch (current_version) {
    case 1:
      // Add pinned tabs backup.
      prefs->Set(kBackupPinnedTabs,
                 *prefs->GetUserPrefValue(prefs::kPinnedTabs));
      // FALL THROUGH

    case 2:
      // SessionStartupPref migration.
      DCHECK(prefs->GetBoolean(prefs::kRestoreOnStartupMigrated));
      prefs->SetInteger(kBackupRestoreOnStartup,
                        prefs->GetInteger(prefs::kRestoreOnStartup));
      prefs->Set(kBackupURLsToRestoreOnStartup,
                 *prefs->GetList(prefs::kURLsToRestoreOnStartup));
      // FALL THROUGH

    case 3:
      // Reset to default values backup prefs whose actual prefs are not set.
      for (size_t i = 0; i < arraysize(kProtectedPrefNames); ++i) {
        if (!prefs->HasPrefPath(kProtectedPrefNames[i]))
          prefs->ClearPref(GetBackupNameFor(kProtectedPrefNames[i]).c_str());
      }
      // FALL THROUGH
  }

  prefs->SetInteger(kBackupVersion, kCurrentVersionNumber);
  UpdateBackupSignature();
}

bool ProtectedPrefsWatcher::UpdateBackupEntry(const std::string& path) {
  std::string backup_path = GetBackupNameFor(path);
  PrefService* prefs = profile_->GetPrefs();
  const PrefService::Preference* pref = prefs->FindPreference(path.c_str());
  if (path == ExtensionPrefs::kExtensionsPref) {
    // For changes in extension dictionary, do nothing if the IDs list remained
    // the same.
    if (!UpdateCachedPrefs())
      return false;
    ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs);
    base::ListValue* extension_ids = extension_ids_update.Get();
    extension_ids->Clear();
    for (ExtensionPrefs::ExtensionIdSet::const_iterator it =
             cached_extension_ids_.begin();
         it != cached_extension_ids_.end(); ++it) {
      extension_ids->Append(base::Value::CreateStringValue(*it));
    }
  } else if (!prefs->HasPrefPath(path.c_str())) {
    // Preference has been removed, remove the backup as well.
    prefs->ClearPref(backup_path.c_str());
  } else if (!pref->IsUserControlled()) {
    return false;
  } else {
    prefs->Set(backup_path.c_str(), *pref->GetValue());
  }
  VLOG(1) << "Updated backup entry for: " << path;
  return true;
}

void ProtectedPrefsWatcher::UpdateBackupSignature() {
  PrefService* prefs = profile_->GetPrefs();
  std::string signed_data = GetSignatureData(prefs);
  DCHECK(!signed_data.empty());
  std::string signature = SignSetting(signed_data);
  DCHECK(!signature.empty());
  std::string signature_base64;
  if (!base::Base64Encode(signature, &signature_base64))
    NOTREACHED();
  prefs->SetString(kBackupSignature, signature_base64);
  // Schedule disk write on FILE thread as soon as possible.
  prefs->CommitPendingWrite();
  VLOG(1) << "Updated backup signature";
}

bool ProtectedPrefsWatcher::IsSignatureValid() const {
  DCHECK(HasBackup());
  PrefService* prefs = profile_->GetPrefs();
  std::string signed_data = GetSignatureData(prefs);
  DCHECK(!signed_data.empty());
  std::string signature;
  if (!base::Base64Decode(prefs->GetString(kBackupSignature), &signature))
    return false;
  return IsSettingValid(signed_data, signature);
}

void ProtectedPrefsWatcher::ValidateBackup() {
  if (!HasBackup()) {
    // Create initial backup entries and sign them.
    InitBackup();
    UMA_HISTOGRAM_ENUMERATION(
        kProtectorHistogramPrefs,
        kProtectorErrorValueValidZero,
        kProtectorErrorCount);
  } else if (IsSignatureValid()) {
    MigrateOldBackupIfNeeded();
    UMA_HISTOGRAM_ENUMERATION(
        kProtectorHistogramPrefs,
        kProtectorErrorValueValid,
        kProtectorErrorCount);
  } else {
    LOG(WARNING) << "Invalid backup signature";
    is_backup_valid_ = false;
    // The whole backup has been compromised, overwrite it.
    InitBackup();
    UMA_HISTOGRAM_ENUMERATION(
        kProtectorHistogramPrefs,
        kProtectorErrorBackupInvalid,
        kProtectorErrorCount);
  }
}

std::string ProtectedPrefsWatcher::GetSignatureData(PrefService* prefs) const {
  int current_version = prefs->GetInteger(kBackupVersion);
  // TODO(ivankr): replace this with some existing reliable serializer.
  // JSONWriter isn't a good choice because JSON formatting may change suddenly.
  std::string data = prefs->GetString(kBackupHomePage);
  StringAppendBoolean(prefs, kBackupHomePageIsNewTabPage, &data);
  StringAppendBoolean(prefs, kBackupShowHomeButton, &data);
  StringAppendInteger(prefs, kBackupRestoreOnStartup, &data);
  StringAppendStringList(prefs->GetList(kBackupURLsToRestoreOnStartup), &data);
  StringAppendStringList(prefs->GetList(kBackupExtensionsIDs), &data);
  if (current_version >= 2) {
    // Version itself is included only since version 2 since it wasn't there
    // in version 1.
    base::StringAppendF(&data, "|v%d", current_version);
    const base::ListValue* pinned_tabs = prefs->GetList(kBackupPinnedTabs);
    for (base::ListValue::const_iterator it = pinned_tabs->begin();
         it != pinned_tabs->end(); ++it) {
      const base::DictionaryValue* tab = NULL;
      if (!(*it)->GetAsDictionary(&tab)) {
        NOTREACHED();
        continue;
      }
      StringAppendStringDictionary(tab, &data);
    }
  }
  return data;
}

}  // namespace protector
<commit_msg>[protector] Fix segfault when preference is policy-controlled.<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/protector/protected_prefs_watcher.h"

#include "base/base64.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/pref_set_observer.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/protector/histograms.h"
#include "chrome/browser/protector/protector_utils.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"

namespace protector {

namespace {

// Prefix added to names of backup entries.
const char kBackupPrefsPrefix[] = "backup.";

// Names of prefs that are backed up.
const char* const kProtectedPrefNames[] = {
  prefs::kHomePage,
  prefs::kHomePageIsNewTabPage,
  prefs::kShowHomeButton,
  prefs::kRestoreOnStartup,
  prefs::kURLsToRestoreOnStartup,
  prefs::kPinnedTabs
};

// Backup pref names.
const char kBackupHomePage[] = "backup.homepage";
const char kBackupHomePageIsNewTabPage[] = "backup.homepage_is_newtabpage";
const char kBackupShowHomeButton[] = "backup.browser.show_home_button";
const char kBackupRestoreOnStartup[] = "backup.session.restore_on_startup";
const char kBackupURLsToRestoreOnStartup[] =
    "backup.session.urls_to_restore_on_startup";
const char kBackupPinnedTabs[] = "backup.pinned_tabs";

// Special backup entries.
const char kBackupExtensionsIDs[] = "backup.extensions.ids";
const char kBackupSignature[] = "backup._signature";
const char kBackupVersion[] = "backup._version";

// Returns name of the backup entry for pref |pref_name|.
std::string GetBackupNameFor(const std::string& pref_name) {
  return kBackupPrefsPrefix + pref_name;
}

// Appends a list of strings to |out|.
void StringAppendStringList(const base::ListValue* list, std::string* out) {
  for (base::ListValue::const_iterator it = list->begin(); it != list->end();
       ++it) {
    std::string item;
    if (!(*it)->GetAsString(&item))
      NOTREACHED();
    base::StringAppendF(out, "|%s", item.c_str());
  }
}

// Appends a dictionary with string values to |out|.
void StringAppendStringDictionary(const base::DictionaryValue* dict,
                                  std::string* out) {
  for (base::DictionaryValue::Iterator it(*dict); it.HasNext(); it.Advance()) {
    std::string value;
    if (!it.value().GetAsString(&value))
      NOTREACHED();
    base::StringAppendF(out, "|%s|%s", it.key().c_str(), value.c_str());
  }
}

void StringAppendBoolean(PrefService* prefs,
                         const char* path,
                         std::string* out) {
  if (prefs->HasPrefPath(path))
    base::StringAppendF(out, "|%d", prefs->GetBoolean(path) ? 1 : 0);
  else
    base::StringAppendF(out, "|");
}

void StringAppendInteger(PrefService* prefs,
                         const char* path,
                         std::string* out) {
  if (prefs->HasPrefPath(path))
    base::StringAppendF(out, "|%d", prefs->GetInteger(path));
  else
    base::StringAppendF(out, "|");
}

}  // namespace

// static
const int ProtectedPrefsWatcher::kCurrentVersionNumber = 4;

ProtectedPrefsWatcher::ProtectedPrefsWatcher(Profile* profile)
    : is_backup_valid_(true),
      profile_(profile) {
  // Perform necessary pref migrations before actually starting to observe
  // pref changes, otherwise the migration would affect the backup data as well.
  EnsurePrefsMigration();
  pref_observer_.reset(PrefSetObserver::CreateProtectedPrefSetObserver(
      profile->GetPrefs(), this));
  UpdateCachedPrefs();
  ValidateBackup();
  VLOG(1) << "Initialized pref watcher";
}

ProtectedPrefsWatcher::~ProtectedPrefsWatcher() {
}

// static
void ProtectedPrefsWatcher::RegisterUserPrefs(PrefService* prefs) {
  prefs->RegisterStringPref(kBackupHomePage, "",
                            PrefService::UNSYNCABLE_PREF);
  prefs->RegisterBooleanPref(kBackupHomePageIsNewTabPage, false,
                             PrefService::UNSYNCABLE_PREF);
  prefs->RegisterBooleanPref(kBackupShowHomeButton, false,
                             PrefService::UNSYNCABLE_PREF);
  prefs->RegisterIntegerPref(kBackupRestoreOnStartup, 0,
                             PrefService::UNSYNCABLE_PREF);
  prefs->RegisterListPref(kBackupURLsToRestoreOnStartup,
                          PrefService::UNSYNCABLE_PREF);
  prefs->RegisterListPref(kBackupPinnedTabs,
                          PrefService::UNSYNCABLE_PREF);
  prefs->RegisterListPref(kBackupExtensionsIDs,
                          PrefService::UNSYNCABLE_PREF);
  prefs->RegisterStringPref(kBackupSignature, "",
                            PrefService::UNSYNCABLE_PREF);
  prefs->RegisterIntegerPref(kBackupVersion, 1,
                             PrefService::UNSYNCABLE_PREF);
}

bool ProtectedPrefsWatcher::DidPrefChange(const std::string& path) const {
  std::string backup_path = GetBackupNameFor(path);
  PrefService* prefs = profile_->GetPrefs();
  const PrefService::Preference* new_pref = prefs->FindPreference(path.c_str());
  DCHECK(new_pref);
  const PrefService::Preference* backup_pref =
      profile_->GetPrefs()->FindPreference(backup_path.c_str());
  DCHECK(backup_pref);
  if (new_pref->IsDefaultValue())
    return !backup_pref->IsDefaultValue();
  if (!new_pref->IsUserControlled())
    return false;
  return !backup_pref->GetValue()->Equals(new_pref->GetValue());
}

const base::Value* ProtectedPrefsWatcher::GetBackupForPref(
    const std::string& path) const {
  if (!is_backup_valid_)
    return NULL;
  std::string backup_path = GetBackupNameFor(path);
  // These do not directly correspond to any real preference.
  DCHECK(backup_path != kBackupExtensionsIDs &&
         backup_path != kBackupSignature);
  PrefService* prefs = profile_->GetPrefs();
  // If backup is not set, return the default value of the actual pref.
  // TODO(ivankr): return NULL instead and handle appropriately in SettingChange
  // classes.
  if (!prefs->HasPrefPath(backup_path.c_str()))
    return prefs->GetDefaultPrefValue(path.c_str());
  const PrefService::Preference* backup_pref =
      profile_->GetPrefs()->FindPreference(backup_path.c_str());
  DCHECK(backup_pref);
  return backup_pref->GetValue();
}

void ProtectedPrefsWatcher::ForceUpdateBackup() {
  UMA_HISTOGRAM_ENUMERATION(
      kProtectorHistogramPrefs,
      kProtectorErrorForcedUpdate,
      kProtectorErrorCount);
  InitBackup();
}

void ProtectedPrefsWatcher::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  DCHECK(type == chrome::NOTIFICATION_PREF_CHANGED);
  const std::string* pref_name = content::Details<std::string>(details).ptr();
  DCHECK(pref_name && pref_observer_->IsObserved(*pref_name));
  if (UpdateBackupEntry(*pref_name))
    UpdateBackupSignature();
}

void ProtectedPrefsWatcher::EnsurePrefsMigration() {
  SessionStartupPref::MigrateIfNecessary(profile_->GetPrefs());
}

bool ProtectedPrefsWatcher::UpdateCachedPrefs() {
  // Direct access to the extensions prefs is required becase ExtensionService
  // may not yet have been initialized.
  const base::DictionaryValue* extension_prefs;
  const base::Value* extension_prefs_value =
      profile_->GetPrefs()->GetUserPrefValue(ExtensionPrefs::kExtensionsPref);
  if (!extension_prefs_value ||
      !extension_prefs_value->GetAsDictionary(&extension_prefs)) {
    return false;
  }
  ExtensionPrefs::ExtensionIdSet extension_ids =
      ExtensionPrefs::GetExtensionsFrom(extension_prefs);
  if (extension_ids == cached_extension_ids_)
    return false;
  cached_extension_ids_.swap(extension_ids);
  return true;
}

bool ProtectedPrefsWatcher::HasBackup() const {
  // TODO(ivankr): as soon as some irreversible change to Preferences happens,
  // add a condition that this change has occured as well (otherwise it's
  // possible to simply clear the "backup" dictionary to make settings
  // unprotected).
  return profile_->GetPrefs()->HasPrefPath(kBackupSignature);
}

void ProtectedPrefsWatcher::InitBackup() {
  PrefService* prefs = profile_->GetPrefs();
  for (size_t i = 0; i < arraysize(kProtectedPrefNames); ++i) {
    const base::Value* user_value =
        prefs->GetUserPrefValue(kProtectedPrefNames[i]);
    if (user_value)
      prefs->Set(GetBackupNameFor(kProtectedPrefNames[i]).c_str(), *user_value);
  }
  ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs);
  base::ListValue* extension_ids = extension_ids_update.Get();
  extension_ids->Clear();
  for (ExtensionPrefs::ExtensionIdSet::const_iterator it =
           cached_extension_ids_.begin();
       it != cached_extension_ids_.end(); ++it) {
    extension_ids->Append(base::Value::CreateStringValue(*it));
  }
  prefs->SetInteger(kBackupVersion, kCurrentVersionNumber);
  UpdateBackupSignature();
}

void ProtectedPrefsWatcher::MigrateOldBackupIfNeeded() {
  PrefService* prefs = profile_->GetPrefs();

  int current_version = prefs->GetInteger(kBackupVersion);
  VLOG(1) << "Backup version: " << current_version;
  if (current_version == kCurrentVersionNumber)
    return;

  switch (current_version) {
    case 1:
      // Add pinned tabs backup.
      prefs->Set(kBackupPinnedTabs,
                 *prefs->GetUserPrefValue(prefs::kPinnedTabs));
      // FALL THROUGH

    case 2:
      // SessionStartupPref migration.
      DCHECK(prefs->GetBoolean(prefs::kRestoreOnStartupMigrated));
      prefs->SetInteger(kBackupRestoreOnStartup,
                        prefs->GetInteger(prefs::kRestoreOnStartup));
      prefs->Set(kBackupURLsToRestoreOnStartup,
                 *prefs->GetList(prefs::kURLsToRestoreOnStartup));
      // FALL THROUGH

    case 3:
      // Reset to default values backup prefs whose actual prefs are not set.
      for (size_t i = 0; i < arraysize(kProtectedPrefNames); ++i) {
        if (!prefs->HasPrefPath(kProtectedPrefNames[i]))
          prefs->ClearPref(GetBackupNameFor(kProtectedPrefNames[i]).c_str());
      }
      // FALL THROUGH
  }

  prefs->SetInteger(kBackupVersion, kCurrentVersionNumber);
  UpdateBackupSignature();
}

bool ProtectedPrefsWatcher::UpdateBackupEntry(const std::string& path) {
  std::string backup_path = GetBackupNameFor(path);
  PrefService* prefs = profile_->GetPrefs();
  const PrefService::Preference* pref = prefs->FindPreference(path.c_str());
  if (path == ExtensionPrefs::kExtensionsPref) {
    // For changes in extension dictionary, do nothing if the IDs list remained
    // the same.
    if (!UpdateCachedPrefs())
      return false;
    ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs);
    base::ListValue* extension_ids = extension_ids_update.Get();
    extension_ids->Clear();
    for (ExtensionPrefs::ExtensionIdSet::const_iterator it =
             cached_extension_ids_.begin();
         it != cached_extension_ids_.end(); ++it) {
      extension_ids->Append(base::Value::CreateStringValue(*it));
    }
  } else if (!prefs->HasPrefPath(path.c_str())) {
    // Preference has been removed, remove the backup as well.
    prefs->ClearPref(backup_path.c_str());
  } else if (!pref->IsUserControlled()) {
    return false;
  } else {
    prefs->Set(backup_path.c_str(), *pref->GetValue());
  }
  VLOG(1) << "Updated backup entry for: " << path;
  return true;
}

void ProtectedPrefsWatcher::UpdateBackupSignature() {
  PrefService* prefs = profile_->GetPrefs();
  std::string signed_data = GetSignatureData(prefs);
  DCHECK(!signed_data.empty());
  std::string signature = SignSetting(signed_data);
  DCHECK(!signature.empty());
  std::string signature_base64;
  if (!base::Base64Encode(signature, &signature_base64))
    NOTREACHED();
  prefs->SetString(kBackupSignature, signature_base64);
  // Schedule disk write on FILE thread as soon as possible.
  prefs->CommitPendingWrite();
  VLOG(1) << "Updated backup signature";
}

bool ProtectedPrefsWatcher::IsSignatureValid() const {
  DCHECK(HasBackup());
  PrefService* prefs = profile_->GetPrefs();
  std::string signed_data = GetSignatureData(prefs);
  DCHECK(!signed_data.empty());
  std::string signature;
  if (!base::Base64Decode(prefs->GetString(kBackupSignature), &signature))
    return false;
  return IsSettingValid(signed_data, signature);
}

void ProtectedPrefsWatcher::ValidateBackup() {
  if (!HasBackup()) {
    // Create initial backup entries and sign them.
    InitBackup();
    UMA_HISTOGRAM_ENUMERATION(
        kProtectorHistogramPrefs,
        kProtectorErrorValueValidZero,
        kProtectorErrorCount);
  } else if (IsSignatureValid()) {
    MigrateOldBackupIfNeeded();
    UMA_HISTOGRAM_ENUMERATION(
        kProtectorHistogramPrefs,
        kProtectorErrorValueValid,
        kProtectorErrorCount);
  } else {
    LOG(WARNING) << "Invalid backup signature";
    is_backup_valid_ = false;
    // The whole backup has been compromised, overwrite it.
    InitBackup();
    UMA_HISTOGRAM_ENUMERATION(
        kProtectorHistogramPrefs,
        kProtectorErrorBackupInvalid,
        kProtectorErrorCount);
  }
}

std::string ProtectedPrefsWatcher::GetSignatureData(PrefService* prefs) const {
  int current_version = prefs->GetInteger(kBackupVersion);
  // TODO(ivankr): replace this with some existing reliable serializer.
  // JSONWriter isn't a good choice because JSON formatting may change suddenly.
  std::string data = prefs->GetString(kBackupHomePage);
  StringAppendBoolean(prefs, kBackupHomePageIsNewTabPage, &data);
  StringAppendBoolean(prefs, kBackupShowHomeButton, &data);
  StringAppendInteger(prefs, kBackupRestoreOnStartup, &data);
  StringAppendStringList(prefs->GetList(kBackupURLsToRestoreOnStartup), &data);
  StringAppendStringList(prefs->GetList(kBackupExtensionsIDs), &data);
  if (current_version >= 2) {
    // Version itself is included only since version 2 since it wasn't there
    // in version 1.
    base::StringAppendF(&data, "|v%d", current_version);
    const base::ListValue* pinned_tabs = prefs->GetList(kBackupPinnedTabs);
    for (base::ListValue::const_iterator it = pinned_tabs->begin();
         it != pinned_tabs->end(); ++it) {
      const base::DictionaryValue* tab = NULL;
      if (!(*it)->GetAsDictionary(&tab)) {
        NOTREACHED();
        continue;
      }
      StringAppendStringDictionary(tab, &data);
    }
  }
  return data;
}

}  // namespace protector
<|endoftext|>


<commit_before>#include "LICM.h"
#include "CSE.h"
#include "ExprUsesVar.h"
#include "IREquality.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Scope.h"
#include "Simplify.h"
#include "Substitute.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;
using std::vector;
using std::pair;
using std::set;

// Is it safe to lift an Expr out of a loop (and potentially across a device boundary)
class CanLift : public IRVisitor {
    using IRVisitor::visit;

    void visit(const Call *op) {
        if (!op->is_pure()) {
            result = false;
        } else {
            IRVisitor::visit(op);
        }
    }

    void visit(const Load *op) {
        result = false;
    }

    void visit(const Variable *op) {
        if (varying.contains(op->name)) {
            result = false;
        }
    }

    const Scope<int> &varying;

public:
    bool result {true};

    CanLift(const Scope<int> &v) : varying(v) {}
};

// Lift pure loop invariants to the top level. Applied independently
// to each loop.
class LiftLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> varying;

    bool can_lift(const Expr &e) {
        CanLift check(varying);
        e.accept(&check);
        return check.result;
    }

    bool should_lift(const Expr &e) {
        if (!can_lift(e)) return false;
        if (e.as<Variable>()) return false;
        if (e.as<Broadcast>()) return false;
        if (is_const(e)) return false;
        // bool vectors are buggy enough in LLVM that lifting them is a bad idea.
        // (We just skip all vectors on the principle that we don't want them
        // on the stack anyway.)
        if (e.type().is_vector()) return false;
        return true;
    }

    Expr visit(const Let *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const For *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

public:

    using IRMutator2::mutate;

    Expr mutate(const Expr &e) override {
        if (should_lift(e)) {
            // Lift it in canonical form
            Expr lifted_expr = simplify(e);
            auto it = lifted.find(lifted_expr);
            if (it == lifted.end()) {
                string name = unique_name('t');
                lifted[lifted_expr] = name;
                return Variable::make(e.type(), name);
            } else {
                return Variable::make(e.type(), it->second);
            }
        } else {
            return IRMutator2::mutate(e);
        }
    }

    map<Expr, string, IRDeepCompare> lifted;
};

class LICM : public IRMutator2 {
    using IRMutator2::visit;

    bool in_gpu_loop{false};

    // Compute the cost of computing an expression inside the inner
    // loop, compared to just loading it as a parameter.
    int cost(Expr e, const set<string> &vars) {
        if (is_const(e)) {
            return 0;
        } else if (const Variable *var = e.as<Variable>()) {
            if (vars.count(var->name)) {
                // We're loading this already
                return 0;
            } else {
                // Would have to load this
                return 1;
            }
        } else if (const Add *add = e.as<Add>()) {
            return cost(add->a, vars) + cost(add->b, vars) + 1;
        } else if (const Sub *sub = e.as<Sub>()) {
            return cost(sub->a, vars) + cost(sub->b, vars) + 1;
        } else if (const Mul *mul = e.as<Mul>()) {
            return cost(mul->a, vars) + cost(mul->b, vars) + 1;
        } else {
            return 0x7fffffff;
        }
    }

    Stmt visit(const For *op) override {
        ScopedValue<bool> old_in_gpu_loop(in_gpu_loop);
        in_gpu_loop =
            (op->for_type == ForType::GPUBlock ||
             op->for_type == ForType::GPUThread);

        if (old_in_gpu_loop && in_gpu_loop) {
            // Don't lift lets to in-between gpu blocks/threads
            return IRMutator2::visit(op);
        } else if (op->device_api == DeviceAPI::GLSL ||
                   op->device_api == DeviceAPI::OpenGLCompute) {
            // Don't lift anything out of OpenGL loops
            return IRMutator2::visit(op);
        } else {

            // Lift invariants
            LiftLoopInvariants lifter;
            Stmt new_stmt = lifter.mutate(op);

            // As an optimization to reduce register pressure, take
            // the set of expressions to lift and check if any can
            // cheaply be computed from others. If so it's better to
            // do that than to load multiple related values off the
            // stack. We currently only consider expressions that are
            // the sum, difference, or product of two variables
            // already used in the kernel, or a variable plus a
            // constant.

            // Linearize all the exprs and names
            vector<Expr> exprs;
            vector<string> names;
            for (const auto &p : lifter.lifted) {
                exprs.push_back(p.first);
                names.push_back(p.second);
            }

            // Jointly CSE the lifted exprs put putting them together into a dummy Expr
            Expr dummy_call = Call::make(Int(32), "dummy", exprs, Call::Extern);
            dummy_call = common_subexpression_elimination(dummy_call, true);

            // Peel off containing lets. These will be lifted.
            vector<pair<string, Expr>> lets;
            while (const Let *let = dummy_call.as<Let>()) {
                lets.push_back({let->name, let->value});
                dummy_call = let->body;
            }

            // Track the set of variables used by the inner loop
            class CollectVars : public IRVisitor {
                using IRVisitor::visit;
                void visit(const Variable *op) {
                    vars.insert(op->name);
                }
            public:
                set<string> vars;
            } vars;
            new_stmt.accept(&vars);

            // Now consider substituting back in each use
            const Call *call = dummy_call.as<Call>();
            internal_assert(call);
            bool converged;
            do {
                converged = true;
                for (size_t i = 0; i < exprs.size(); i++) {
                    if (!exprs[i].defined()) continue;
                    Expr e = call->args[i];
                    if (cost(e, vars.vars) <= 1) {
                        // Just subs it back in - computing it is as cheap
                        // as loading it.
                        e.accept(&vars);
                        new_stmt = substitute(names[i], e, new_stmt);
                        names[i].clear();
                        exprs[i] = Expr();
                        converged = false;
                    } else {
                        exprs[i] = e;
                    }
                }
            } while (!converged);

            // Recurse
            const For *loop = new_stmt.as<For>();
            internal_assert(loop);

            new_stmt = For::make(loop->name, loop->min, loop->extent,
                                 loop->for_type, loop->device_api, mutate(loop->body));

            // Wrap lets for the lifted invariants
            for (size_t i = 0; i < exprs.size(); i++) {
                if (exprs[i].defined()) {
                    new_stmt = LetStmt::make(names[i], exprs[i], new_stmt);
                }
            }

            // Wrap the lets pulled out by CSE
            while (!lets.empty()) {
                new_stmt = LetStmt::make(lets.back().first, lets.back().second, new_stmt);
                lets.pop_back();
            }

            return new_stmt;
        }
    }
};

// Reassociate summations to group together the loop invariants. Useful to run before LICM.
class GroupLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> var_depth;

    class ExprDepth : public IRVisitor {
        using IRVisitor::visit;
        const Scope<int> &depth;

        void visit(const Variable *op) {
            if (depth.contains(op->name)) {
                result = std::max(result, depth.get(op->name));
            }
        }
    public:
        int result = 0;
        ExprDepth(const Scope<int> &var_depth) : depth(var_depth) {}
    };

    int expr_depth(const Expr &e) {
        ExprDepth depth(var_depth);
        e.accept(&depth);
        return depth.result;
    }

    struct Term {
        Expr expr;
        bool positive;
        int depth;
    };

    vector<Term> extract_summation(Expr e) {
        vector<Term> pending, terms;
        pending.push_back({e, true, 0});
        while (!pending.empty()) {
            Term next = pending.back();
            pending.pop_back();
            const Add *add = next.expr.as<Add>();
            const Sub *sub = next.expr.as<Sub>();
            if (add) {
                pending.push_back({add->a, next.positive, 0});
                pending.push_back({add->b, next.positive, 0});
            } else if (sub) {
                pending.push_back({sub->a, next.positive, 0});
                pending.push_back({sub->b, !next.positive, 0});
            } else {
                next.expr = mutate(next.expr);
                if (next.expr.as<Add>() || next.expr.as<Sub>()) {
                    // After mutation it became an add or sub, throw it back on the pending queue.
                    pending.push_back(next);
                } else {
                    next.depth = expr_depth(next.expr);
                    terms.push_back(next);
                }
            }
        }

        // Sort the terms by loop depth. Terms of equal depth are
        // likely already in a good order, so don't mess with them.
        std::stable_sort(terms.begin(), terms.end(),
                  [](const Term &a, const Term &b) {
                      return a.depth > b.depth;
                  });

        return terms;
    }

    Expr reassociate_summation(Expr e) {
        vector<Term> terms = extract_summation(e);

        Expr result;
        bool positive = true;
        while (!terms.empty()) {
            Term next = terms.back();
            terms.pop_back();
            if (result.defined()) {
                if (next.positive == positive) {
                    result += next.expr;
                } else if (next.positive) {
                    result = next.expr - result;
                    positive = true;
                } else {
                    result -= next.expr;
                }
            } else {
                result = next.expr;
                positive = next.positive;
            }
        }

        if (!positive) {
            result = make_zero(result.type()) - result;
        }

        return result;
    }

    Expr visit(const Sub *op) override {
        return reassociate_summation(op);
    }

    Expr visit(const Add *op) override {
        return reassociate_summation(op);
    }

    int depth = 0;

    Stmt visit(const For *op) override {
        depth++;
	ScopedBinding<int> bind(var_depth, op->name, depth);
        Stmt stmt = IRMutator2::visit(op);
        depth--;
	return stmt;
    }

    Expr visit(const Let *op) override {
	ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
	return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
	ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
	return IRMutator2::visit(op);
    }

};

// Turn for loops of size one into let statements
Stmt loop_invariant_code_motion(Stmt s) {
    s = GroupLoopInvariants().mutate(s);
    s = common_subexpression_elimination(s);
    s = LICM().mutate(s);
    s = simplify_exprs(s);
    return s;
}

}
}
<commit_msg>Remove tabs.<commit_after>#include "LICM.h"
#include "CSE.h"
#include "ExprUsesVar.h"
#include "IREquality.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Scope.h"
#include "Simplify.h"
#include "Substitute.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;
using std::vector;
using std::pair;
using std::set;

// Is it safe to lift an Expr out of a loop (and potentially across a device boundary)
class CanLift : public IRVisitor {
    using IRVisitor::visit;

    void visit(const Call *op) {
        if (!op->is_pure()) {
            result = false;
        } else {
            IRVisitor::visit(op);
        }
    }

    void visit(const Load *op) {
        result = false;
    }

    void visit(const Variable *op) {
        if (varying.contains(op->name)) {
            result = false;
        }
    }

    const Scope<int> &varying;

public:
    bool result {true};

    CanLift(const Scope<int> &v) : varying(v) {}
};

// Lift pure loop invariants to the top level. Applied independently
// to each loop.
class LiftLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> varying;

    bool can_lift(const Expr &e) {
        CanLift check(varying);
        e.accept(&check);
        return check.result;
    }

    bool should_lift(const Expr &e) {
        if (!can_lift(e)) return false;
        if (e.as<Variable>()) return false;
        if (e.as<Broadcast>()) return false;
        if (is_const(e)) return false;
        // bool vectors are buggy enough in LLVM that lifting them is a bad idea.
        // (We just skip all vectors on the principle that we don't want them
        // on the stack anyway.)
        if (e.type().is_vector()) return false;
        return true;
    }

    Expr visit(const Let *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const For *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

public:

    using IRMutator2::mutate;

    Expr mutate(const Expr &e) override {
        if (should_lift(e)) {
            // Lift it in canonical form
            Expr lifted_expr = simplify(e);
            auto it = lifted.find(lifted_expr);
            if (it == lifted.end()) {
                string name = unique_name('t');
                lifted[lifted_expr] = name;
                return Variable::make(e.type(), name);
            } else {
                return Variable::make(e.type(), it->second);
            }
        } else {
            return IRMutator2::mutate(e);
        }
    }

    map<Expr, string, IRDeepCompare> lifted;
};

class LICM : public IRMutator2 {
    using IRMutator2::visit;

    bool in_gpu_loop{false};

    // Compute the cost of computing an expression inside the inner
    // loop, compared to just loading it as a parameter.
    int cost(Expr e, const set<string> &vars) {
        if (is_const(e)) {
            return 0;
        } else if (const Variable *var = e.as<Variable>()) {
            if (vars.count(var->name)) {
                // We're loading this already
                return 0;
            } else {
                // Would have to load this
                return 1;
            }
        } else if (const Add *add = e.as<Add>()) {
            return cost(add->a, vars) + cost(add->b, vars) + 1;
        } else if (const Sub *sub = e.as<Sub>()) {
            return cost(sub->a, vars) + cost(sub->b, vars) + 1;
        } else if (const Mul *mul = e.as<Mul>()) {
            return cost(mul->a, vars) + cost(mul->b, vars) + 1;
        } else {
            return 0x7fffffff;
        }
    }

    Stmt visit(const For *op) override {
        ScopedValue<bool> old_in_gpu_loop(in_gpu_loop);
        in_gpu_loop =
            (op->for_type == ForType::GPUBlock ||
             op->for_type == ForType::GPUThread);

        if (old_in_gpu_loop && in_gpu_loop) {
            // Don't lift lets to in-between gpu blocks/threads
            return IRMutator2::visit(op);
        } else if (op->device_api == DeviceAPI::GLSL ||
                   op->device_api == DeviceAPI::OpenGLCompute) {
            // Don't lift anything out of OpenGL loops
            return IRMutator2::visit(op);
        } else {

            // Lift invariants
            LiftLoopInvariants lifter;
            Stmt new_stmt = lifter.mutate(op);

            // As an optimization to reduce register pressure, take
            // the set of expressions to lift and check if any can
            // cheaply be computed from others. If so it's better to
            // do that than to load multiple related values off the
            // stack. We currently only consider expressions that are
            // the sum, difference, or product of two variables
            // already used in the kernel, or a variable plus a
            // constant.

            // Linearize all the exprs and names
            vector<Expr> exprs;
            vector<string> names;
            for (const auto &p : lifter.lifted) {
                exprs.push_back(p.first);
                names.push_back(p.second);
            }

            // Jointly CSE the lifted exprs put putting them together into a dummy Expr
            Expr dummy_call = Call::make(Int(32), "dummy", exprs, Call::Extern);
            dummy_call = common_subexpression_elimination(dummy_call, true);

            // Peel off containing lets. These will be lifted.
            vector<pair<string, Expr>> lets;
            while (const Let *let = dummy_call.as<Let>()) {
                lets.push_back({let->name, let->value});
                dummy_call = let->body;
            }

            // Track the set of variables used by the inner loop
            class CollectVars : public IRVisitor {
                using IRVisitor::visit;
                void visit(const Variable *op) {
                    vars.insert(op->name);
                }
            public:
                set<string> vars;
            } vars;
            new_stmt.accept(&vars);

            // Now consider substituting back in each use
            const Call *call = dummy_call.as<Call>();
            internal_assert(call);
            bool converged;
            do {
                converged = true;
                for (size_t i = 0; i < exprs.size(); i++) {
                    if (!exprs[i].defined()) continue;
                    Expr e = call->args[i];
                    if (cost(e, vars.vars) <= 1) {
                        // Just subs it back in - computing it is as cheap
                        // as loading it.
                        e.accept(&vars);
                        new_stmt = substitute(names[i], e, new_stmt);
                        names[i].clear();
                        exprs[i] = Expr();
                        converged = false;
                    } else {
                        exprs[i] = e;
                    }
                }
            } while (!converged);

            // Recurse
            const For *loop = new_stmt.as<For>();
            internal_assert(loop);

            new_stmt = For::make(loop->name, loop->min, loop->extent,
                                 loop->for_type, loop->device_api, mutate(loop->body));

            // Wrap lets for the lifted invariants
            for (size_t i = 0; i < exprs.size(); i++) {
                if (exprs[i].defined()) {
                    new_stmt = LetStmt::make(names[i], exprs[i], new_stmt);
                }
            }

            // Wrap the lets pulled out by CSE
            while (!lets.empty()) {
                new_stmt = LetStmt::make(lets.back().first, lets.back().second, new_stmt);
                lets.pop_back();
            }

            return new_stmt;
        }
    }
};

// Reassociate summations to group together the loop invariants. Useful to run before LICM.
class GroupLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> var_depth;

    class ExprDepth : public IRVisitor {
        using IRVisitor::visit;
        const Scope<int> &depth;

        void visit(const Variable *op) {
            if (depth.contains(op->name)) {
                result = std::max(result, depth.get(op->name));
            }
        }
    public:
        int result = 0;
        ExprDepth(const Scope<int> &var_depth) : depth(var_depth) {}
    };

    int expr_depth(const Expr &e) {
        ExprDepth depth(var_depth);
        e.accept(&depth);
        return depth.result;
    }

    struct Term {
        Expr expr;
        bool positive;
        int depth;
    };

    vector<Term> extract_summation(Expr e) {
        vector<Term> pending, terms;
        pending.push_back({e, true, 0});
        while (!pending.empty()) {
            Term next = pending.back();
            pending.pop_back();
            const Add *add = next.expr.as<Add>();
            const Sub *sub = next.expr.as<Sub>();
            if (add) {
                pending.push_back({add->a, next.positive, 0});
                pending.push_back({add->b, next.positive, 0});
            } else if (sub) {
                pending.push_back({sub->a, next.positive, 0});
                pending.push_back({sub->b, !next.positive, 0});
            } else {
                next.expr = mutate(next.expr);
                if (next.expr.as<Add>() || next.expr.as<Sub>()) {
                    // After mutation it became an add or sub, throw it back on the pending queue.
                    pending.push_back(next);
                } else {
                    next.depth = expr_depth(next.expr);
                    terms.push_back(next);
                }
            }
        }

        // Sort the terms by loop depth. Terms of equal depth are
        // likely already in a good order, so don't mess with them.
        std::stable_sort(terms.begin(), terms.end(),
                  [](const Term &a, const Term &b) {
                      return a.depth > b.depth;
                  });

        return terms;
    }

    Expr reassociate_summation(Expr e) {
        vector<Term> terms = extract_summation(e);

        Expr result;
        bool positive = true;
        while (!terms.empty()) {
            Term next = terms.back();
            terms.pop_back();
            if (result.defined()) {
                if (next.positive == positive) {
                    result += next.expr;
                } else if (next.positive) {
                    result = next.expr - result;
                    positive = true;
                } else {
                    result -= next.expr;
                }
            } else {
                result = next.expr;
                positive = next.positive;
            }
        }

        if (!positive) {
            result = make_zero(result.type()) - result;
        }

        return result;
    }

    Expr visit(const Sub *op) override {
        return reassociate_summation(op);
    }

    Expr visit(const Add *op) override {
        return reassociate_summation(op);
    }

    int depth = 0;

    Stmt visit(const For *op) override {
        depth++;
        ScopedBinding<int> bind(var_depth, op->name, depth);
        Stmt stmt = IRMutator2::visit(op);
        depth--;
        return stmt;
    }

    Expr visit(const Let *op) override {
        ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
        return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
        ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
        return IRMutator2::visit(op);
    }

};

// Turn for loops of size one into let statements
Stmt loop_invariant_code_motion(Stmt s) {
    s = GroupLoopInvariants().mutate(s);
    s = common_subexpression_elimination(s);
    s = LICM().mutate(s);
    s = simplify_exprs(s);
    return s;
}

}
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 Samplecount S.L.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <methcla/engine.h>
#include "Methcla/Audio/Engine.hpp"
#include "Methcla/Audio/SynthDef.hpp"

#include <boost/filesystem.hpp>

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <unordered_map>

#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
#include "lv2/lv2plug.in/ns/ext/atom/util.h"

class Options
{
public:
    Options(const Methcla_Option* options)
    {
        for (const Methcla_Option* cur = options; cur->key != nullptr; cur++) {
            m_options[cur->key] = cur->value;
        }
    }

    template <typename T> T lookup(const std::string& key, const T& def=nullptr)
    {
        auto it = m_options.find(key);
        return it == m_options.end() ? def : static_cast<T>(it->second);
    }

private:
    typedef std::unordered_map<std::string,const void*> OptionMap;
    OptionMap m_options;
};

struct Methcla_Engine
{
    Methcla_Engine(const Methcla_Option* inOptions)
    {
        Options options(inOptions);
        const boost::filesystem::path lv2Path(options.lookup<const char*>(METHCLA_OPTION__LV2_PATH));

        // TODO: Put this somewhere else
        auto libs = options.lookup<const Methcla_LV2_Library*>(METHCLA_OPTION__LV2_LIBRARIES);
        Methcla::Audio::PluginManager::StaticLibraryMap staticLibs;
        if (libs != nullptr) {
            while (libs->plugin != nullptr) {
                staticLibs[libs->plugin] = libs->function;
                libs++;
            }
        }

        m_pluginManager = new Methcla::Audio::PluginManager(staticLibs);
        m_engine = new Methcla::Audio::Engine(*m_pluginManager, lv2Path);
    }
    ~Methcla_Engine()
    {
        delete m_engine;
        delete m_pluginManager;
    }

    Methcla::Audio::PluginManager* m_pluginManager;
    Methcla::Audio::Engine*        m_engine;
};

Methcla_Engine* methcla_engine_new(const Methcla_Option* options)
{
    // cout << "Methcla_Engine_new" << endl;
    return new Methcla_Engine(options);
}

void Methcla_Engine_free(Methcla_Engine* engine)
{
    // cout << "Methcla_Engine_free" << endl;
    methcla_engine_stop(engine);
    delete engine;
}

void methcla_engine_start(Methcla_Engine* engine)
{
    // cout << "Methcla_Engine_start" << endl;
    engine->m_engine->start();
}

void methcla_engine_stop(Methcla_Engine* engine)
{
    // cout << "Methcla_Engine_stop" << endl;
    engine->m_engine->stop();
}

LV2_URID methcla_engine_map_uri(Methcla_Engine* engine, const char* uri)
{
    return engine->m_pluginManager->uriMap().map(uri);
}

const char* methcla_engine_unmap_uri(Methcla_Engine* engine, LV2_URID urid)
{
    return engine->m_pluginManager->uriMap().unmap(urid);
}

void methcla_engine_request(Methcla_Engine* engine, const LV2_Atom* request, Methcla_Response_Handler handler, void* handler_data)
{
    // engine->m_engine->env().request(request, handler, handlerData);
}

void* methcla_engine_impl(Methcla_Engine* engine)
{
    return engine->m_engine;
}
<commit_msg>Implement methcla_engine_request<commit_after>// Copyright 2012-2013 Samplecount S.L.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <methcla/engine.h>
#include "Methcla/Audio/Engine.hpp"
#include "Methcla/Audio/SynthDef.hpp"

#include <boost/filesystem.hpp>

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <unordered_map>

#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
#include "lv2/lv2plug.in/ns/ext/atom/util.h"

class Options
{
public:
    Options(const Methcla_Option* options)
    {
        for (const Methcla_Option* cur = options; cur->key != nullptr; cur++) {
            m_options[cur->key] = cur->value;
        }
    }

    template <typename T> T lookup(const std::string& key, const T& def=nullptr)
    {
        auto it = m_options.find(key);
        return it == m_options.end() ? def : static_cast<T>(it->second);
    }

private:
    typedef std::unordered_map<std::string,const void*> OptionMap;
    OptionMap m_options;
};

struct Methcla_Engine
{
    Methcla_Engine(const Methcla_Option* inOptions)
    {
        Options options(inOptions);
        const boost::filesystem::path lv2Path(options.lookup<const char*>(METHCLA_OPTION__LV2_PATH));

        // TODO: Put this somewhere else
        auto libs = options.lookup<const Methcla_LV2_Library*>(METHCLA_OPTION__LV2_LIBRARIES);
        Methcla::Audio::PluginManager::StaticLibraryMap staticLibs;
        if (libs != nullptr) {
            while (libs->plugin != nullptr) {
                staticLibs[libs->plugin] = libs->function;
                libs++;
            }
        }

        m_pluginManager = new Methcla::Audio::PluginManager(staticLibs);
        m_engine = new Methcla::Audio::Engine(*m_pluginManager, lv2Path);
    }
    ~Methcla_Engine()
    {
        delete m_engine;
        delete m_pluginManager;
    }

    Methcla::Audio::PluginManager* m_pluginManager;
    Methcla::Audio::Engine*        m_engine;
};

Methcla_Engine* methcla_engine_new(const Methcla_Option* options)
{
    // cout << "Methcla_Engine_new" << endl;
    return new Methcla_Engine(options);
}

void Methcla_Engine_free(Methcla_Engine* engine)
{
    // cout << "Methcla_Engine_free" << endl;
    methcla_engine_stop(engine);
    delete engine;
}

void methcla_engine_start(Methcla_Engine* engine)
{
    // cout << "Methcla_Engine_start" << endl;
    engine->m_engine->start();
}

void methcla_engine_stop(Methcla_Engine* engine)
{
    // cout << "Methcla_Engine_stop" << endl;
    engine->m_engine->stop();
}

LV2_URID methcla_engine_map_uri(Methcla_Engine* engine, const char* uri)
{
    return engine->m_pluginManager->uriMap().map(uri);
}

const char* methcla_engine_unmap_uri(Methcla_Engine* engine, LV2_URID urid)
{
    return engine->m_pluginManager->uriMap().unmap(urid);
}

void methcla_engine_request(Methcla_Engine* engine, Methcla_Response_Handler handler, void* handler_data, const LV2_Atom* request)
{
    engine->m_engine->env().request(handler, handler_data, request);
}

void* methcla_engine_impl(Methcla_Engine* engine)
{
    return engine->m_engine;
}
<|endoftext|>