<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 "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
#include "chrome/browser/chromeos/cros/mock_input_method_library.h"
#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h"
#include "chrome/browser/chromeos/login/mock_authenticator.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/screen_locker_tester.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/browser_dialogs.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/ui_test_utils.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "views/controls/textfield/textfield.h"
#include "views/window/window_gtk.h"

namespace {

// An object that wait for lock state and fullscreen state.
class Waiter : public NotificationObserver {
 public:
  explicit Waiter(Browser* browser)
      : browser_(browser),
        running_(false) {
    registrar_.Add(this,
                   NotificationType::SCREEN_LOCK_STATE_CHANGED,
                   NotificationService::AllSources());
    handler_id_ = g_signal_connect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        "window-state-event",
        G_CALLBACK(OnWindowStateEventThunk),
        this);
  }

  ~Waiter() {
    g_signal_handler_disconnect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        handler_id_);
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
    if (running_)
      MessageLoop::current()->Quit();
  }

  // Wait until the two conditions are met.
  void Wait(bool locker_state, bool fullscreen) {
    running_ = true;
    scoped_ptr<chromeos::test::ScreenLockerTester>
        tester(chromeos::ScreenLocker::GetTester());
    while (tester->IsLocked() != locker_state ||
           browser_->window()->IsFullscreen() != fullscreen) {
      ui_test_utils::RunMessageLoop();
    }
    // Make sure all pending tasks are executed.
    ui_test_utils::RunAllPendingInMessageLoop();
    running_ = false;
  }

  CHROMEGTK_CALLBACK_1(Waiter, gboolean, OnWindowStateEvent,
                       GdkEventWindowState*);

 private:
  Browser* browser_;
  gulong handler_id_;
  NotificationRegistrar registrar_;

  // Are we currently running the message loop?
  bool running_;

  DISALLOW_COPY_AND_ASSIGN(Waiter);
};

gboolean Waiter::OnWindowStateEvent(GtkWidget* widget,
                                    GdkEventWindowState* event) {
  MessageLoop::current()->Quit();
  return false;
}

}  // namespace

namespace chromeos {

class ScreenLockerTest : public CrosInProcessBrowserTest {
 public:
  ScreenLockerTest() : mock_screen_lock_library_(NULL),
                       mock_input_method_library_(NULL) {
  }

 protected:
  MockScreenLockLibrary *mock_screen_lock_library_;
  MockInputMethodLibrary *mock_input_method_library_;

  // Test the no password mode with different unlock scheme given by
  // |unlock| function.
  void TestNoPassword(void (unlock)(views::Widget*)) {
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    UserManager::Get()->OffTheRecordUserLoggedIn();
    ScreenLocker::Show();
    scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
    tester->EmulateWindowManagerReady();
    if (!chromeos::ScreenLocker::GetTester()->IsLocked())
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    EXPECT_TRUE(tester->IsLocked());
    tester->InjectMockAuthenticator("", "");

    unlock(tester->GetWidget());

    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_TRUE(tester->IsLocked());

    // Emulate LockScreen request from PowerManager (via SessionManager).
    ScreenLocker::Hide();
    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_FALSE(tester->IsLocked());
  }

  void LockScreenWithUser(test::ScreenLockerTester* tester,
                          const std::string& user) {
    UserManager::Get()->UserLoggedIn(user);
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    if (!tester->IsLocked()) {
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    }
    EXPECT_TRUE(tester->IsLocked());
  }

 private:
  virtual void SetUpInProcessBrowserTestFixture() {
    cros_mock_->InitStatusAreaMocks();
    cros_mock_->InitMockScreenLockLibrary();
    mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library();
    mock_input_method_library_ = cros_mock_->mock_input_method_library();
    EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    // Expectations for the status are on the screen lock window.
    cros_mock_->SetStatusAreaMocksExpectations();
    // Expectations for the status area on the browser window.
    cros_mock_->SetStatusAreaMocksExpectations();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
    command_line->AppendSwitch(switches::kNoFirstRun);
  }

  DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest);
};

// See http://crbug.com/79374
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, FLAKY_TestBasic) {
  EXPECT_CALL(*mock_input_method_library_, GetNumActiveInputMethods())
      .Times(1)
      .WillRepeatedly((testing::Return(0)))
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  if (!chromeos::ScreenLocker::GetTester()->IsLocked())
    ui_test_utils::WaitForNotification(
        NotificationType::SCREEN_LOCK_STATE_CHANGED);

  // Test to make sure that the widget is actually appearing and is of
  // reasonable size, preventing a regression of
  // http://code.google.com/p/chromium-os/issues/detail?id=5987
  gfx::Rect lock_bounds = tester->GetChildWidget()->GetWindowScreenBounds();
  EXPECT_GT(lock_bounds.width(), 10);
  EXPECT_GT(lock_bounds.height(), 10);

  tester->InjectMockAuthenticator("user", "pass");
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("fail");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  // Successful authentication simply send a unlock request to PowerManager.
  EXPECT_TRUE(tester->IsLocked());

  // Emulate LockScreen request from PowerManager (via SessionManager).
  // TODO(oshima): Find out better way to handle this in mock.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

// Temporarily disabling screen locker tests while investigating the
// issue crbug.com/78764.
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestFullscreenExit) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  {
    Waiter waiter(browser());
    browser()->ToggleFullscreenMode();
    waiter.Wait(false /* not locked */, true /* full screen */);
    EXPECT_TRUE(browser()->window()->IsFullscreen());
    EXPECT_FALSE(tester->IsLocked());
  }
  {
    Waiter waiter(browser());
    UserManager::Get()->UserLoggedIn("user");
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    waiter.Wait(true /* locked */, false /* full screen */);
    EXPECT_FALSE(browser()->window()->IsFullscreen());
    EXPECT_TRUE(tester->IsLocked());
  }
  tester->InjectMockAuthenticator("user", "pass");
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

void MouseMove(views::Widget* widget) {
  ui_controls::SendMouseMove(10, 10);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
                       DISABLED_TestNoPasswordWithMouseMove) {
  TestNoPassword(MouseMove);
}

void MouseClick(views::Widget* widget) {
  ui_controls::SendMouseClick(ui_controls::RIGHT);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
                       DISABLED_TestNoPasswordWithMouseClick) {
  TestNoPassword(MouseClick);
}

void KeyPress(views::Widget* widget) {
  ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()),
                            ui::VKEY_SPACE, false, false, false, false);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestNoPasswordWithKeyPress) {
  TestNoPassword(KeyPress);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestShowTwice) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(2)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  // Calling Show again simply send LockCompleted signal.
  ScreenLocker::Show();
  EXPECT_TRUE(tester->IsLocked());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestEscape) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  tester->SetPassword("password");
  EXPECT_EQ("password", tester->GetPassword());
  // Escape clears the password.
  ui_controls::SendKeyPress(GTK_WINDOW(tester->GetWidget()->GetNativeView()),
                            ui::VKEY_ESCAPE, false, false, false, false);
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ("", tester->GetPassword());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

}  // namespace chromeos
<commit_msg>Disabling TestBasic<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 "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
#include "chrome/browser/chromeos/cros/mock_input_method_library.h"
#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h"
#include "chrome/browser/chromeos/login/mock_authenticator.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/screen_locker_tester.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/browser_dialogs.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/ui_test_utils.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "views/controls/textfield/textfield.h"
#include "views/window/window_gtk.h"

namespace {

// An object that wait for lock state and fullscreen state.
class Waiter : public NotificationObserver {
 public:
  explicit Waiter(Browser* browser)
      : browser_(browser),
        running_(false) {
    registrar_.Add(this,
                   NotificationType::SCREEN_LOCK_STATE_CHANGED,
                   NotificationService::AllSources());
    handler_id_ = g_signal_connect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        "window-state-event",
        G_CALLBACK(OnWindowStateEventThunk),
        this);
  }

  ~Waiter() {
    g_signal_handler_disconnect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        handler_id_);
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
    if (running_)
      MessageLoop::current()->Quit();
  }

  // Wait until the two conditions are met.
  void Wait(bool locker_state, bool fullscreen) {
    running_ = true;
    scoped_ptr<chromeos::test::ScreenLockerTester>
        tester(chromeos::ScreenLocker::GetTester());
    while (tester->IsLocked() != locker_state ||
           browser_->window()->IsFullscreen() != fullscreen) {
      ui_test_utils::RunMessageLoop();
    }
    // Make sure all pending tasks are executed.
    ui_test_utils::RunAllPendingInMessageLoop();
    running_ = false;
  }

  CHROMEGTK_CALLBACK_1(Waiter, gboolean, OnWindowStateEvent,
                       GdkEventWindowState*);

 private:
  Browser* browser_;
  gulong handler_id_;
  NotificationRegistrar registrar_;

  // Are we currently running the message loop?
  bool running_;

  DISALLOW_COPY_AND_ASSIGN(Waiter);
};

gboolean Waiter::OnWindowStateEvent(GtkWidget* widget,
                                    GdkEventWindowState* event) {
  MessageLoop::current()->Quit();
  return false;
}

}  // namespace

namespace chromeos {

class ScreenLockerTest : public CrosInProcessBrowserTest {
 public:
  ScreenLockerTest() : mock_screen_lock_library_(NULL),
                       mock_input_method_library_(NULL) {
  }

 protected:
  MockScreenLockLibrary *mock_screen_lock_library_;
  MockInputMethodLibrary *mock_input_method_library_;

  // Test the no password mode with different unlock scheme given by
  // |unlock| function.
  void TestNoPassword(void (unlock)(views::Widget*)) {
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    UserManager::Get()->OffTheRecordUserLoggedIn();
    ScreenLocker::Show();
    scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
    tester->EmulateWindowManagerReady();
    if (!chromeos::ScreenLocker::GetTester()->IsLocked())
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    EXPECT_TRUE(tester->IsLocked());
    tester->InjectMockAuthenticator("", "");

    unlock(tester->GetWidget());

    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_TRUE(tester->IsLocked());

    // Emulate LockScreen request from PowerManager (via SessionManager).
    ScreenLocker::Hide();
    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_FALSE(tester->IsLocked());
  }

  void LockScreenWithUser(test::ScreenLockerTester* tester,
                          const std::string& user) {
    UserManager::Get()->UserLoggedIn(user);
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    if (!tester->IsLocked()) {
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    }
    EXPECT_TRUE(tester->IsLocked());
  }

 private:
  virtual void SetUpInProcessBrowserTestFixture() {
    cros_mock_->InitStatusAreaMocks();
    cros_mock_->InitMockScreenLockLibrary();
    mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library();
    mock_input_method_library_ = cros_mock_->mock_input_method_library();
    EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    // Expectations for the status are on the screen lock window.
    cros_mock_->SetStatusAreaMocksExpectations();
    // Expectations for the status area on the browser window.
    cros_mock_->SetStatusAreaMocksExpectations();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
    command_line->AppendSwitch(switches::kNoFirstRun);
  }

  DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest);
};

// Temporarily disabling all screen locker tests while investigating the
// issue crbug.com/78764.
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestBasic) {
  EXPECT_CALL(*mock_input_method_library_, GetNumActiveInputMethods())
      .Times(1)
      .WillRepeatedly((testing::Return(0)))
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  if (!chromeos::ScreenLocker::GetTester()->IsLocked())
    ui_test_utils::WaitForNotification(
        NotificationType::SCREEN_LOCK_STATE_CHANGED);

  // Test to make sure that the widget is actually appearing and is of
  // reasonable size, preventing a regression of
  // http://code.google.com/p/chromium-os/issues/detail?id=5987
  gfx::Rect lock_bounds = tester->GetChildWidget()->GetWindowScreenBounds();
  EXPECT_GT(lock_bounds.width(), 10);
  EXPECT_GT(lock_bounds.height(), 10);

  tester->InjectMockAuthenticator("user", "pass");
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("fail");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  // Successful authentication simply send a unlock request to PowerManager.
  EXPECT_TRUE(tester->IsLocked());

  // Emulate LockScreen request from PowerManager (via SessionManager).
  // TODO(oshima): Find out better way to handle this in mock.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestFullscreenExit) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  {
    Waiter waiter(browser());
    browser()->ToggleFullscreenMode();
    waiter.Wait(false /* not locked */, true /* full screen */);
    EXPECT_TRUE(browser()->window()->IsFullscreen());
    EXPECT_FALSE(tester->IsLocked());
  }
  {
    Waiter waiter(browser());
    UserManager::Get()->UserLoggedIn("user");
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    waiter.Wait(true /* locked */, false /* full screen */);
    EXPECT_FALSE(browser()->window()->IsFullscreen());
    EXPECT_TRUE(tester->IsLocked());
  }
  tester->InjectMockAuthenticator("user", "pass");
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

void MouseMove(views::Widget* widget) {
  ui_controls::SendMouseMove(10, 10);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
                       DISABLED_TestNoPasswordWithMouseMove) {
  TestNoPassword(MouseMove);
}

void MouseClick(views::Widget* widget) {
  ui_controls::SendMouseClick(ui_controls::RIGHT);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
                       DISABLED_TestNoPasswordWithMouseClick) {
  TestNoPassword(MouseClick);
}

void KeyPress(views::Widget* widget) {
  ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()),
                            ui::VKEY_SPACE, false, false, false, false);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestNoPasswordWithKeyPress) {
  TestNoPassword(KeyPress);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestShowTwice) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(2)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  // Calling Show again simply send LockCompleted signal.
  ScreenLocker::Show();
  EXPECT_TRUE(tester->IsLocked());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestEscape) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  tester->SetPassword("password");
  EXPECT_EQ("password", tester->GetPassword());
  // Escape clears the password.
  ui_controls::SendKeyPress(GTK_WINDOW(tester->GetWidget()->GetNativeView()),
                            ui::VKEY_ESCAPE, false, false, false, false);
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ("", tester->GetPassword());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

}  // namespace chromeos
<|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/sync_file_system/local_file_sync_service.h"

#include "base/stl_util.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync_file_system/local_change_processor.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "googleurl/src/gurl.h"
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_url.h"
#include "webkit/fileapi/syncable/file_change.h"
#include "webkit/fileapi/syncable/local_file_change_tracker.h"
#include "webkit/fileapi/syncable/local_file_sync_context.h"
#include "webkit/fileapi/syncable/sync_file_metadata.h"

using content::BrowserThread;
using fileapi::FileSystemURL;

namespace sync_file_system {

namespace {

void PrepareForProcessRemoteChangeCallbackAdapter(
    const RemoteChangeProcessor::PrepareChangeCallback& callback,
    SyncStatusCode status,
    const LocalFileSyncInfo& sync_file_info) {
  callback.Run(status, sync_file_info.metadata, sync_file_info.changes);
}

}  // namespace

LocalFileSyncService::OriginChangeMap::OriginChangeMap()
    : next_(change_count_map_.end()) {}
LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}

bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) {
  DCHECK(origin);
  if (change_count_map_.empty())
    return false;
  Map::iterator begin = next_;
  do {
    if (next_ == change_count_map_.end())
      next_ = change_count_map_.begin();
    DCHECK_NE(0, next_->second);
    *origin = next_++->first;
    if (!ContainsKey(disabled_origins_, *origin))
      return true;
  } while (next_ != begin);
  return false;
}

int64 LocalFileSyncService::OriginChangeMap::GetTotalChangeCount() const {
  int64 num_changes = 0;
  for (Map::const_iterator iter = change_count_map_.begin();
       iter != change_count_map_.end(); ++iter) {
    if (ContainsKey(disabled_origins_, iter->first))
      continue;
    num_changes += iter->second;
  }
  return num_changes;
}

void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
    const GURL& origin, int64 changes) {
  if (changes != 0) {
    change_count_map_[origin] = changes;
    return;
  }
  Map::iterator found = change_count_map_.find(origin);
  if (found != change_count_map_.end()) {
    if (next_ == found)
      ++next_;
    change_count_map_.erase(found);
  }
}

void LocalFileSyncService::OriginChangeMap::SetOriginEnabled(
    const GURL& origin, bool enabled) {
  if (enabled)
    disabled_origins_.erase(origin);
  else
    disabled_origins_.insert(origin);
}

// LocalFileSyncService -------------------------------------------------------

LocalFileSyncService::LocalFileSyncService(Profile* profile)
    : profile_(profile),
      sync_context_(new LocalFileSyncContext(
          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))),
      local_change_processor_(NULL) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  sync_context_->AddOriginChangeObserver(this);
}

LocalFileSyncService::~LocalFileSyncService() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

void LocalFileSyncService::Shutdown() {
  sync_context_->RemoveOriginChangeObserver(this);
  sync_context_->ShutdownOnUIThread();
  profile_ = NULL;
}

void LocalFileSyncService::MaybeInitializeFileSystemContext(
    const GURL& app_origin,
    const std::string& service_name,
    fileapi::FileSystemContext* file_system_context,
    const SyncStatusCallback& callback) {
  sync_context_->MaybeInitializeFileSystemContext(
      app_origin, service_name, file_system_context,
      base::Bind(&LocalFileSyncService::DidInitializeFileSystemContext,
                 AsWeakPtr(), app_origin,
                 make_scoped_refptr(file_system_context), callback));
}

void LocalFileSyncService::AddChangeObserver(Observer* observer) {
  change_observers_.AddObserver(observer);
}

void LocalFileSyncService::RegisterURLForWaitingSync(
    const FileSystemURL& url,
    const base::Closure& on_syncable_callback) {
  sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback);
}

void LocalFileSyncService::ProcessLocalChange(
    const SyncFileCallback& callback) {
  DCHECK(local_change_processor_);
  // Pick an origin to process next.
  GURL origin;
  if (!origin_change_map_.NextOriginToProcess(&origin)) {
    callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL());
    return;
  }
  DCHECK(local_sync_callback_.is_null());
  DCHECK(!origin.is_empty());
  DCHECK(ContainsKey(origin_to_contexts_, origin));

  local_sync_callback_ = callback;

  sync_context_->GetFileForLocalSync(
      origin_to_contexts_[origin],
      base::Bind(&LocalFileSyncService::DidGetFileForLocalSync,
                 AsWeakPtr()));
}

void LocalFileSyncService::SetLocalChangeProcessor(
    LocalChangeProcessor* processor) {
  local_change_processor_ = processor;
}

void LocalFileSyncService::HasPendingLocalChanges(
    const FileSystemURL& url,
    const HasPendingLocalChangeCallback& callback) {
  if (!ContainsKey(origin_to_contexts_, url.origin())) {
    base::MessageLoopProxy::current()->PostTask(
        FROM_HERE,
        base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false));
    return;
  }
  sync_context_->HasPendingLocalChanges(
      origin_to_contexts_[url.origin()], url, callback);
}

void LocalFileSyncService::ClearSyncFlagForURL(
    const FileSystemURL& url) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->ClearSyncFlagForURL(url);
}

void LocalFileSyncService::GetLocalFileMetadata(
    const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
                                 url, callback);
}

void LocalFileSyncService::PrepareForProcessRemoteChange(
    const FileSystemURL& url,
    const std::string& service_name,
    const PrepareChangeCallback& callback) {
  if (!ContainsKey(origin_to_contexts_, url.origin())) {
    // This could happen if a remote sync is triggered for the app that hasn't
    // been initialized in this service.
    DCHECK(profile_);
    // The given url.origin() must be for valid installed app.
    ExtensionService* extension_service =
        extensions::ExtensionSystem::Get(profile_)->extension_service();
    const extensions::Extension* extension = extension_service->GetInstalledApp(
        url.origin());
    DCHECK(extension);
    GURL site_url = extension_service->GetSiteForExtensionId(extension->id());
    DCHECK(!site_url.is_empty());
    scoped_refptr<fileapi::FileSystemContext> file_system_context =
        content::BrowserContext::GetStoragePartitionForSite(
            profile_, site_url)->GetFileSystemContext();
    MaybeInitializeFileSystemContext(
        url.origin(),
        service_name,
        file_system_context,
        base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
                   AsWeakPtr(), url, service_name,
                   file_system_context, callback));
    return;
  }

  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->PrepareForSync(
      origin_to_contexts_[url.origin()], url,
      base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
}

void LocalFileSyncService::ApplyRemoteChange(
    const FileChange& change,
    const base::FilePath& local_path,
    const FileSystemURL& url,
    const SyncStatusCallback& callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->ApplyRemoteChange(
      origin_to_contexts_[url.origin()],
      change, local_path, url, callback);
}

void LocalFileSyncService::ClearLocalChanges(
    const FileSystemURL& url,
    const base::Closure& completion_callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->ClearChangesForURL(origin_to_contexts_[url.origin()],
                                    url, completion_callback);
}

void LocalFileSyncService::RecordFakeLocalChange(
    const FileSystemURL& url,
    const FileChange& change,
    const SyncStatusCallback& callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
                                       url, change, callback);
}

void LocalFileSyncService::OnChangesAvailableInOrigins(
    const std::set<GURL>& origins) {
  bool need_notification = false;
  for (std::set<GURL>::const_iterator iter = origins.begin();
       iter != origins.end(); ++iter) {
    const GURL& origin = *iter;
    if (!ContainsKey(origin_to_contexts_, origin)) {
      // This could happen if this is called for apps/origins that haven't
      // been initialized yet, or for apps/origins that are disabled.
      // (Local change tracker could call this for uninitialized origins
      // while it's reading dirty files from the database in the
      // initialization phase.)
      pending_origins_with_changes_.insert(origin);
      continue;
    }
    need_notification = true;
    fileapi::FileSystemContext* context = origin_to_contexts_[origin];
    DCHECK(context->change_tracker());
    origin_change_map_.SetOriginChangeCount(
        origin, context->change_tracker()->num_changes());
  }
  if (!need_notification)
    return;
  int64 num_changes = origin_change_map_.GetTotalChangeCount();
  FOR_EACH_OBSERVER(Observer, change_observers_,
                    OnLocalChangeAvailable(num_changes));
}

void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
  if (!ContainsKey(origin_to_contexts_, origin))
    return;
  origin_change_map_.SetOriginEnabled(origin, enabled);
}

void LocalFileSyncService::DidInitializeFileSystemContext(
    const GURL& app_origin,
    fileapi::FileSystemContext* file_system_context,
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    callback.Run(status);
    return;
  }
  DCHECK(file_system_context);
  origin_to_contexts_[app_origin] = file_system_context;

  if (pending_origins_with_changes_.find(app_origin) !=
      pending_origins_with_changes_.end()) {
    // We have remaining changes for the origin.
    pending_origins_with_changes_.erase(app_origin);
    DCHECK(file_system_context->change_tracker());
    origin_change_map_.SetOriginChangeCount(
        app_origin, file_system_context->change_tracker()->num_changes());
    int64 num_changes = origin_change_map_.GetTotalChangeCount();
    FOR_EACH_OBSERVER(Observer, change_observers_,
                      OnLocalChangeAvailable(num_changes));
  }
  callback.Run(status);
}

void LocalFileSyncService::DidInitializeForRemoteSync(
    const FileSystemURL& url,
    const std::string& service_name,
    fileapi::FileSystemContext* file_system_context,
    const PrepareChangeCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    DVLOG(1) << "FileSystemContext initialization failed for remote sync:"
             << url.DebugString() << " status=" << status
             << " (" << SyncStatusCodeToString(status) << ")";
    callback.Run(status, SyncFileMetadata(), FileChangeList());
    return;
  }
  origin_to_contexts_[url.origin()] = file_system_context;
  PrepareForProcessRemoteChange(url, service_name, callback);
}

void LocalFileSyncService::RunLocalSyncCallback(
    SyncStatusCode status,
    const FileSystemURL& url) {
  DCHECK(!local_sync_callback_.is_null());
  SyncFileCallback callback = local_sync_callback_;
  local_sync_callback_.Reset();
  callback.Run(status, url);
}

void LocalFileSyncService::DidGetFileForLocalSync(
    SyncStatusCode status,
    const LocalFileSyncInfo& sync_file_info) {
  DCHECK(!local_sync_callback_.is_null());
  if (status != SYNC_STATUS_OK) {
    RunLocalSyncCallback(status, sync_file_info.url);
    return;
  }
  if (sync_file_info.changes.empty()) {
    // There's a slight chance this could happen.
    SyncFileCallback callback = local_sync_callback_;
    local_sync_callback_.Reset();
    ProcessLocalChange(callback);
    return;
  }

  DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
           << " change:" << sync_file_info.changes.front().DebugString();

  local_change_processor_->ApplyLocalChange(
      sync_file_info.changes.front(),
      sync_file_info.local_file_path,
      sync_file_info.metadata,
      sync_file_info.url,
      base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
                 AsWeakPtr(),
                 sync_file_info,
                 sync_file_info.changes.front(),
                 sync_file_info.changes.PopAndGetNewList()));
}

void LocalFileSyncService::ProcessNextChangeForURL(
    const LocalFileSyncInfo& sync_file_info,
    const FileChange& last_change,
    const FileChangeList& changes,
    SyncStatusCode status) {
  DVLOG(1) << "Processed one local change: "
           << sync_file_info.url.DebugString()
           << " change:" << last_change.DebugString()
           << " status:" << status;

  if (status == SYNC_FILE_ERROR_NOT_FOUND &&
      last_change.change() == FileChange::FILE_CHANGE_DELETE) {
    // This must be ok (and could happen).
    status = SYNC_STATUS_OK;
  }

  // TODO(kinuko,tzik): Handle other errors that should not be considered
  // a sync error.

  const FileSystemURL& url = sync_file_info.url;
  if (status != SYNC_STATUS_OK || changes.empty()) {
    if (status == SYNC_STATUS_OK || status == SYNC_STATUS_HAS_CONFLICT) {
      // Clear the recorded changes for the URL if the sync was successfull
      // OR has failed due to conflict (so that we won't stick to the same
      // conflicting file again and again).
      DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
      sync_context_->ClearChangesForURL(
          origin_to_contexts_[url.origin()], url,
          base::Bind(&LocalFileSyncService::RunLocalSyncCallback,
                     AsWeakPtr(), status, url));
      return;
    }
    RunLocalSyncCallback(status, url);
    return;
  }

  local_change_processor_->ApplyLocalChange(
      changes.front(),
      sync_file_info.local_file_path,
      sync_file_info.metadata,
      url,
      base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
                 AsWeakPtr(), sync_file_info,
                 changes.front(), changes.PopAndGetNewList()));
}

}  // namespace sync_file_system
<commit_msg>Quick fix: LocalFileSyncService::PrepareForProcessRemoteChange for uninstalled apps should not crash<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/sync_file_system/local_file_sync_service.h"

#include "base/stl_util.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync_file_system/local_change_processor.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "googleurl/src/gurl.h"
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_url.h"
#include "webkit/fileapi/syncable/file_change.h"
#include "webkit/fileapi/syncable/local_file_change_tracker.h"
#include "webkit/fileapi/syncable/local_file_sync_context.h"
#include "webkit/fileapi/syncable/sync_file_metadata.h"

using content::BrowserThread;
using fileapi::FileSystemURL;

namespace sync_file_system {

namespace {

void PrepareForProcessRemoteChangeCallbackAdapter(
    const RemoteChangeProcessor::PrepareChangeCallback& callback,
    SyncStatusCode status,
    const LocalFileSyncInfo& sync_file_info) {
  callback.Run(status, sync_file_info.metadata, sync_file_info.changes);
}

}  // namespace

LocalFileSyncService::OriginChangeMap::OriginChangeMap()
    : next_(change_count_map_.end()) {}
LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}

bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) {
  DCHECK(origin);
  if (change_count_map_.empty())
    return false;
  Map::iterator begin = next_;
  do {
    if (next_ == change_count_map_.end())
      next_ = change_count_map_.begin();
    DCHECK_NE(0, next_->second);
    *origin = next_++->first;
    if (!ContainsKey(disabled_origins_, *origin))
      return true;
  } while (next_ != begin);
  return false;
}

int64 LocalFileSyncService::OriginChangeMap::GetTotalChangeCount() const {
  int64 num_changes = 0;
  for (Map::const_iterator iter = change_count_map_.begin();
       iter != change_count_map_.end(); ++iter) {
    if (ContainsKey(disabled_origins_, iter->first))
      continue;
    num_changes += iter->second;
  }
  return num_changes;
}

void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
    const GURL& origin, int64 changes) {
  if (changes != 0) {
    change_count_map_[origin] = changes;
    return;
  }
  Map::iterator found = change_count_map_.find(origin);
  if (found != change_count_map_.end()) {
    if (next_ == found)
      ++next_;
    change_count_map_.erase(found);
  }
}

void LocalFileSyncService::OriginChangeMap::SetOriginEnabled(
    const GURL& origin, bool enabled) {
  if (enabled)
    disabled_origins_.erase(origin);
  else
    disabled_origins_.insert(origin);
}

// LocalFileSyncService -------------------------------------------------------

LocalFileSyncService::LocalFileSyncService(Profile* profile)
    : profile_(profile),
      sync_context_(new LocalFileSyncContext(
          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))),
      local_change_processor_(NULL) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  sync_context_->AddOriginChangeObserver(this);
}

LocalFileSyncService::~LocalFileSyncService() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

void LocalFileSyncService::Shutdown() {
  sync_context_->RemoveOriginChangeObserver(this);
  sync_context_->ShutdownOnUIThread();
  profile_ = NULL;
}

void LocalFileSyncService::MaybeInitializeFileSystemContext(
    const GURL& app_origin,
    const std::string& service_name,
    fileapi::FileSystemContext* file_system_context,
    const SyncStatusCallback& callback) {
  sync_context_->MaybeInitializeFileSystemContext(
      app_origin, service_name, file_system_context,
      base::Bind(&LocalFileSyncService::DidInitializeFileSystemContext,
                 AsWeakPtr(), app_origin,
                 make_scoped_refptr(file_system_context), callback));
}

void LocalFileSyncService::AddChangeObserver(Observer* observer) {
  change_observers_.AddObserver(observer);
}

void LocalFileSyncService::RegisterURLForWaitingSync(
    const FileSystemURL& url,
    const base::Closure& on_syncable_callback) {
  sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback);
}

void LocalFileSyncService::ProcessLocalChange(
    const SyncFileCallback& callback) {
  DCHECK(local_change_processor_);
  // Pick an origin to process next.
  GURL origin;
  if (!origin_change_map_.NextOriginToProcess(&origin)) {
    callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL());
    return;
  }
  DCHECK(local_sync_callback_.is_null());
  DCHECK(!origin.is_empty());
  DCHECK(ContainsKey(origin_to_contexts_, origin));

  local_sync_callback_ = callback;

  sync_context_->GetFileForLocalSync(
      origin_to_contexts_[origin],
      base::Bind(&LocalFileSyncService::DidGetFileForLocalSync,
                 AsWeakPtr()));
}

void LocalFileSyncService::SetLocalChangeProcessor(
    LocalChangeProcessor* processor) {
  local_change_processor_ = processor;
}

void LocalFileSyncService::HasPendingLocalChanges(
    const FileSystemURL& url,
    const HasPendingLocalChangeCallback& callback) {
  if (!ContainsKey(origin_to_contexts_, url.origin())) {
    base::MessageLoopProxy::current()->PostTask(
        FROM_HERE,
        base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false));
    return;
  }
  sync_context_->HasPendingLocalChanges(
      origin_to_contexts_[url.origin()], url, callback);
}

void LocalFileSyncService::ClearSyncFlagForURL(
    const FileSystemURL& url) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->ClearSyncFlagForURL(url);
}

void LocalFileSyncService::GetLocalFileMetadata(
    const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
                                 url, callback);
}

void LocalFileSyncService::PrepareForProcessRemoteChange(
    const FileSystemURL& url,
    const std::string& service_name,
    const PrepareChangeCallback& callback) {
  if (!ContainsKey(origin_to_contexts_, url.origin())) {
    // This could happen if a remote sync is triggered for the app that hasn't
    // been initialized in this service.
    DCHECK(profile_);
    // The given url.origin() must be for valid installed app.
    ExtensionService* extension_service =
        extensions::ExtensionSystem::Get(profile_)->extension_service();
    const extensions::Extension* extension = extension_service->GetInstalledApp(
        url.origin());
    if (!extension) {
      LOG(WARNING) << "PrepareForProcessRemoteChange called for non-existing "
                   << " origin:" << url.origin().spec();
      // The extension has been uninstalled and this method is called
      // before the remote changes for the origin are removed.
      callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC,
                   SyncFileMetadata(), FileChangeList());
      return;
    }
    GURL site_url = extension_service->GetSiteForExtensionId(extension->id());
    DCHECK(!site_url.is_empty());
    scoped_refptr<fileapi::FileSystemContext> file_system_context =
        content::BrowserContext::GetStoragePartitionForSite(
            profile_, site_url)->GetFileSystemContext();
    MaybeInitializeFileSystemContext(
        url.origin(),
        service_name,
        file_system_context,
        base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
                   AsWeakPtr(), url, service_name,
                   file_system_context, callback));
    return;
  }

  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->PrepareForSync(
      origin_to_contexts_[url.origin()], url,
      base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
}

void LocalFileSyncService::ApplyRemoteChange(
    const FileChange& change,
    const base::FilePath& local_path,
    const FileSystemURL& url,
    const SyncStatusCallback& callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->ApplyRemoteChange(
      origin_to_contexts_[url.origin()],
      change, local_path, url, callback);
}

void LocalFileSyncService::ClearLocalChanges(
    const FileSystemURL& url,
    const base::Closure& completion_callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->ClearChangesForURL(origin_to_contexts_[url.origin()],
                                    url, completion_callback);
}

void LocalFileSyncService::RecordFakeLocalChange(
    const FileSystemURL& url,
    const FileChange& change,
    const SyncStatusCallback& callback) {
  DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
  sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
                                       url, change, callback);
}

void LocalFileSyncService::OnChangesAvailableInOrigins(
    const std::set<GURL>& origins) {
  bool need_notification = false;
  for (std::set<GURL>::const_iterator iter = origins.begin();
       iter != origins.end(); ++iter) {
    const GURL& origin = *iter;
    if (!ContainsKey(origin_to_contexts_, origin)) {
      // This could happen if this is called for apps/origins that haven't
      // been initialized yet, or for apps/origins that are disabled.
      // (Local change tracker could call this for uninitialized origins
      // while it's reading dirty files from the database in the
      // initialization phase.)
      pending_origins_with_changes_.insert(origin);
      continue;
    }
    need_notification = true;
    fileapi::FileSystemContext* context = origin_to_contexts_[origin];
    DCHECK(context->change_tracker());
    origin_change_map_.SetOriginChangeCount(
        origin, context->change_tracker()->num_changes());
  }
  if (!need_notification)
    return;
  int64 num_changes = origin_change_map_.GetTotalChangeCount();
  FOR_EACH_OBSERVER(Observer, change_observers_,
                    OnLocalChangeAvailable(num_changes));
}

void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
  if (!ContainsKey(origin_to_contexts_, origin))
    return;
  origin_change_map_.SetOriginEnabled(origin, enabled);
}

void LocalFileSyncService::DidInitializeFileSystemContext(
    const GURL& app_origin,
    fileapi::FileSystemContext* file_system_context,
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    callback.Run(status);
    return;
  }
  DCHECK(file_system_context);
  origin_to_contexts_[app_origin] = file_system_context;

  if (pending_origins_with_changes_.find(app_origin) !=
      pending_origins_with_changes_.end()) {
    // We have remaining changes for the origin.
    pending_origins_with_changes_.erase(app_origin);
    DCHECK(file_system_context->change_tracker());
    origin_change_map_.SetOriginChangeCount(
        app_origin, file_system_context->change_tracker()->num_changes());
    int64 num_changes = origin_change_map_.GetTotalChangeCount();
    FOR_EACH_OBSERVER(Observer, change_observers_,
                      OnLocalChangeAvailable(num_changes));
  }
  callback.Run(status);
}

void LocalFileSyncService::DidInitializeForRemoteSync(
    const FileSystemURL& url,
    const std::string& service_name,
    fileapi::FileSystemContext* file_system_context,
    const PrepareChangeCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    DVLOG(1) << "FileSystemContext initialization failed for remote sync:"
             << url.DebugString() << " status=" << status
             << " (" << SyncStatusCodeToString(status) << ")";
    callback.Run(status, SyncFileMetadata(), FileChangeList());
    return;
  }
  origin_to_contexts_[url.origin()] = file_system_context;
  PrepareForProcessRemoteChange(url, service_name, callback);
}

void LocalFileSyncService::RunLocalSyncCallback(
    SyncStatusCode status,
    const FileSystemURL& url) {
  DCHECK(!local_sync_callback_.is_null());
  SyncFileCallback callback = local_sync_callback_;
  local_sync_callback_.Reset();
  callback.Run(status, url);
}

void LocalFileSyncService::DidGetFileForLocalSync(
    SyncStatusCode status,
    const LocalFileSyncInfo& sync_file_info) {
  DCHECK(!local_sync_callback_.is_null());
  if (status != SYNC_STATUS_OK) {
    RunLocalSyncCallback(status, sync_file_info.url);
    return;
  }
  if (sync_file_info.changes.empty()) {
    // There's a slight chance this could happen.
    SyncFileCallback callback = local_sync_callback_;
    local_sync_callback_.Reset();
    ProcessLocalChange(callback);
    return;
  }

  DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
           << " change:" << sync_file_info.changes.front().DebugString();

  local_change_processor_->ApplyLocalChange(
      sync_file_info.changes.front(),
      sync_file_info.local_file_path,
      sync_file_info.metadata,
      sync_file_info.url,
      base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
                 AsWeakPtr(),
                 sync_file_info,
                 sync_file_info.changes.front(),
                 sync_file_info.changes.PopAndGetNewList()));
}

void LocalFileSyncService::ProcessNextChangeForURL(
    const LocalFileSyncInfo& sync_file_info,
    const FileChange& last_change,
    const FileChangeList& changes,
    SyncStatusCode status) {
  DVLOG(1) << "Processed one local change: "
           << sync_file_info.url.DebugString()
           << " change:" << last_change.DebugString()
           << " status:" << status;

  if (status == SYNC_FILE_ERROR_NOT_FOUND &&
      last_change.change() == FileChange::FILE_CHANGE_DELETE) {
    // This must be ok (and could happen).
    status = SYNC_STATUS_OK;
  }

  // TODO(kinuko,tzik): Handle other errors that should not be considered
  // a sync error.

  const FileSystemURL& url = sync_file_info.url;
  if (status != SYNC_STATUS_OK || changes.empty()) {
    if (status == SYNC_STATUS_OK || status == SYNC_STATUS_HAS_CONFLICT) {
      // Clear the recorded changes for the URL if the sync was successfull
      // OR has failed due to conflict (so that we won't stick to the same
      // conflicting file again and again).
      DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
      sync_context_->ClearChangesForURL(
          origin_to_contexts_[url.origin()], url,
          base::Bind(&LocalFileSyncService::RunLocalSyncCallback,
                     AsWeakPtr(), status, url));
      return;
    }
    RunLocalSyncCallback(status, url);
    return;
  }

  local_change_processor_->ApplyLocalChange(
      changes.front(),
      sync_file_info.local_file_path,
      sync_file_info.metadata,
      url,
      base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
                 AsWeakPtr(), sync_file_info,
                 changes.front(), changes.PopAndGetNewList()));
}

}  // namespace sync_file_system
<|endoftext|>


<commit_before><commit_msg>Type auto casting when allocating arrays<commit_after><|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "CheckUndefinedSymbols.h"
#include "Overview.h"

#include <Names.h>
#include <Literals.h>
#include <Symbols.h>
#include <TranslationUnit.h>
#include <Scope.h>
#include <AST.h>

using namespace CPlusPlus;

CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc)
    : ASTVisitor(doc->control()), _doc(doc)
{ }

CheckUndefinedSymbols::~CheckUndefinedSymbols()
{ }

void CheckUndefinedSymbols::setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding)
{
    _globalNamespaceBinding = globalNamespaceBinding;
    _types.clear();
    _protocols.clear();

    if (_globalNamespaceBinding) {
        QSet<NamespaceBinding *> processed;
        buildTypeMap(_globalNamespaceBinding.data(), &processed);
    }
}

void CheckUndefinedSymbols::operator()(AST *ast)
{ accept(ast); }

QByteArray CheckUndefinedSymbols::templateParameterName(NameAST *ast) const
{
    if (ast && ast->name) {
        if (Identifier *id = ast->name->identifier())
            return QByteArray::fromRawData(id->chars(), id->size());
    }

    return QByteArray();
}

QByteArray CheckUndefinedSymbols::templateParameterName(DeclarationAST *ast) const
{
    if (ast) {
        if (TypenameTypeParameterAST *d = ast->asTypenameTypeParameter())
            return templateParameterName(d->name);
        else if (TemplateTypeParameterAST *d = ast->asTemplateTypeParameter())
            return templateParameterName(d->name);
    }
    return QByteArray();
}

bool CheckUndefinedSymbols::isType(const QByteArray &name) const
{
    for (int i = _compoundStatementStack.size() - 1; i != -1; --i) {
        Scope *members = _compoundStatementStack.at(i)->symbol->members();

        for (unsigned m = 0; m < members->symbolCount(); ++m) {
            Symbol *member = members->symbolAt(m);

            if (member->isTypedef() && member->isDeclaration()) {
                if (Identifier *id = member->identifier()) {
                    if (name == id->chars())
                        return true;
                }
            }
        }
    }

    for (int i = _templateDeclarationStack.size() - 1; i != - 1; --i) {
        TemplateDeclarationAST *templateDeclaration = _templateDeclarationStack.at(i);

        for (DeclarationListAST *it = templateDeclaration->template_parameters; it; it = it->next) {
            DeclarationAST *templateParameter = it->declaration;

            if (templateParameterName(templateParameter) == name)
                return true;
        }
    }

    return _types.contains(name);
}

bool CheckUndefinedSymbols::isType(Identifier *id) const
{
    if (! id)
        return false;

    return isType(QByteArray::fromRawData(id->chars(), id->size()));
}

void CheckUndefinedSymbols::addType(Name *name)
{
    if (! name)
        return;

    if (Identifier *id = name->identifier())
        _types.insert(QByteArray(id->chars(), id->size()));
}

void CheckUndefinedSymbols::addProtocol(Name *name)
{
    if (!name)
        return;

    if (Identifier *id = name->identifier())
        _protocols.insert(QByteArray(id->chars(), id->size()));
}

bool CheckUndefinedSymbols::isProtocol(const QByteArray &name) const
{
    return _protocols.contains(name);
}

void CheckUndefinedSymbols::buildTypeMap(Class *klass)
{
    addType(klass->name());

    for (unsigned i = 0; i < klass->memberCount(); ++i) {
        buildMemberTypeMap(klass->memberAt(i));
    }
}

void CheckUndefinedSymbols::buildMemberTypeMap(Symbol *member)
{
    if (member == 0)
        return;

    if (Class *klass = member->asClass()) {
        buildTypeMap(klass);
    } else if (Enum *e = member->asEnum()) {
        addType(e->name());
    } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) {
        addType(fwd->name());
    } else if (Declaration *decl = member->asDeclaration()) {
        if (decl->isTypedef())
            addType(decl->name());
    }
}

void CheckUndefinedSymbols::buildTypeMap(NamespaceBinding *binding, QSet<NamespaceBinding *> *processed)
{
    if (! processed->contains(binding)) {
        processed->insert(binding);

        if (Identifier *id = binding->identifier()) {
            _namespaceNames.insert(QByteArray(id->chars(), id->size()));
        }

        foreach (Namespace *ns, binding->symbols) {
            for (unsigned i = 0; i < ns->memberCount(); ++i) {
                Symbol *member = ns->memberAt(i);

                if (Class *klass = member->asClass()) {
                    buildTypeMap(klass);
                } else if (Enum *e = member->asEnum()) {
                    addType(e->name());
                } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) {
                    addType(fwd->name());
                } else if (Declaration *decl = member->asDeclaration()) {
                    if (decl->isTypedef())
                        addType(decl->name());
                } else if (ObjCForwardClassDeclaration *fKlass = member->asObjCForwardClassDeclaration()) {
                    addType(fKlass->name());
                } else if (ObjCClass *klass = member->asObjCClass()) {
                    addType(klass->name());

                    for (unsigned i = 0; i < klass->memberCount(); ++i)
                        buildMemberTypeMap(klass->memberAt(i));
                } else if (ObjCForwardProtocolDeclaration *fProto = member->asObjCForwardProtocolDeclaration()) {
                    addProtocol(fProto->name());
                } else if (ObjCProtocol *proto = member->asObjCProtocol()) {
                    addProtocol(proto->name());

                    for (unsigned i = 0; i < proto->memberCount(); ++i)
                        buildMemberTypeMap(proto->memberAt(i));
                }
            }
        }

        foreach (NamespaceBinding *childBinding, binding->children) {
            buildTypeMap(childBinding, processed);
        }
    }
}

FunctionDeclaratorAST *CheckUndefinedSymbols::currentFunctionDeclarator() const
{
    if (_functionDeclaratorStack.isEmpty())
        return 0;

    return _functionDeclaratorStack.last();
}

CompoundStatementAST *CheckUndefinedSymbols::compoundStatement() const
{
    if (_compoundStatementStack.isEmpty())
        return 0;

    return _compoundStatementStack.last();
}

bool CheckUndefinedSymbols::visit(FunctionDeclaratorAST *ast)
{
    _functionDeclaratorStack.append(ast);
    return true;
}

void CheckUndefinedSymbols::endVisit(FunctionDeclaratorAST *)
{
    _functionDeclaratorStack.removeLast();
}

bool CheckUndefinedSymbols::visit(TypeofSpecifierAST *ast)
{
    accept(ast->next);
    return false;
}

bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *ast)
{
    if (ast->name) {
        if (! ast->name->name) {
            unsigned line, col;
            getTokenStartPosition(ast->firstToken(), &line, &col);
            // qWarning() << _doc->fileName() << line << col;
        } else if (Identifier *id = ast->name->name->identifier()) {
            if (! isType(id)) {
                if (FunctionDeclaratorAST *functionDeclarator = currentFunctionDeclarator()) {
                    if (functionDeclarator->as_cpp_initializer)
                        return true;
                }

                Overview oo;
                translationUnit()->warning(ast->firstToken(), "`%s' is not a type name",
                                           qPrintable(oo(ast->name->name)));
            }
        }
    }
    return true;
}

bool CheckUndefinedSymbols::visit(TemplateDeclarationAST *ast)
{
    _templateDeclarationStack.append(ast);
    return true;
}

void CheckUndefinedSymbols::endVisit(TemplateDeclarationAST *)
{
    _templateDeclarationStack.removeLast();
}

bool CheckUndefinedSymbols::visit(ClassSpecifierAST *ast)
{
    if (ast->base_clause) {
        unsigned line, col;
        getTokenStartPosition(ast->firstToken(), &line, &col);
    }

    bool hasQ_OBJECT_CHECK = false;

    if (ast->symbol) {
        Class *klass = ast->symbol->asClass();

        for (unsigned i = 0; i < klass->memberCount(); ++i) {
            Symbol *symbol = klass->memberAt(i);

            if (symbol->name() && symbol->name()->isNameId()) {
                NameId *nameId = symbol->name()->asNameId();

                if (! qstrcmp(nameId->identifier()->chars(), "qt_check_for_QOBJECT_macro")) {
                    hasQ_OBJECT_CHECK = true;
                    break;
                }
            }
        }
    }

    _qobjectStack.append(hasQ_OBJECT_CHECK);

    return true;
}

void CheckUndefinedSymbols::endVisit(ClassSpecifierAST *)
{ _qobjectStack.removeLast(); }

bool CheckUndefinedSymbols::qobjectCheck() const
{
    if (_qobjectStack.isEmpty())
        return false;

    return _qobjectStack.last();
}

bool CheckUndefinedSymbols::visit(FunctionDefinitionAST *ast)
{
    if (ast->symbol) {
        Function *fun = ast->symbol->asFunction();
        if ((fun->isSignal() || fun->isSlot()) && ! qobjectCheck()) {
            translationUnit()->warning(ast->firstToken(),
                                       "you forgot the Q_OBJECT macro");
        }
    }
    return true;
}

void CheckUndefinedSymbols::endVisit(FunctionDefinitionAST *)
{ }

bool CheckUndefinedSymbols::visit(CompoundStatementAST *ast)
{
    _compoundStatementStack.append(ast);
    return true;
}

void CheckUndefinedSymbols::endVisit(CompoundStatementAST *)
{
    _compoundStatementStack.removeLast();
}

bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *ast)
{
    const bool check = qobjectCheck();
    for (List<Declaration *> *it = ast->symbols; it; it = it->next) {
        Declaration *decl = it->value;

        if (Function *fun = decl->type()->asFunctionType()) {
            if ((fun->isSignal() || fun->isSlot()) && ! check) {
                translationUnit()->warning(ast->firstToken(),
                                           "you forgot the Q_OBJECT macro");
            }
        }
    }
    return true;
}

bool CheckUndefinedSymbols::visit(BaseSpecifierAST *base)
{
    if (NameAST *nameAST = base->name) {
        bool resolvedBaseClassName = false;

        if (Name *name = nameAST->name) {
            Identifier *id = name->identifier();
            const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
            if (isType(spell))
                resolvedBaseClassName = true;
        }

        if (! resolvedBaseClassName) {
            const char *token = "after `:'";

            if (base->comma_token)
                token = "after `,'";

            translationUnit()->warning(nameAST->firstToken(),
                                       "expected class-name %s token", token);
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(UsingDirectiveAST *ast)
{
    if (ast->symbol && ast->symbol->name() && _globalNamespaceBinding) {
        const Location loc = Location(ast->symbol);

        NamespaceBinding *binding = _globalNamespaceBinding.data();

        if (Scope *enclosingNamespaceScope = ast->symbol->enclosingNamespaceScope())
            binding = NamespaceBinding::find(enclosingNamespaceScope->owner()->asNamespace(), binding);

        if (! binding || ! binding->resolveNamespace(loc, ast->symbol->name())) {
            translationUnit()->warning(ast->name->firstToken(),
                                       "expected a namespace");
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(QualifiedNameAST *ast)
{
    if (ast->name) {
        QualifiedNameId *q = ast->name->asQualifiedNameId();
        for (unsigned i = 0; i < q->nameCount() - 1; ++i) {
            Name *name = q->nameAt(i);
            if (Identifier *id = name->identifier()) {
                const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
                if (! (_namespaceNames.contains(spell) || isType(id))) {
                    translationUnit()->warning(ast->firstToken(),
                                               "`%s' is not a namespace or class name",
                                               spell.constData());
                }
            }
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(CastExpressionAST *ast)
{
    if (ast->lparen_token && ast->type_id && ast->rparen_token && ast->expression) {
        if (TypeIdAST *cast_type_id = ast->type_id->asTypeId()) {
            SpecifierAST *type_specifier = cast_type_id->type_specifier;
            if (! cast_type_id->declarator && type_specifier && ! type_specifier->next &&
                type_specifier->asNamedTypeSpecifier() && ast->expression &&
                ast->expression->asUnaryExpression()) {
                // this ast node is ambigious, e.g.
                //   (a) + b
                // it can be parsed as
                //   ((a) + b)
                // or
                //   (a) (+b)
                accept(ast->expression);
                return false;
            }
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(SizeofExpressionAST *ast)
{
    if (ast->lparen_token && ast->expression && ast->rparen_token) {
        if (TypeIdAST *type_id = ast->expression->asTypeId()) {
            SpecifierAST *type_specifier = type_id->type_specifier;
            if (! type_id->declarator && type_specifier && ! type_specifier->next &&
                type_specifier->asNamedTypeSpecifier()) {
                // this sizeof expression is ambiguos, e.g.
                // sizeof (a)
                //   `a' can be a typeid or a nested-expression.
                return false;
            } else if (type_id->declarator
                       &&   type_id->declarator->postfix_declarators
                       && ! type_id->declarator->postfix_declarators->next
                       &&   type_id->declarator->postfix_declarators->asArrayDeclarator() != 0) {
                // this sizeof expression is ambiguos, e.g.
                // sizeof(a[10])
                //   `a' can be a typeid or an expression.
                return false;
            }
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(ObjCClassDeclarationAST *ast)
{
    if (NameAST *nameAST = ast->superclass) {
        bool resolvedSuperClassName = false;

        if (Name *name = nameAST->name) {
            Identifier *id = name->identifier();
            const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
            if (isType(spell))
                resolvedSuperClassName = true;
        }

        if (! resolvedSuperClassName) {
            translationUnit()->warning(nameAST->firstToken(),
                                       "expected class-name after ':' token");
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(ObjCProtocolRefsAST *ast)
{
    for (IdentifierListAST *iter = ast->identifier_list; iter; iter = iter->next) {
        if (NameAST *nameAST = iter->name) {
            bool resolvedProtocolName = false;

            if (Name *name = nameAST->name) {
                Identifier *id = name->identifier();
                const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
                if (isProtocol(spell))
                    resolvedProtocolName = true;
            }

            if (!resolvedProtocolName) {
                char after;

                if (iter == ast->identifier_list)
                    after = '<';
                else
                    after = ',';

                translationUnit()->warning(nameAST->firstToken(), "expected protocol name after '%c' token", after);
            }
        }
    }

    return false;
}
<commit_msg>Fixed: Template argument marked as "not a type name" when defined as primitive type<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "CheckUndefinedSymbols.h"
#include "Overview.h"

#include <Names.h>
#include <Literals.h>
#include <Symbols.h>
#include <TranslationUnit.h>
#include <Scope.h>
#include <AST.h>

using namespace CPlusPlus;

CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc)
    : ASTVisitor(doc->control()), _doc(doc)
{ }

CheckUndefinedSymbols::~CheckUndefinedSymbols()
{ }

void CheckUndefinedSymbols::setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding)
{
    _globalNamespaceBinding = globalNamespaceBinding;
    _types.clear();
    _protocols.clear();

    if (_globalNamespaceBinding) {
        QSet<NamespaceBinding *> processed;
        buildTypeMap(_globalNamespaceBinding.data(), &processed);
    }
}

void CheckUndefinedSymbols::operator()(AST *ast)
{ accept(ast); }

QByteArray CheckUndefinedSymbols::templateParameterName(NameAST *ast) const
{
    if (ast && ast->name) {
        if (Identifier *id = ast->name->identifier())
            return QByteArray::fromRawData(id->chars(), id->size());
    }

    return QByteArray();
}

QByteArray CheckUndefinedSymbols::templateParameterName(DeclarationAST *ast) const
{
    if (ast) {
        if (TypenameTypeParameterAST *d = ast->asTypenameTypeParameter())
            return templateParameterName(d->name);
        else if (TemplateTypeParameterAST *d = ast->asTemplateTypeParameter())
            return templateParameterName(d->name);
        else if (ParameterDeclarationAST *d = ast->asParameterDeclaration()) {
            if (d->symbol) {
                if (const Identifier *id = d->symbol->identifier())
                    return QByteArray::fromRawData(id->chars(), id->size());
            }
        }
    }
    return QByteArray();
}

bool CheckUndefinedSymbols::isType(const QByteArray &name) const
{
    for (int i = _compoundStatementStack.size() - 1; i != -1; --i) {
        Scope *members = _compoundStatementStack.at(i)->symbol->members();

        for (unsigned m = 0; m < members->symbolCount(); ++m) {
            Symbol *member = members->symbolAt(m);

            if (member->isTypedef() && member->isDeclaration()) {
                if (Identifier *id = member->identifier()) {
                    if (name == id->chars())
                        return true;
                }
            }
        }
    }

    for (int i = _templateDeclarationStack.size() - 1; i != - 1; --i) {
        TemplateDeclarationAST *templateDeclaration = _templateDeclarationStack.at(i);

        for (DeclarationListAST *it = templateDeclaration->template_parameters; it; it = it->next) {
            DeclarationAST *templateParameter = it->declaration;

            if (templateParameterName(templateParameter) == name)
                return true;
        }
    }

    return _types.contains(name);
}

bool CheckUndefinedSymbols::isType(Identifier *id) const
{
    if (! id)
        return false;

    return isType(QByteArray::fromRawData(id->chars(), id->size()));
}

void CheckUndefinedSymbols::addType(Name *name)
{
    if (! name)
        return;

    if (Identifier *id = name->identifier())
        _types.insert(QByteArray(id->chars(), id->size()));
}

void CheckUndefinedSymbols::addProtocol(Name *name)
{
    if (!name)
        return;

    if (Identifier *id = name->identifier())
        _protocols.insert(QByteArray(id->chars(), id->size()));
}

bool CheckUndefinedSymbols::isProtocol(const QByteArray &name) const
{
    return _protocols.contains(name);
}

void CheckUndefinedSymbols::buildTypeMap(Class *klass)
{
    addType(klass->name());

    for (unsigned i = 0; i < klass->memberCount(); ++i) {
        buildMemberTypeMap(klass->memberAt(i));
    }
}

void CheckUndefinedSymbols::buildMemberTypeMap(Symbol *member)
{
    if (member == 0)
        return;

    if (Class *klass = member->asClass()) {
        buildTypeMap(klass);
    } else if (Enum *e = member->asEnum()) {
        addType(e->name());
    } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) {
        addType(fwd->name());
    } else if (Declaration *decl = member->asDeclaration()) {
        if (decl->isTypedef())
            addType(decl->name());
    }
}

void CheckUndefinedSymbols::buildTypeMap(NamespaceBinding *binding, QSet<NamespaceBinding *> *processed)
{
    if (! processed->contains(binding)) {
        processed->insert(binding);

        if (Identifier *id = binding->identifier()) {
            _namespaceNames.insert(QByteArray(id->chars(), id->size()));
        }

        foreach (Namespace *ns, binding->symbols) {
            for (unsigned i = 0; i < ns->memberCount(); ++i) {
                Symbol *member = ns->memberAt(i);

                if (Class *klass = member->asClass()) {
                    buildTypeMap(klass);
                } else if (Enum *e = member->asEnum()) {
                    addType(e->name());
                } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) {
                    addType(fwd->name());
                } else if (Declaration *decl = member->asDeclaration()) {
                    if (decl->isTypedef())
                        addType(decl->name());
                } else if (ObjCForwardClassDeclaration *fKlass = member->asObjCForwardClassDeclaration()) {
                    addType(fKlass->name());
                } else if (ObjCClass *klass = member->asObjCClass()) {
                    addType(klass->name());

                    for (unsigned i = 0; i < klass->memberCount(); ++i)
                        buildMemberTypeMap(klass->memberAt(i));
                } else if (ObjCForwardProtocolDeclaration *fProto = member->asObjCForwardProtocolDeclaration()) {
                    addProtocol(fProto->name());
                } else if (ObjCProtocol *proto = member->asObjCProtocol()) {
                    addProtocol(proto->name());

                    for (unsigned i = 0; i < proto->memberCount(); ++i)
                        buildMemberTypeMap(proto->memberAt(i));
                }
            }
        }

        foreach (NamespaceBinding *childBinding, binding->children) {
            buildTypeMap(childBinding, processed);
        }
    }
}

FunctionDeclaratorAST *CheckUndefinedSymbols::currentFunctionDeclarator() const
{
    if (_functionDeclaratorStack.isEmpty())
        return 0;

    return _functionDeclaratorStack.last();
}

CompoundStatementAST *CheckUndefinedSymbols::compoundStatement() const
{
    if (_compoundStatementStack.isEmpty())
        return 0;

    return _compoundStatementStack.last();
}

bool CheckUndefinedSymbols::visit(FunctionDeclaratorAST *ast)
{
    _functionDeclaratorStack.append(ast);
    return true;
}

void CheckUndefinedSymbols::endVisit(FunctionDeclaratorAST *)
{
    _functionDeclaratorStack.removeLast();
}

bool CheckUndefinedSymbols::visit(TypeofSpecifierAST *ast)
{
    accept(ast->next);
    return false;
}

bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *ast)
{
    if (ast->name) {
        if (! ast->name->name) {
            unsigned line, col;
            getTokenStartPosition(ast->firstToken(), &line, &col);
            // qWarning() << _doc->fileName() << line << col;
        } else if (Identifier *id = ast->name->name->identifier()) {
            if (! isType(id)) {
                if (FunctionDeclaratorAST *functionDeclarator = currentFunctionDeclarator()) {
                    if (functionDeclarator->as_cpp_initializer)
                        return true;
                }

                Overview oo;
                translationUnit()->warning(ast->firstToken(), "`%s' is not a type name",
                                           qPrintable(oo(ast->name->name)));
            }
        }
    }
    return true;
}

bool CheckUndefinedSymbols::visit(TemplateDeclarationAST *ast)
{
    _templateDeclarationStack.append(ast);
    return true;
}

void CheckUndefinedSymbols::endVisit(TemplateDeclarationAST *)
{
    _templateDeclarationStack.removeLast();
}

bool CheckUndefinedSymbols::visit(ClassSpecifierAST *ast)
{
    if (ast->base_clause) {
        unsigned line, col;
        getTokenStartPosition(ast->firstToken(), &line, &col);
    }

    bool hasQ_OBJECT_CHECK = false;

    if (ast->symbol) {
        Class *klass = ast->symbol->asClass();

        for (unsigned i = 0; i < klass->memberCount(); ++i) {
            Symbol *symbol = klass->memberAt(i);

            if (symbol->name() && symbol->name()->isNameId()) {
                NameId *nameId = symbol->name()->asNameId();

                if (! qstrcmp(nameId->identifier()->chars(), "qt_check_for_QOBJECT_macro")) {
                    hasQ_OBJECT_CHECK = true;
                    break;
                }
            }
        }
    }

    _qobjectStack.append(hasQ_OBJECT_CHECK);

    return true;
}

void CheckUndefinedSymbols::endVisit(ClassSpecifierAST *)
{ _qobjectStack.removeLast(); }

bool CheckUndefinedSymbols::qobjectCheck() const
{
    if (_qobjectStack.isEmpty())
        return false;

    return _qobjectStack.last();
}

bool CheckUndefinedSymbols::visit(FunctionDefinitionAST *ast)
{
    if (ast->symbol) {
        Function *fun = ast->symbol->asFunction();
        if ((fun->isSignal() || fun->isSlot()) && ! qobjectCheck()) {
            translationUnit()->warning(ast->firstToken(),
                                       "you forgot the Q_OBJECT macro");
        }
    }
    return true;
}

void CheckUndefinedSymbols::endVisit(FunctionDefinitionAST *)
{ }

bool CheckUndefinedSymbols::visit(CompoundStatementAST *ast)
{
    _compoundStatementStack.append(ast);
    return true;
}

void CheckUndefinedSymbols::endVisit(CompoundStatementAST *)
{
    _compoundStatementStack.removeLast();
}

bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *ast)
{
    const bool check = qobjectCheck();
    for (List<Declaration *> *it = ast->symbols; it; it = it->next) {
        Declaration *decl = it->value;

        if (Function *fun = decl->type()->asFunctionType()) {
            if ((fun->isSignal() || fun->isSlot()) && ! check) {
                translationUnit()->warning(ast->firstToken(),
                                           "you forgot the Q_OBJECT macro");
            }
        }
    }
    return true;
}

bool CheckUndefinedSymbols::visit(BaseSpecifierAST *base)
{
    if (NameAST *nameAST = base->name) {
        bool resolvedBaseClassName = false;

        if (Name *name = nameAST->name) {
            Identifier *id = name->identifier();
            const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
            if (isType(spell))
                resolvedBaseClassName = true;
        }

        if (! resolvedBaseClassName) {
            const char *token = "after `:'";

            if (base->comma_token)
                token = "after `,'";

            translationUnit()->warning(nameAST->firstToken(),
                                       "expected class-name %s token", token);
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(UsingDirectiveAST *ast)
{
    if (ast->symbol && ast->symbol->name() && _globalNamespaceBinding) {
        const Location loc = Location(ast->symbol);

        NamespaceBinding *binding = _globalNamespaceBinding.data();

        if (Scope *enclosingNamespaceScope = ast->symbol->enclosingNamespaceScope())
            binding = NamespaceBinding::find(enclosingNamespaceScope->owner()->asNamespace(), binding);

        if (! binding || ! binding->resolveNamespace(loc, ast->symbol->name())) {
            translationUnit()->warning(ast->name->firstToken(),
                                       "expected a namespace");
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(QualifiedNameAST *ast)
{
    if (ast->name) {
        QualifiedNameId *q = ast->name->asQualifiedNameId();
        for (unsigned i = 0; i < q->nameCount() - 1; ++i) {
            Name *name = q->nameAt(i);
            if (Identifier *id = name->identifier()) {
                const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
                if (! (_namespaceNames.contains(spell) || isType(id))) {
                    translationUnit()->warning(ast->firstToken(),
                                               "`%s' is not a namespace or class name",
                                               spell.constData());
                }
            }
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(CastExpressionAST *ast)
{
    if (ast->lparen_token && ast->type_id && ast->rparen_token && ast->expression) {
        if (TypeIdAST *cast_type_id = ast->type_id->asTypeId()) {
            SpecifierAST *type_specifier = cast_type_id->type_specifier;
            if (! cast_type_id->declarator && type_specifier && ! type_specifier->next &&
                type_specifier->asNamedTypeSpecifier() && ast->expression &&
                ast->expression->asUnaryExpression()) {
                // this ast node is ambigious, e.g.
                //   (a) + b
                // it can be parsed as
                //   ((a) + b)
                // or
                //   (a) (+b)
                accept(ast->expression);
                return false;
            }
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(SizeofExpressionAST *ast)
{
    if (ast->lparen_token && ast->expression && ast->rparen_token) {
        if (TypeIdAST *type_id = ast->expression->asTypeId()) {
            SpecifierAST *type_specifier = type_id->type_specifier;
            if (! type_id->declarator && type_specifier && ! type_specifier->next &&
                type_specifier->asNamedTypeSpecifier()) {
                // this sizeof expression is ambiguos, e.g.
                // sizeof (a)
                //   `a' can be a typeid or a nested-expression.
                return false;
            } else if (type_id->declarator
                       &&   type_id->declarator->postfix_declarators
                       && ! type_id->declarator->postfix_declarators->next
                       &&   type_id->declarator->postfix_declarators->asArrayDeclarator() != 0) {
                // this sizeof expression is ambiguos, e.g.
                // sizeof(a[10])
                //   `a' can be a typeid or an expression.
                return false;
            }
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(ObjCClassDeclarationAST *ast)
{
    if (NameAST *nameAST = ast->superclass) {
        bool resolvedSuperClassName = false;

        if (Name *name = nameAST->name) {
            Identifier *id = name->identifier();
            const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
            if (isType(spell))
                resolvedSuperClassName = true;
        }

        if (! resolvedSuperClassName) {
            translationUnit()->warning(nameAST->firstToken(),
                                       "expected class-name after ':' token");
        }
    }

    return true;
}

bool CheckUndefinedSymbols::visit(ObjCProtocolRefsAST *ast)
{
    for (IdentifierListAST *iter = ast->identifier_list; iter; iter = iter->next) {
        if (NameAST *nameAST = iter->name) {
            bool resolvedProtocolName = false;

            if (Name *name = nameAST->name) {
                Identifier *id = name->identifier();
                const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
                if (isProtocol(spell))
                    resolvedProtocolName = true;
            }

            if (!resolvedProtocolName) {
                char after;

                if (iter == ast->identifier_list)
                    after = '<';
                else
                    after = ',';

                translationUnit()->warning(nameAST->firstToken(), "expected protocol name after '%c' token", after);
            }
        }
    }

    return false;
}
<|endoftext|>


<commit_before>#include "opencv_video_source.h"
#include <string>
#include <iostream>

VideoSourceOpenCV::VideoSourceOpenCV(const char * path)
    :IVideoSource()
    , _frame_rate(0)
    , _daemon(nullptr)
{
    _cap.open(path);
    if (!_cap.isOpened()) {
        std::string error = std::string("Could not open the video source: ")
                            + path;
        throw std::runtime_error(error);
    }
    _daemon = new gg::BroadcastDaemon(this);
}

VideoSourceOpenCV::VideoSourceOpenCV(int deviceId)
    : IVideoSource()
    , _frame_rate(0)
{
    _cap.open(deviceId);
    if (!_cap.isOpened()) {
        std::string error("Device with id ");
        error.append(std::to_string(deviceId));
        error.append(" could not be opened");
        throw gg::DeviceNotFound(error);
    }
    _daemon = new gg::BroadcastDaemon(this);
}

VideoSourceOpenCV::~VideoSourceOpenCV()
{
    free(_daemon);
}

double VideoSourceOpenCV::get_frame_rate()
{
    if (!_cap.isOpened())
    {
        _frame_rate = 0;
        return _frame_rate;
    }

    if (_frame_rate > 0) // already obtained or estimated
        return _frame_rate;

    // will work for files, but <= 0 for most online devices
    _frame_rate = _cap.get(CV_CAP_PROP_FPS);

    if (_frame_rate <= 0) // i.e. not a file, see:
    // http://www.learnopencv.com/how-to-find-frame-rate-or-frames-per-second-fps-in-opencv-python-cpp/
    {
        // Number of frames to capture
        int num_frames = 120;

        // Start and end times
        time_t start, end;

        gg::VideoFrame buffer(gg::ColourSpace::BGRA, true);

        // Start time
        time(&start);

        // Grab a few frames
        for(int i = 0; i < num_frames; i++)
        {
            get_frame(buffer);
        }

        // End Time
        time(&end);

        // Time elapsed
        float seconds = difftime(end, start);

        // Calculate frames per second
        _frame_rate  = num_frames / seconds;
    }

    // to make sure e.g. -1 is not returned,
    // which could lead to bugs in computations
    if (_frame_rate < 0) _frame_rate = 0;

    return _frame_rate;
}

bool VideoSourceOpenCV::get_frame_dimensions(int &width, int & height)
{
    width = height = 0;
    if (!_cap.isOpened()) return false;

    width = (int)_cap.get(CV_CAP_PROP_FRAME_WIDTH);
    height = (int)_cap.get(CV_CAP_PROP_FRAME_HEIGHT);

    return (width != 0 && height != 0);
}

bool VideoSourceOpenCV::get_frame(gg::VideoFrame & frame)
{
    if (frame.colour() != gg::ColourSpace::BGRA)
        return false;

    if (!_cap.isOpened()) return false;

    bool has_read = _cap.read(_buffer);

    if (has_read) {
        // Initialize the BGRA buffer, only if not already done so.
        if (_buffer_bgra.empty() ||
            _buffer_bgra.rows != _buffer.rows ||
            _buffer_bgra.cols != _buffer.cols) {
            _buffer_bgra = cv::Mat::zeros(_buffer.rows, _buffer.cols, CV_8UC4);
        }
        // Convert to BGRA
        cv::cvtColor(_buffer, _buffer_bgra, CV_BGR2BGRA);
        unsigned char * data = nullptr;
        size_t data_length = 0;
        size_t cols = 0, rows = 0;
        if (_get_sub_frame) {
            _buffer_bgra(_sub_frame).copyTo(_buffer_sub_bgra);
            data = _buffer_sub_bgra.data;
            cols = _buffer_sub_bgra.cols;
            rows = _buffer_sub_bgra.rows;
            data_length = _buffer_sub_bgra.channels() * cols * rows;
        }

        else {
            data = _buffer_bgra.data;
            cols = _buffer_bgra.cols;
            rows = _buffer_bgra.rows;
            data_length = _buffer_bgra.channels() * cols * rows;
        }

        frame.init_from_specs(data, data_length, cols, rows);
    }
    return has_read;
}

void VideoSourceOpenCV::attach(gg::IObserver & observer)
{
    gg::IObservable::attach(observer);
    if (_observers.size() == 1)
        _daemon->start(get_frame_rate());
}

void VideoSourceOpenCV::detach(gg::IObserver & observer)
{
    gg::IObservable::detach(observer);
    if (_observers.empty())
        _daemon->stop();
}
<commit_msg>Issue #86: fixed detach of OpenCV video source: follows the same logic as its attach now<commit_after>#include "opencv_video_source.h"
#include <string>
#include <iostream>

VideoSourceOpenCV::VideoSourceOpenCV(const char * path)
    :IVideoSource()
    , _frame_rate(0)
    , _daemon(nullptr)
{
    _cap.open(path);
    if (!_cap.isOpened()) {
        std::string error = std::string("Could not open the video source: ")
                            + path;
        throw std::runtime_error(error);
    }
    _daemon = new gg::BroadcastDaemon(this);
}

VideoSourceOpenCV::VideoSourceOpenCV(int deviceId)
    : IVideoSource()
    , _frame_rate(0)
{
    _cap.open(deviceId);
    if (!_cap.isOpened()) {
        std::string error("Device with id ");
        error.append(std::to_string(deviceId));
        error.append(" could not be opened");
        throw gg::DeviceNotFound(error);
    }
    _daemon = new gg::BroadcastDaemon(this);
}

VideoSourceOpenCV::~VideoSourceOpenCV()
{
    free(_daemon);
}

double VideoSourceOpenCV::get_frame_rate()
{
    if (!_cap.isOpened())
    {
        _frame_rate = 0;
        return _frame_rate;
    }

    if (_frame_rate > 0) // already obtained or estimated
        return _frame_rate;

    // will work for files, but <= 0 for most online devices
    _frame_rate = _cap.get(CV_CAP_PROP_FPS);

    if (_frame_rate <= 0) // i.e. not a file, see:
    // http://www.learnopencv.com/how-to-find-frame-rate-or-frames-per-second-fps-in-opencv-python-cpp/
    {
        // Number of frames to capture
        int num_frames = 120;

        // Start and end times
        time_t start, end;

        gg::VideoFrame buffer(gg::ColourSpace::BGRA, true);

        // Start time
        time(&start);

        // Grab a few frames
        for(int i = 0; i < num_frames; i++)
        {
            get_frame(buffer);
        }

        // End Time
        time(&end);

        // Time elapsed
        float seconds = difftime(end, start);

        // Calculate frames per second
        _frame_rate  = num_frames / seconds;
    }

    // to make sure e.g. -1 is not returned,
    // which could lead to bugs in computations
    if (_frame_rate < 0) _frame_rate = 0;

    return _frame_rate;
}

bool VideoSourceOpenCV::get_frame_dimensions(int &width, int & height)
{
    width = height = 0;
    if (!_cap.isOpened()) return false;

    width = (int)_cap.get(CV_CAP_PROP_FRAME_WIDTH);
    height = (int)_cap.get(CV_CAP_PROP_FRAME_HEIGHT);

    return (width != 0 && height != 0);
}

bool VideoSourceOpenCV::get_frame(gg::VideoFrame & frame)
{
    if (frame.colour() != gg::ColourSpace::BGRA)
        return false;

    if (!_cap.isOpened()) return false;

    bool has_read = _cap.read(_buffer);

    if (has_read) {
        // Initialize the BGRA buffer, only if not already done so.
        if (_buffer_bgra.empty() ||
            _buffer_bgra.rows != _buffer.rows ||
            _buffer_bgra.cols != _buffer.cols) {
            _buffer_bgra = cv::Mat::zeros(_buffer.rows, _buffer.cols, CV_8UC4);
        }
        // Convert to BGRA
        cv::cvtColor(_buffer, _buffer_bgra, CV_BGR2BGRA);
        unsigned char * data = nullptr;
        size_t data_length = 0;
        size_t cols = 0, rows = 0;
        if (_get_sub_frame) {
            _buffer_bgra(_sub_frame).copyTo(_buffer_sub_bgra);
            data = _buffer_sub_bgra.data;
            cols = _buffer_sub_bgra.cols;
            rows = _buffer_sub_bgra.rows;
            data_length = _buffer_sub_bgra.channels() * cols * rows;
        }

        else {
            data = _buffer_bgra.data;
            cols = _buffer_bgra.cols;
            rows = _buffer_bgra.rows;
            data_length = _buffer_bgra.channels() * cols * rows;
        }

        frame.init_from_specs(data, data_length, cols, rows);
    }
    return has_read;
}

void VideoSourceOpenCV::attach(gg::IObserver & observer)
{
    gg::IObservable::attach(observer);
    if (_observers.size() == 1)
        _daemon->start(get_frame_rate());
}

void VideoSourceOpenCV::detach(gg::IObserver & observer)
{
    if (_observers.size() == 1)
        _daemon->stop();
    gg::IObservable::detach(observer);
}
<|endoftext|>