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

#include "chrome/browser/bookmark_bar_context_menu_controller.h"
#include "chrome/browser/bookmark_bar_model.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/views/bookmark_bar_view.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/browser/page_navigator.h"
#include "chrome/test/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// PageNavigator implementation that records the URL.
class TestingPageNavigator : public PageNavigator {
 public:
  virtual void OpenURL(const GURL& url,
                       WindowOpenDisposition disposition,
                       PageTransition::Type transition) {
    urls_.push_back(url);
  }

  std::vector<GURL> urls_;
};

}  // namespace

class BookmarkBarContextMenuControllerTest : public testing::Test {
 public:
  BookmarkBarContextMenuControllerTest() : bb_view_(NULL), model_(NULL) {
  }

  virtual void SetUp() {
    BookmarkBarView::testing_ = true;

    profile_.reset(new TestingProfile());
    profile_->set_has_history_service(true);
    profile_->CreateBookmarkBarModel();

    model_ = profile_->GetBookmarkBarModel();

    bb_view_ = new BookmarkBarView(profile_.get(), NULL);
    bb_view_->SetPageNavigator(&navigator_);

    AddTestData();
  }

  virtual void TearDown() {
    BookmarkBarView::testing_ = false;
  }

 protected:
  BookmarkBarModel* model_;
  BookmarkBarView* bb_view_;
  TestingPageNavigator navigator_;

 private:
  // Creates the following structure:
  // a
  // F1
  //  f1a
  //  F11
  //   f11a
  // F2
  void AddTestData() {
    std::string test_base = "file:///c:/tmp/";

    model_->AddURL(model_->GetBookmarkBarNode(), 0, L"a",
                   GURL(test_base + "a"));
    BookmarkBarNode* f1 =
        model_->AddGroup(model_->GetBookmarkBarNode(), 1, L"F1");
    model_->AddURL(f1, 0, L"f1a", GURL(test_base + "f1a"));
    BookmarkBarNode* f11 = model_->AddGroup(f1, 1, L"F11");
    model_->AddURL(f11, 0, L"f11a", GURL(test_base + "f11a"));
    model_->AddGroup(model_->GetBookmarkBarNode(), 2, L"F2");
  }

  scoped_ptr<TestingProfile> profile_;
};

// Tests Deleting from the menu.
TEST_F(BookmarkBarContextMenuControllerTest, DeleteURL) {
  BookmarkBarContextMenuController controller(
      bb_view_, model_->GetBookmarkBarNode()->GetChild(0));
  GURL url = model_->GetBookmarkBarNode()->GetChild(0)->GetURL();
  ASSERT_TRUE(controller.IsCommandEnabled(
                  BookmarkBarContextMenuController::delete_bookmark_id));
  // Delete the URL.
  controller.ExecuteCommand(
      BookmarkBarContextMenuController::delete_bookmark_id);
  // Model shouldn't have URL anymore.
  ASSERT_TRUE(model_->GetNodeByURL(url) == NULL);
}

// Tests openning from the menu.
TEST_F(BookmarkBarContextMenuControllerTest, OpenURL) {
  BookmarkBarContextMenuController controller(
      bb_view_, model_->GetBookmarkBarNode()->GetChild(0));
  GURL url = model_->GetBookmarkBarNode()->GetChild(0)->GetURL();
  ASSERT_TRUE(controller.IsCommandEnabled(
                  BookmarkBarContextMenuController::open_bookmark_id));
  // Open it.
  controller.ExecuteCommand(
      BookmarkBarContextMenuController::open_bookmark_id);
  // Should have navigated to it.
  ASSERT_EQ(1, navigator_.urls_.size());
  ASSERT_TRUE(url == navigator_.urls_[0]);
}

// Tests open all on a folder with a couple of bookmarks.
TEST_F(BookmarkBarContextMenuControllerTest, OpenAll) {
  BookmarkBarNode* folder = model_->GetBookmarkBarNode()->GetChild(1);
  BookmarkBarContextMenuController controller(bb_view_, folder);
  ASSERT_TRUE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_id));
  ASSERT_TRUE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_in_new_window_id));
  // Open it.
  controller.ExecuteCommand(
      BookmarkBarContextMenuController::open_all_bookmarks_id);
  // Should have navigated to F1's children.
  ASSERT_EQ(2, navigator_.urls_.size());
  ASSERT_TRUE(folder->GetChild(0)->GetURL() == navigator_.urls_[0]);
  ASSERT_TRUE(folder->GetChild(1)->GetChild(0)->GetURL() ==
              navigator_.urls_[1]);
}

// Tests that menus are appropriately disabled for empty folders.
TEST_F(BookmarkBarContextMenuControllerTest, DisableForEmptyFolder) {
  BookmarkBarNode* folder = model_->GetBookmarkBarNode()->GetChild(2);
  BookmarkBarContextMenuController controller(bb_view_, folder);
  EXPECT_FALSE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_id));
  EXPECT_FALSE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_in_new_window_id));
}
<commit_msg>Fixes leak in BookmarkBarContextMenu test.<commit_after>// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "chrome/browser/bookmark_bar_context_menu_controller.h"
#include "chrome/browser/bookmark_bar_model.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/views/bookmark_bar_view.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/browser/page_navigator.h"
#include "chrome/test/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// PageNavigator implementation that records the URL.
class TestingPageNavigator : public PageNavigator {
 public:
  virtual void OpenURL(const GURL& url,
                       WindowOpenDisposition disposition,
                       PageTransition::Type transition) {
    urls_.push_back(url);
  }

  std::vector<GURL> urls_;
};

}  // namespace

class BookmarkBarContextMenuControllerTest : public testing::Test {
 public:
  BookmarkBarContextMenuControllerTest() : bb_view_(NULL), model_(NULL) {
  }

  virtual void SetUp() {
    BookmarkBarView::testing_ = true;

    profile_.reset(new TestingProfile());
    profile_->set_has_history_service(true);
    profile_->CreateBookmarkBarModel();

    model_ = profile_->GetBookmarkBarModel();

    bb_view_.reset(new BookmarkBarView(profile_.get(), NULL));
    bb_view_->SetPageNavigator(&navigator_);

    AddTestData();
  }

  virtual void TearDown() {
    BookmarkBarView::testing_ = false;
  }

 protected:
  scoped_ptr<TestingProfile> profile_;
  BookmarkBarModel* model_;
  scoped_ptr<BookmarkBarView> bb_view_;
  TestingPageNavigator navigator_;

 private:
  // Creates the following structure:
  // a
  // F1
  //  f1a
  //  F11
  //   f11a
  // F2
  void AddTestData() {
    std::string test_base = "file:///c:/tmp/";

    model_->AddURL(model_->GetBookmarkBarNode(), 0, L"a",
                   GURL(test_base + "a"));
    BookmarkBarNode* f1 =
        model_->AddGroup(model_->GetBookmarkBarNode(), 1, L"F1");
    model_->AddURL(f1, 0, L"f1a", GURL(test_base + "f1a"));
    BookmarkBarNode* f11 = model_->AddGroup(f1, 1, L"F11");
    model_->AddURL(f11, 0, L"f11a", GURL(test_base + "f11a"));
    model_->AddGroup(model_->GetBookmarkBarNode(), 2, L"F2");
  }
};

// Tests Deleting from the menu.
TEST_F(BookmarkBarContextMenuControllerTest, DeleteURL) {
  BookmarkBarContextMenuController controller(
      bb_view_.get(), model_->GetBookmarkBarNode()->GetChild(0));
  GURL url = model_->GetBookmarkBarNode()->GetChild(0)->GetURL();
  ASSERT_TRUE(controller.IsCommandEnabled(
                  BookmarkBarContextMenuController::delete_bookmark_id));
  // Delete the URL.
  controller.ExecuteCommand(
      BookmarkBarContextMenuController::delete_bookmark_id);
  // Model shouldn't have URL anymore.
  ASSERT_TRUE(model_->GetNodeByURL(url) == NULL);
}

// Tests openning from the menu.
TEST_F(BookmarkBarContextMenuControllerTest, OpenURL) {
  BookmarkBarContextMenuController controller(
      bb_view_.get(), model_->GetBookmarkBarNode()->GetChild(0));
  GURL url = model_->GetBookmarkBarNode()->GetChild(0)->GetURL();
  ASSERT_TRUE(controller.IsCommandEnabled(
                  BookmarkBarContextMenuController::open_bookmark_id));
  // Open it.
  controller.ExecuteCommand(
      BookmarkBarContextMenuController::open_bookmark_id);
  // Should have navigated to it.
  ASSERT_EQ(1, navigator_.urls_.size());
  ASSERT_TRUE(url == navigator_.urls_[0]);
}

// Tests open all on a folder with a couple of bookmarks.
TEST_F(BookmarkBarContextMenuControllerTest, OpenAll) {
  BookmarkBarNode* folder = model_->GetBookmarkBarNode()->GetChild(1);
  BookmarkBarContextMenuController controller(bb_view_.get(), folder);
  ASSERT_TRUE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_id));
  ASSERT_TRUE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_in_new_window_id));
  // Open it.
  controller.ExecuteCommand(
      BookmarkBarContextMenuController::open_all_bookmarks_id);
  // Should have navigated to F1's children.
  ASSERT_EQ(2, navigator_.urls_.size());
  ASSERT_TRUE(folder->GetChild(0)->GetURL() == navigator_.urls_[0]);
  ASSERT_TRUE(folder->GetChild(1)->GetChild(0)->GetURL() ==
              navigator_.urls_[1]);
}

// Tests that menus are appropriately disabled for empty folders.
TEST_F(BookmarkBarContextMenuControllerTest, DisableForEmptyFolder) {
  BookmarkBarNode* folder = model_->GetBookmarkBarNode()->GetChild(2);
  BookmarkBarContextMenuController controller(bb_view_.get(), folder);
  EXPECT_FALSE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_id));
  EXPECT_FALSE(controller.IsCommandEnabled(
      BookmarkBarContextMenuController::open_all_bookmarks_in_new_window_id));
}
<|endoftext|>


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

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

#include "base/json_writer.h"
#include "base/values.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/extensions/extension_event_names.h"
#include "chrome/browser/extensions/extension_message_service.h"
#include "chrome/browser/extensions/extension_tabs_module_constants.h"
#include "chrome/browser/extensions/extension_page_actions_module_constants.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"

namespace events = extension_event_names;
namespace tab_keys = extension_tabs_module_constants;
namespace page_action_keys = extension_page_actions_module_constants;

ExtensionBrowserEventRouter::TabEntry::TabEntry()
    : state_(ExtensionTabUtil::TAB_COMPLETE),
      pending_navigate_(false),
      url_() {
}

ExtensionBrowserEventRouter::TabEntry::TabEntry(const TabContents* contents)
    : state_(ExtensionTabUtil::TAB_COMPLETE),
      pending_navigate_(false),
      url_(contents->GetURL()) {
  UpdateLoadState(contents);
}

DictionaryValue* ExtensionBrowserEventRouter::TabEntry::UpdateLoadState(
    const TabContents* contents) {
  ExtensionTabUtil::TabStatus old_state = state_;
  state_ = ExtensionTabUtil::GetTabStatus(contents);

  if (old_state == state_)
    return false;

  if (state_ == ExtensionTabUtil::TAB_LOADING) {
    // Do not send "loading" state changed now. Wait for navigate so the new
    // url is available.
    pending_navigate_ = true;
    return NULL;

  } else if (state_ == ExtensionTabUtil::TAB_COMPLETE) {
    // Send "complete" state change.
    DictionaryValue* changed_properties = new DictionaryValue();
    changed_properties->SetString(tab_keys::kStatusKey,
        tab_keys::kStatusValueComplete);
    return changed_properties;

  } else {
    NOTREACHED();
    return NULL;
  }
}

DictionaryValue* ExtensionBrowserEventRouter::TabEntry::DidNavigate(
    const TabContents* contents) {
  if (!pending_navigate_)
    return NULL;

  DictionaryValue* changed_properties = new DictionaryValue();
  changed_properties->SetString(tab_keys::kStatusKey,
      tab_keys::kStatusValueLoading);

  GURL new_url = contents->GetURL();
  if (new_url != url_) {
    url_ = new_url;
    changed_properties->SetString(tab_keys::kUrlKey, url_.spec());
  }

  pending_navigate_ = false;
  return changed_properties;
}

ExtensionBrowserEventRouter* ExtensionBrowserEventRouter::GetInstance() {
  return Singleton<ExtensionBrowserEventRouter>::get();
}

static void DispatchEvent(Profile* profile,
                          const char* event_name,
                          const std::string json_args) {
  if (profile->GetExtensionMessageService()) {
    profile->GetExtensionMessageService()->
        DispatchEventToRenderers(event_name, json_args);
  }
}

static void DispatchSimpleBrowserEvent(Profile* profile,
                                       const int window_id,
                                       const char* event_name) {
  ListValue args;
  args.Append(Value::CreateIntegerValue(window_id));

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(profile, event_name, json_args);
}

void ExtensionBrowserEventRouter::Init() {
  if (initialized_)
    return;

  BrowserList::AddObserver(this);

  initialized_ = true;
}

ExtensionBrowserEventRouter::ExtensionBrowserEventRouter()
    : initialized_(false) { }

void ExtensionBrowserEventRouter::OnBrowserAdded(const Browser* browser) {
  // Start listening to TabStripModel events for this browser.
  browser->tabstrip_model()->AddObserver(this);

  // The window isn't ready at this point, so we defer until it is.
  registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
      Source<const Browser>(browser));
}

void ExtensionBrowserEventRouter::OnBrowserWindowReady(const Browser* browser) {
  ListValue args;

  registrar_.Remove(this, NotificationType::BROWSER_WINDOW_READY,
      Source<const Browser>(browser));

  DictionaryValue* window_dictionary = ExtensionTabUtil::CreateWindowValue(
      browser, false);
  args.Append(window_dictionary);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(browser->profile(), events::kOnWindowCreated, json_args);
}

void ExtensionBrowserEventRouter::OnBrowserRemoving(const Browser* browser) {
  // Stop listening to TabStripModel events for this browser.
  browser->tabstrip_model()->RemoveObserver(this);

  DispatchSimpleBrowserEvent(browser->profile(),
                             ExtensionTabUtil::GetWindowId(browser),
                             events::kOnWindowRemoved);
}

void ExtensionBrowserEventRouter::OnBrowserSetLastActive(
    const Browser* browser) {
  DispatchSimpleBrowserEvent(browser->profile(),
                             ExtensionTabUtil::GetWindowId(browser),
                             events::kOnWindowFocusedChanged);
}

void ExtensionBrowserEventRouter::TabCreatedAt(TabContents* contents,
                                                int index,
                                                bool foreground) {
  ListValue args;
  args.Append(ExtensionTabUtil::CreateTabValue(contents));
  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabCreated, json_args);

  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(&contents->controller()));

  // Observing TAB_CONTENTS_DESTROYED is necessary because it's
  // possible for tabs to be created, detached and then destroyed without
  // ever having been re-attached and closed. This happens in the case of
  // a devtools TabContents that is opened in window, docked, then closed.
  registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
                 Source<TabContents>(contents));
}

void ExtensionBrowserEventRouter::TabInsertedAt(TabContents* contents,
                                                int index,
                                                bool foreground) {
  // If tab is new, send created event.
  int tab_id = ExtensionTabUtil::GetTabId(contents);
  if (tab_entries_.find(tab_id) == tab_entries_.end()) {
    tab_entries_[tab_id] = TabEntry(contents);

    TabCreatedAt(contents, index, foreground);
    return;
  }

  ListValue args;
  args.Append(Value::CreateIntegerValue(tab_id));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kNewWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(contents)));
  object_args->Set(tab_keys::kNewPositionKey, Value::CreateIntegerValue(
      index));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabAttached, json_args);
}

void ExtensionBrowserEventRouter::TabDetachedAt(TabContents* contents,
                                                int index) {
  int tab_id = ExtensionTabUtil::GetTabId(contents);
  if (tab_entries_.find(tab_id) == tab_entries_.end()) {
    // The tab was removed. Don't send detach event.
    return;
  }

  ListValue args;
  args.Append(Value::CreateIntegerValue(tab_id));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kOldWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(contents)));
  object_args->Set(tab_keys::kOldPositionKey, Value::CreateIntegerValue(
      index));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabDetached, json_args);
}

void ExtensionBrowserEventRouter::TabClosingAt(TabContents* contents,
                                               int index) {
  int tab_id = ExtensionTabUtil::GetTabId(contents);

  ListValue args;
  args.Append(Value::CreateIntegerValue(tab_id));

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabRemoved, json_args);

  int removed_count = tab_entries_.erase(tab_id);
  DCHECK_GT(removed_count, 0);

  registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
      Source<NavigationController>(&contents->controller()));
  registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
      Source<TabContents>(contents));
}

void ExtensionBrowserEventRouter::TabSelectedAt(TabContents* old_contents,
                                                TabContents* new_contents,
                                                int index,
                                                bool user_gesture) {
  ListValue args;
  args.Append(Value::CreateIntegerValue(
      ExtensionTabUtil::GetTabId(new_contents)));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(new_contents)));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(new_contents->profile(), events::kOnTabSelectionChanged,
                json_args);
}

void ExtensionBrowserEventRouter::TabMoved(TabContents* contents,
                                           int from_index,
                                           int to_index,
                                           bool pinned_state_changed) {
  ListValue args;
  args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents)));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(contents)));
  object_args->Set(tab_keys::kFromIndexKey, Value::CreateIntegerValue(
      from_index));
  object_args->Set(tab_keys::kToIndexKey, Value::CreateIntegerValue(
      to_index));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabMoved, json_args);
}

void ExtensionBrowserEventRouter::TabUpdated(TabContents* contents,
                                             bool did_navigate) {
  int tab_id = ExtensionTabUtil::GetTabId(contents);
  std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id);
  DCHECK(tab_entries_.end() != i);
  TabEntry& entry = i->second;

  DictionaryValue* changed_properties = NULL;
  if (did_navigate)
    changed_properties = entry.DidNavigate(contents);
  else
    changed_properties = entry.UpdateLoadState(contents);

  if (changed_properties) {
    // The state of the tab (as seen from the extension point of view) has
    // changed.  Send a notification to the extension.
    ListValue args;
    args.Append(Value::CreateIntegerValue(tab_id));
    args.Append(changed_properties);

    std::string json_args;
    JSONWriter::Write(&args, false, &json_args);

    DispatchEvent(contents->profile(), events::kOnTabUpdated, json_args);
  }
}

void ExtensionBrowserEventRouter::Observe(NotificationType type,
                                          const NotificationSource& source,
                                          const NotificationDetails& details) {
  if (type == NotificationType::NAV_ENTRY_COMMITTED) {
    NavigationController* source_controller =
        Source<NavigationController>(source).ptr();
    TabUpdated(source_controller->tab_contents(), true);
  } else if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
    // Tab was destroyed after being detached (without being re-attached).
    TabContents* contents = Source<TabContents>(source).ptr();
    registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
        Source<NavigationController>(&contents->controller()));
    registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
        Source<TabContents>(contents));
  } else if (type == NotificationType::BROWSER_WINDOW_READY) {
    const Browser* browser = Source<const Browser>(source).ptr();
    OnBrowserWindowReady(browser);
  } else {
    NOTREACHED();
  }
}

void ExtensionBrowserEventRouter::TabChangedAt(TabContents* contents,
                                               int index,
                                               bool loading_only) {
  TabUpdated(contents, false);
}

void ExtensionBrowserEventRouter::TabStripEmpty() { }

void ExtensionBrowserEventRouter::PageActionExecuted(
    Profile* profile,
    const std::string& extension_id,
    const std::string& page_action_id,
    int tab_id,
    const std::string& url,
    int button) {
  ListValue args;
  args.Append(Value::CreateStringValue(page_action_id));

  DictionaryValue* data = new DictionaryValue();
  data->Set(tab_keys::kTabIdKey, Value::CreateIntegerValue(tab_id));
  data->Set(tab_keys::kTabUrlKey, Value::CreateStringValue(url));
  data->Set(page_action_keys::kButtonKey, Value::CreateIntegerValue(button));
  args.Append(data);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  std::string event_name = std::string("pageAction/") + extension_id;
  DispatchEvent(profile, event_name.c_str(), json_args);
}

void ExtensionBrowserEventRouter::BrowserActionExecuted(
    Profile* profile, const std::string& extension_id, Browser* browser) {
  TabContents* tab_contents = NULL;
  int tab_id = 0;
  if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id))
    return;

  ListValue args;
  args.Append(ExtensionTabUtil::CreateTabValue(tab_contents));
  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  std::string event_name = std::string("browserAction/") + extension_id;
  DispatchEvent(profile, event_name.c_str(), json_args);
}
<commit_msg>Extensions: guarantee removal of BROWSER_WINDOW_READY registration.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

#include "base/json_writer.h"
#include "base/values.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/extensions/extension_event_names.h"
#include "chrome/browser/extensions/extension_message_service.h"
#include "chrome/browser/extensions/extension_tabs_module_constants.h"
#include "chrome/browser/extensions/extension_page_actions_module_constants.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"

namespace events = extension_event_names;
namespace tab_keys = extension_tabs_module_constants;
namespace page_action_keys = extension_page_actions_module_constants;

ExtensionBrowserEventRouter::TabEntry::TabEntry()
    : state_(ExtensionTabUtil::TAB_COMPLETE),
      pending_navigate_(false),
      url_() {
}

ExtensionBrowserEventRouter::TabEntry::TabEntry(const TabContents* contents)
    : state_(ExtensionTabUtil::TAB_COMPLETE),
      pending_navigate_(false),
      url_(contents->GetURL()) {
  UpdateLoadState(contents);
}

DictionaryValue* ExtensionBrowserEventRouter::TabEntry::UpdateLoadState(
    const TabContents* contents) {
  ExtensionTabUtil::TabStatus old_state = state_;
  state_ = ExtensionTabUtil::GetTabStatus(contents);

  if (old_state == state_)
    return false;

  if (state_ == ExtensionTabUtil::TAB_LOADING) {
    // Do not send "loading" state changed now. Wait for navigate so the new
    // url is available.
    pending_navigate_ = true;
    return NULL;

  } else if (state_ == ExtensionTabUtil::TAB_COMPLETE) {
    // Send "complete" state change.
    DictionaryValue* changed_properties = new DictionaryValue();
    changed_properties->SetString(tab_keys::kStatusKey,
        tab_keys::kStatusValueComplete);
    return changed_properties;

  } else {
    NOTREACHED();
    return NULL;
  }
}

DictionaryValue* ExtensionBrowserEventRouter::TabEntry::DidNavigate(
    const TabContents* contents) {
  if (!pending_navigate_)
    return NULL;

  DictionaryValue* changed_properties = new DictionaryValue();
  changed_properties->SetString(tab_keys::kStatusKey,
      tab_keys::kStatusValueLoading);

  GURL new_url = contents->GetURL();
  if (new_url != url_) {
    url_ = new_url;
    changed_properties->SetString(tab_keys::kUrlKey, url_.spec());
  }

  pending_navigate_ = false;
  return changed_properties;
}

ExtensionBrowserEventRouter* ExtensionBrowserEventRouter::GetInstance() {
  return Singleton<ExtensionBrowserEventRouter>::get();
}

static void DispatchEvent(Profile* profile,
                          const char* event_name,
                          const std::string json_args) {
  if (profile->GetExtensionMessageService()) {
    profile->GetExtensionMessageService()->
        DispatchEventToRenderers(event_name, json_args);
  }
}

static void DispatchSimpleBrowserEvent(Profile* profile,
                                       const int window_id,
                                       const char* event_name) {
  ListValue args;
  args.Append(Value::CreateIntegerValue(window_id));

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(profile, event_name, json_args);
}

void ExtensionBrowserEventRouter::Init() {
  if (initialized_)
    return;

  BrowserList::AddObserver(this);

  initialized_ = true;
}

ExtensionBrowserEventRouter::ExtensionBrowserEventRouter()
    : initialized_(false) { }

void ExtensionBrowserEventRouter::OnBrowserAdded(const Browser* browser) {
  // Start listening to TabStripModel events for this browser.
  browser->tabstrip_model()->AddObserver(this);

  // The window isn't ready at this point, so we defer until it is.
  registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
      Source<const Browser>(browser));
}

void ExtensionBrowserEventRouter::OnBrowserWindowReady(const Browser* browser) {
  ListValue args;

  DictionaryValue* window_dictionary = ExtensionTabUtil::CreateWindowValue(
      browser, false);
  args.Append(window_dictionary);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(browser->profile(), events::kOnWindowCreated, json_args);
}

void ExtensionBrowserEventRouter::OnBrowserRemoving(const Browser* browser) {
  // Stop listening to TabStripModel events for this browser.
  browser->tabstrip_model()->RemoveObserver(this);

  registrar_.Remove(this, NotificationType::BROWSER_WINDOW_READY,
      Source<const Browser>(browser));

  DispatchSimpleBrowserEvent(browser->profile(),
                             ExtensionTabUtil::GetWindowId(browser),
                             events::kOnWindowRemoved);
}

void ExtensionBrowserEventRouter::OnBrowserSetLastActive(
    const Browser* browser) {
  DispatchSimpleBrowserEvent(browser->profile(),
                             ExtensionTabUtil::GetWindowId(browser),
                             events::kOnWindowFocusedChanged);
}

void ExtensionBrowserEventRouter::TabCreatedAt(TabContents* contents,
                                                int index,
                                                bool foreground) {
  ListValue args;
  args.Append(ExtensionTabUtil::CreateTabValue(contents));
  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabCreated, json_args);

  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(&contents->controller()));

  // Observing TAB_CONTENTS_DESTROYED is necessary because it's
  // possible for tabs to be created, detached and then destroyed without
  // ever having been re-attached and closed. This happens in the case of
  // a devtools TabContents that is opened in window, docked, then closed.
  registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
                 Source<TabContents>(contents));
}

void ExtensionBrowserEventRouter::TabInsertedAt(TabContents* contents,
                                                int index,
                                                bool foreground) {
  // If tab is new, send created event.
  int tab_id = ExtensionTabUtil::GetTabId(contents);
  if (tab_entries_.find(tab_id) == tab_entries_.end()) {
    tab_entries_[tab_id] = TabEntry(contents);

    TabCreatedAt(contents, index, foreground);
    return;
  }

  ListValue args;
  args.Append(Value::CreateIntegerValue(tab_id));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kNewWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(contents)));
  object_args->Set(tab_keys::kNewPositionKey, Value::CreateIntegerValue(
      index));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabAttached, json_args);
}

void ExtensionBrowserEventRouter::TabDetachedAt(TabContents* contents,
                                                int index) {
  int tab_id = ExtensionTabUtil::GetTabId(contents);
  if (tab_entries_.find(tab_id) == tab_entries_.end()) {
    // The tab was removed. Don't send detach event.
    return;
  }

  ListValue args;
  args.Append(Value::CreateIntegerValue(tab_id));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kOldWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(contents)));
  object_args->Set(tab_keys::kOldPositionKey, Value::CreateIntegerValue(
      index));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabDetached, json_args);
}

void ExtensionBrowserEventRouter::TabClosingAt(TabContents* contents,
                                               int index) {
  int tab_id = ExtensionTabUtil::GetTabId(contents);

  ListValue args;
  args.Append(Value::CreateIntegerValue(tab_id));

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabRemoved, json_args);

  int removed_count = tab_entries_.erase(tab_id);
  DCHECK_GT(removed_count, 0);

  registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
      Source<NavigationController>(&contents->controller()));
  registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
      Source<TabContents>(contents));
}

void ExtensionBrowserEventRouter::TabSelectedAt(TabContents* old_contents,
                                                TabContents* new_contents,
                                                int index,
                                                bool user_gesture) {
  ListValue args;
  args.Append(Value::CreateIntegerValue(
      ExtensionTabUtil::GetTabId(new_contents)));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(new_contents)));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(new_contents->profile(), events::kOnTabSelectionChanged,
                json_args);
}

void ExtensionBrowserEventRouter::TabMoved(TabContents* contents,
                                           int from_index,
                                           int to_index,
                                           bool pinned_state_changed) {
  ListValue args;
  args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents)));

  DictionaryValue* object_args = new DictionaryValue();
  object_args->Set(tab_keys::kWindowIdKey, Value::CreateIntegerValue(
      ExtensionTabUtil::GetWindowIdOfTab(contents)));
  object_args->Set(tab_keys::kFromIndexKey, Value::CreateIntegerValue(
      from_index));
  object_args->Set(tab_keys::kToIndexKey, Value::CreateIntegerValue(
      to_index));
  args.Append(object_args);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  DispatchEvent(contents->profile(), events::kOnTabMoved, json_args);
}

void ExtensionBrowserEventRouter::TabUpdated(TabContents* contents,
                                             bool did_navigate) {
  int tab_id = ExtensionTabUtil::GetTabId(contents);
  std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id);
  DCHECK(tab_entries_.end() != i);
  TabEntry& entry = i->second;

  DictionaryValue* changed_properties = NULL;
  if (did_navigate)
    changed_properties = entry.DidNavigate(contents);
  else
    changed_properties = entry.UpdateLoadState(contents);

  if (changed_properties) {
    // The state of the tab (as seen from the extension point of view) has
    // changed.  Send a notification to the extension.
    ListValue args;
    args.Append(Value::CreateIntegerValue(tab_id));
    args.Append(changed_properties);

    std::string json_args;
    JSONWriter::Write(&args, false, &json_args);

    DispatchEvent(contents->profile(), events::kOnTabUpdated, json_args);
  }
}

void ExtensionBrowserEventRouter::Observe(NotificationType type,
                                          const NotificationSource& source,
                                          const NotificationDetails& details) {
  if (type == NotificationType::NAV_ENTRY_COMMITTED) {
    NavigationController* source_controller =
        Source<NavigationController>(source).ptr();
    TabUpdated(source_controller->tab_contents(), true);
  } else if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
    // Tab was destroyed after being detached (without being re-attached).
    TabContents* contents = Source<TabContents>(source).ptr();
    registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
        Source<NavigationController>(&contents->controller()));
    registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
        Source<TabContents>(contents));
  } else if (type == NotificationType::BROWSER_WINDOW_READY) {
    const Browser* browser = Source<const Browser>(source).ptr();
    OnBrowserWindowReady(browser);
  } else {
    NOTREACHED();
  }
}

void ExtensionBrowserEventRouter::TabChangedAt(TabContents* contents,
                                               int index,
                                               bool loading_only) {
  TabUpdated(contents, false);
}

void ExtensionBrowserEventRouter::TabStripEmpty() { }

void ExtensionBrowserEventRouter::PageActionExecuted(
    Profile* profile,
    const std::string& extension_id,
    const std::string& page_action_id,
    int tab_id,
    const std::string& url,
    int button) {
  ListValue args;
  args.Append(Value::CreateStringValue(page_action_id));

  DictionaryValue* data = new DictionaryValue();
  data->Set(tab_keys::kTabIdKey, Value::CreateIntegerValue(tab_id));
  data->Set(tab_keys::kTabUrlKey, Value::CreateStringValue(url));
  data->Set(page_action_keys::kButtonKey, Value::CreateIntegerValue(button));
  args.Append(data);

  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  std::string event_name = std::string("pageAction/") + extension_id;
  DispatchEvent(profile, event_name.c_str(), json_args);
}

void ExtensionBrowserEventRouter::BrowserActionExecuted(
    Profile* profile, const std::string& extension_id, Browser* browser) {
  TabContents* tab_contents = NULL;
  int tab_id = 0;
  if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id))
    return;

  ListValue args;
  args.Append(ExtensionTabUtil::CreateTabValue(tab_contents));
  std::string json_args;
  JSONWriter::Write(&args, false, &json_args);

  std::string event_name = std::string("browserAction/") + extension_id;
  DispatchEvent(profile, event_name.c_str(), json_args);
}
<|endoftext|>


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

#include <atlbase.h>
#include <atlapp.h>
#include <atlmisc.h>

#include "chrome/common/gfx/icon_util.h"
#include "base/gfx/size.h"
#include "base/scoped_ptr.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "skia/include/SkBitmap.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

  static const wchar_t* const kSmallIconName = L"icon_util\\16_X_16_icon.ico";
  static const wchar_t* const kLargeIconName = L"icon_util\\128_X_128_icon.ico";
  static const wchar_t* const kTempIconFilename = L"temp_test_icon.ico";

  class IconUtilTest : public testing::Test {
   public:
    IconUtilTest() {
      PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_);
    }
    ~IconUtilTest() {}

    static const int kSmallIconWidth = 16;
    static const int kSmallIconHeight = 16;
    static const int kLargeIconWidth = 128;
    static const int kLargeIconHeight = 128;

    // Given a file name for an .ico file and an image dimentions, this
    // function loads the icon and returns an HICON handle.
    HICON LoadIconFromFile(const std::wstring& filename,
                           int width,
                           int height) {
      HICON icon =
          static_cast<HICON>(LoadImage(NULL,
                                       filename.c_str(),
                                       IMAGE_ICON,
                                       width,
                                       height,
                                       LR_LOADTRANSPARENT | LR_LOADFROMFILE));
      return icon;
    }

   protected:
    // The root directory for test files.
    std::wstring test_data_directory_;

   private:
    DISALLOW_EVIL_CONSTRUCTORS(IconUtilTest);
  };
};

// The following test case makes sure IconUtil::SkBitmapFromHICON fails
// gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestIconToBitmapInvalidParameters) {
  std::wstring icon_filename(test_data_directory_);
  file_util::AppendToPath(&icon_filename, kSmallIconName);
  gfx::Size icon_size(kSmallIconWidth, kSmallIconHeight);
  HICON icon = LoadIconFromFile(icon_filename,
                                icon_size.width(),
                                icon_size.height());
  ASSERT_TRUE(icon != NULL);

  // Invalid size parameter.
  gfx::Size invalid_icon_size(kSmallIconHeight, 0);
  EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(icon, invalid_icon_size),
            static_cast<SkBitmap*>(NULL));

  // Invalid icon.
  EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(NULL, icon_size),
            static_cast<SkBitmap*>(NULL));

  // The following code should succeed.
  scoped_ptr<SkBitmap> bitmap;
  bitmap.reset(IconUtil::CreateSkBitmapFromHICON(icon, icon_size));
  EXPECT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  ::DestroyIcon(icon);
}

// The following test case makes sure IconUtil::CreateHICONFromSkBitmap fails
// gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) {
  HICON icon = NULL;
  scoped_ptr<SkBitmap> bitmap;

  // Wrong bitmap format.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_EQ(icon, static_cast<HICON>(NULL));

  // Invalid bitmap size.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_EQ(icon, static_cast<HICON>(NULL));

  // Valid bitmap configuration but no pixels allocated.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_TRUE(icon == NULL);
}

// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap
// fails gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) {
  scoped_ptr<SkBitmap> bitmap;
  std::wstring valid_icon_filename(test_data_directory_);
  file_util::AppendToPath(&valid_icon_filename, kSmallIconName);
  std::wstring invalid_icon_filename(L"C:\\<>?.ico");

  // Wrong bitmap format.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    valid_icon_filename));

  // Invalid bitmap size.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    valid_icon_filename));

  // Bitmap with no allocated pixels.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    valid_icon_filename));

  // Invalid file name.
  bitmap->allocPixels();
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    invalid_icon_filename));
}

// This test case makes sure that when we load an icon from disk and convert
// the HICON into a bitmap, the bitmap has the expected format and dimentions.
TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) {
  scoped_ptr<SkBitmap> bitmap;
  std::wstring small_icon_filename(test_data_directory_);
  file_util::AppendToPath(&small_icon_filename, kSmallIconName);
  gfx::Size small_icon_size(kSmallIconWidth, kSmallIconHeight);
  HICON small_icon = LoadIconFromFile(small_icon_filename,
                                      small_icon_size.width(),
                                      small_icon_size.height());
  ASSERT_NE(small_icon, static_cast<HICON>(NULL));
  bitmap.reset(IconUtil::CreateSkBitmapFromHICON(small_icon, small_icon_size));
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  EXPECT_EQ(bitmap->width(), small_icon_size.width());
  EXPECT_EQ(bitmap->height(), small_icon_size.height());
  EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
  ::DestroyIcon(small_icon);

  std::wstring large_icon_filename(test_data_directory_);
  file_util::AppendToPath(&large_icon_filename, kLargeIconName);
  gfx::Size large_icon_size(kLargeIconWidth, kLargeIconHeight);
  HICON large_icon = LoadIconFromFile(large_icon_filename,
                                      large_icon_size.width(),
                                      large_icon_size.height());
  ASSERT_NE(large_icon, static_cast<HICON>(NULL));
  bitmap.reset(IconUtil::CreateSkBitmapFromHICON(large_icon, large_icon_size));
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  EXPECT_EQ(bitmap->width(), large_icon_size.width());
  EXPECT_EQ(bitmap->height(), large_icon_size.height());
  EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
  ::DestroyIcon(large_icon);
}

// This test case makes sure that when an HICON is created from an SkBitmap,
// the returned handle is valid and refers to an icon with the expected
// dimentions color depth etc.
TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) {
  scoped_ptr<SkBitmap> bitmap;
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  bitmap->allocPixels();
  HICON icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_NE(icon, static_cast<HICON>(NULL));
  ICONINFO icon_info;
  ASSERT_TRUE(::GetIconInfo(icon, &icon_info));
  EXPECT_TRUE(icon_info.fIcon);

  // Now that have the icon information, we should obtain the specification of
  // the icon's bitmap and make sure it matches the specification of the
  // SkBitmap we started with.
  //
  // The bitmap handle contained in the icon information is a handle to a
  // compatible bitmap so we need to call ::GetDIBits() in order to retrieve
  // the bitmap's header information.
  BITMAPINFO bitmap_info;
  ::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO));
  bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO);
  HDC hdc = ::GetDC(NULL);
  int result = ::GetDIBits(hdc,
                           icon_info.hbmColor,
                           0,
                           kSmallIconWidth,
                           NULL,
                           &bitmap_info,
                           DIB_RGB_COLORS);
  ASSERT_GT(result, 0);
  EXPECT_EQ(bitmap_info.bmiHeader.biWidth, kSmallIconWidth);
  EXPECT_EQ(bitmap_info.bmiHeader.biHeight, kSmallIconHeight);
  EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1);
  EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32);
  ::ReleaseDC(NULL, hdc);
  ::DestroyIcon(icon);
}

// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap
// creates a valid .ico file given an SkBitmap.
TEST_F(IconUtilTest, TestCreateIconFile) {
  scoped_ptr<SkBitmap> bitmap;
  std::wstring icon_filename(test_data_directory_);
  file_util::AppendToPath(&icon_filename, kTempIconFilename);

  // Allocating the bitmap.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  bitmap->allocPixels();

  // Setting the pixels to black.
  memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4);

  EXPECT_TRUE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                   icon_filename));

  // We are currently only testing that it is possible to load an icon from
  // the .ico file we just created. We don't really check the additional icon
  // images created by IconUtil::CreateIconFileFromSkBitmap.
  HICON icon = LoadIconFromFile(icon_filename,
                                kSmallIconWidth,
                                kSmallIconHeight);
  EXPECT_NE(icon, static_cast<HICON>(NULL));
  if (icon != NULL) {
    ::DestroyIcon(icon);
  }
}

<commit_msg>Initialize memory to avoid UMR in unit test.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <atlbase.h>
#include <atlapp.h>
#include <atlmisc.h>

#include "chrome/common/gfx/icon_util.h"
#include "base/gfx/size.h"
#include "base/scoped_ptr.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "skia/include/SkBitmap.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

  static const wchar_t* const kSmallIconName = L"icon_util\\16_X_16_icon.ico";
  static const wchar_t* const kLargeIconName = L"icon_util\\128_X_128_icon.ico";
  static const wchar_t* const kTempIconFilename = L"temp_test_icon.ico";

  class IconUtilTest : public testing::Test {
   public:
    IconUtilTest() {
      PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_);
    }
    ~IconUtilTest() {}

    static const int kSmallIconWidth = 16;
    static const int kSmallIconHeight = 16;
    static const int kLargeIconWidth = 128;
    static const int kLargeIconHeight = 128;

    // Given a file name for an .ico file and an image dimentions, this
    // function loads the icon and returns an HICON handle.
    HICON LoadIconFromFile(const std::wstring& filename,
                           int width,
                           int height) {
      HICON icon =
          static_cast<HICON>(LoadImage(NULL,
                                       filename.c_str(),
                                       IMAGE_ICON,
                                       width,
                                       height,
                                       LR_LOADTRANSPARENT | LR_LOADFROMFILE));
      return icon;
    }

   protected:
    // The root directory for test files.
    std::wstring test_data_directory_;

   private:
    DISALLOW_EVIL_CONSTRUCTORS(IconUtilTest);
  };
};

// The following test case makes sure IconUtil::SkBitmapFromHICON fails
// gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestIconToBitmapInvalidParameters) {
  std::wstring icon_filename(test_data_directory_);
  file_util::AppendToPath(&icon_filename, kSmallIconName);
  gfx::Size icon_size(kSmallIconWidth, kSmallIconHeight);
  HICON icon = LoadIconFromFile(icon_filename,
                                icon_size.width(),
                                icon_size.height());
  ASSERT_TRUE(icon != NULL);

  // Invalid size parameter.
  gfx::Size invalid_icon_size(kSmallIconHeight, 0);
  EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(icon, invalid_icon_size),
            static_cast<SkBitmap*>(NULL));

  // Invalid icon.
  EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(NULL, icon_size),
            static_cast<SkBitmap*>(NULL));

  // The following code should succeed.
  scoped_ptr<SkBitmap> bitmap;
  bitmap.reset(IconUtil::CreateSkBitmapFromHICON(icon, icon_size));
  EXPECT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  ::DestroyIcon(icon);
}

// The following test case makes sure IconUtil::CreateHICONFromSkBitmap fails
// gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) {
  HICON icon = NULL;
  scoped_ptr<SkBitmap> bitmap;

  // Wrong bitmap format.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_EQ(icon, static_cast<HICON>(NULL));

  // Invalid bitmap size.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_EQ(icon, static_cast<HICON>(NULL));

  // Valid bitmap configuration but no pixels allocated.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_TRUE(icon == NULL);
}

// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap
// fails gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) {
  scoped_ptr<SkBitmap> bitmap;
  std::wstring valid_icon_filename(test_data_directory_);
  file_util::AppendToPath(&valid_icon_filename, kSmallIconName);
  std::wstring invalid_icon_filename(L"C:\\<>?.ico");

  // Wrong bitmap format.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    valid_icon_filename));

  // Invalid bitmap size.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    valid_icon_filename));

  // Bitmap with no allocated pixels.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    valid_icon_filename));

  // Invalid file name.
  bitmap->allocPixels();
  // Setting the pixels to black.
  memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4);
  EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                    invalid_icon_filename));
}

// This test case makes sure that when we load an icon from disk and convert
// the HICON into a bitmap, the bitmap has the expected format and dimentions.
TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) {
  scoped_ptr<SkBitmap> bitmap;
  std::wstring small_icon_filename(test_data_directory_);
  file_util::AppendToPath(&small_icon_filename, kSmallIconName);
  gfx::Size small_icon_size(kSmallIconWidth, kSmallIconHeight);
  HICON small_icon = LoadIconFromFile(small_icon_filename,
                                      small_icon_size.width(),
                                      small_icon_size.height());
  ASSERT_NE(small_icon, static_cast<HICON>(NULL));
  bitmap.reset(IconUtil::CreateSkBitmapFromHICON(small_icon, small_icon_size));
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  EXPECT_EQ(bitmap->width(), small_icon_size.width());
  EXPECT_EQ(bitmap->height(), small_icon_size.height());
  EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
  ::DestroyIcon(small_icon);

  std::wstring large_icon_filename(test_data_directory_);
  file_util::AppendToPath(&large_icon_filename, kLargeIconName);
  gfx::Size large_icon_size(kLargeIconWidth, kLargeIconHeight);
  HICON large_icon = LoadIconFromFile(large_icon_filename,
                                      large_icon_size.width(),
                                      large_icon_size.height());
  ASSERT_NE(large_icon, static_cast<HICON>(NULL));
  bitmap.reset(IconUtil::CreateSkBitmapFromHICON(large_icon, large_icon_size));
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  EXPECT_EQ(bitmap->width(), large_icon_size.width());
  EXPECT_EQ(bitmap->height(), large_icon_size.height());
  EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
  ::DestroyIcon(large_icon);
}

// This test case makes sure that when an HICON is created from an SkBitmap,
// the returned handle is valid and refers to an icon with the expected
// dimentions color depth etc.
TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) {
  scoped_ptr<SkBitmap> bitmap;
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  bitmap->allocPixels();
  HICON icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_NE(icon, static_cast<HICON>(NULL));
  ICONINFO icon_info;
  ASSERT_TRUE(::GetIconInfo(icon, &icon_info));
  EXPECT_TRUE(icon_info.fIcon);

  // Now that have the icon information, we should obtain the specification of
  // the icon's bitmap and make sure it matches the specification of the
  // SkBitmap we started with.
  //
  // The bitmap handle contained in the icon information is a handle to a
  // compatible bitmap so we need to call ::GetDIBits() in order to retrieve
  // the bitmap's header information.
  BITMAPINFO bitmap_info;
  ::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO));
  bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO);
  HDC hdc = ::GetDC(NULL);
  int result = ::GetDIBits(hdc,
                           icon_info.hbmColor,
                           0,
                           kSmallIconWidth,
                           NULL,
                           &bitmap_info,
                           DIB_RGB_COLORS);
  ASSERT_GT(result, 0);
  EXPECT_EQ(bitmap_info.bmiHeader.biWidth, kSmallIconWidth);
  EXPECT_EQ(bitmap_info.bmiHeader.biHeight, kSmallIconHeight);
  EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1);
  EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32);
  ::ReleaseDC(NULL, hdc);
  ::DestroyIcon(icon);
}

// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap
// creates a valid .ico file given an SkBitmap.
TEST_F(IconUtilTest, TestCreateIconFile) {
  scoped_ptr<SkBitmap> bitmap;
  std::wstring icon_filename(test_data_directory_);
  file_util::AppendToPath(&icon_filename, kTempIconFilename);

  // Allocating the bitmap.
  bitmap.reset(new SkBitmap);
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
                    kSmallIconWidth,
                    kSmallIconHeight);
  bitmap->allocPixels();

  // Setting the pixels to black.
  memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4);

  EXPECT_TRUE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
                                                   icon_filename));

  // We are currently only testing that it is possible to load an icon from
  // the .ico file we just created. We don't really check the additional icon
  // images created by IconUtil::CreateIconFileFromSkBitmap.
  HICON icon = LoadIconFromFile(icon_filename,
                                kSmallIconWidth,
                                kSmallIconHeight);
  EXPECT_NE(icon, static_cast<HICON>(NULL));
  if (icon != NULL) {
    ::DestroyIcon(icon);
  }
}

<|endoftext|>


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

#include "chrome/common/pepper_plugin_registry.h"

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "remoting/client/plugin/pepper_entrypoints.h"

const char* PepperPluginRegistry::kPDFPluginName = "Chrome PDF Viewer";
const char* PepperPluginRegistry::kPDFPluginMimeType = "application/pdf";
const char* PepperPluginRegistry::kPDFPluginExtension = "pdf";
const char* PepperPluginRegistry::kPDFPluginDescription =
    "Portable Document Format";

const char* PepperPluginRegistry::kNaClPluginName = "Chrome NaCl";
const char* PepperPluginRegistry::kNaClPluginMimeType =
    "application/x-nacl";
const char* PepperPluginRegistry::kNaClPluginExtension = "nexe";
const char* PepperPluginRegistry::kNaClPluginDescription =
    "Native Client Executable";

PepperPluginInfo::PepperPluginInfo()
    : is_internal(false),
      is_out_of_process(false) {
}

PepperPluginInfo::~PepperPluginInfo() {}

// static
PepperPluginRegistry* PepperPluginRegistry::GetInstance() {
  static PepperPluginRegistry* registry = NULL;
  // This object leaks.  It is a temporary hack to work around a crash.
  // http://code.google.com/p/chromium/issues/detail?id=63234
  if (!registry)
    registry = new PepperPluginRegistry;
  return registry;
}

// static
void PepperPluginRegistry::GetList(std::vector<PepperPluginInfo>* plugins) {
  InternalPluginInfoList internal_plugin_info;
  GetInternalPluginInfo(&internal_plugin_info);
  for (InternalPluginInfoList::const_iterator it =
         internal_plugin_info.begin();
       it != internal_plugin_info.end();
       ++it) {
    plugins->push_back(*it);
  }

  GetPluginInfoFromSwitch(plugins);
  GetExtraPlugins(plugins);
}

// static
void PepperPluginRegistry::PreloadModules() {
  std::vector<PepperPluginInfo> plugins;
  GetList(&plugins);
  for (size_t i = 0; i < plugins.size(); ++i) {
    if (!plugins[i].is_internal && !plugins[i].is_out_of_process) {
      base::NativeLibrary library = base::LoadNativeLibrary(plugins[i].path);
      LOG_IF(WARNING, !library) << "Unable to load plugin "
                                << plugins[i].path.value();
    }
  }
}

// static
void PepperPluginRegistry::GetPluginInfoFromSwitch(
    std::vector<PepperPluginInfo>* plugins) {
  const std::string value =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
          switches::kRegisterPepperPlugins);
  if (value.empty())
    return;

  bool out_of_process =
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kPpapiOutOfProcess);

  // FORMAT:
  // command-line = <plugin-entry> + *( LWS + "," + LWS + <plugin-entry> )
  // plugin-entry = <file-path> + ["#" + <name> + ["#" + <description>]] +
  //                *1( LWS + ";" + LWS + <mime-type> )

  std::vector<std::string> modules;
  base::SplitString(value, ',', &modules);
  for (size_t i = 0; i < modules.size(); ++i) {
    std::vector<std::string> parts;
    base::SplitString(modules[i], ';', &parts);
    if (parts.size() < 2) {
      DLOG(ERROR) << "Required mime-type not found";
      continue;
    }

    std::vector<std::string> name_parts;
    base::SplitString(parts[0], '#', &name_parts);

    PepperPluginInfo plugin;
    plugin.is_out_of_process = out_of_process;
#if defined(OS_WIN)
    // This means we can't provide plugins from non-ASCII paths, but
    // since this switch is only for development I don't think that's
    // too awful.
    plugin.path = FilePath(ASCIIToUTF16(name_parts[0]));
#else
    plugin.path = FilePath(name_parts[0]);
#endif
    if (name_parts.size() > 1)
      plugin.name = name_parts[1];
    if (name_parts.size() > 2)
      plugin.type_descriptions = name_parts[2];
    for (size_t j = 1; j < parts.size(); ++j)
      plugin.mime_types.push_back(parts[j]);

    plugins->push_back(plugin);
  }
}

// static
void PepperPluginRegistry::GetExtraPlugins(
    std::vector<PepperPluginInfo>* plugins) {
  // Once we're sandboxed, we can't know if the PDF plugin is
  // available or not; but (on Linux) this function is always called
  // once before we're sandboxed.  So the first time through test if
  // the file is available and then skip the check on subsequent calls
  // if yes.
  static bool skip_pdf_file_check = false;
  FilePath path;
  if (PathService::Get(chrome::FILE_PDF_PLUGIN, &path)) {
    if (skip_pdf_file_check || file_util::PathExists(path)) {
      PepperPluginInfo pdf;
      pdf.path = path;
      pdf.name = kPDFPluginName;
      pdf.mime_types.push_back(kPDFPluginMimeType);
      pdf.file_extensions = kPDFPluginExtension;
      pdf.type_descriptions = kPDFPluginDescription;
      plugins->push_back(pdf);

      skip_pdf_file_check = true;
    }
  }

  // Verify that we enable nacl on the command line.  The name of the
  // switch varies between the browser and renderer process.
  bool enable_nacl =
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl) ||
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kInternalNaCl);

  static bool skip_nacl_file_check = false;
  if (enable_nacl && PathService::Get(chrome::FILE_NACL_PLUGIN, &path)) {
    if (skip_nacl_file_check || file_util::PathExists(path)) {
      PepperPluginInfo nacl;
      nacl.path = path;
      nacl.name = kNaClPluginName;
      nacl.mime_types.push_back(kNaClPluginMimeType);

      // TODO(bbudge) Remove this mime type after NaCl tree has been updated.
      const char* kNaClPluginOldMimeType =
          "application/x-ppapi-nacl-srpc";
      nacl.mime_types.push_back(kNaClPluginOldMimeType);

      nacl.file_extensions = kNaClPluginExtension;
      nacl.type_descriptions = kNaClPluginDescription;
      plugins->push_back(nacl);

      skip_nacl_file_check = true;
    }
  }
}

PepperPluginRegistry::InternalPluginInfo::InternalPluginInfo() {
  is_internal = true;
}

// static
void PepperPluginRegistry::GetInternalPluginInfo(
    InternalPluginInfoList* plugin_info) {
  // Currently, to centralize the internal plugin registration logic, we
  // hardcode the list of plugins, mimetypes, and registration information
  // in this function.  This is gross, but because the GetList() function is
  // called from both the renderer and browser the other option is to force a
  // special register function for each plugin to be called by both
  // RendererMain() and BrowserMain().  This seemed like the better tradeoff.
  //
  // TODO(ajwong): Think up a better way to maintain the plugin registration
  // information. Perhaps by construction of a singly linked list of
  // plugin initializers that is built with static initializers?

#if defined(ENABLE_REMOTING)
  if (CommandLine::ForCurrentProcess()->HasSwitch(
      switches::kEnableRemoting)) {
    InternalPluginInfo info;
    // Add the chromoting plugin.
    DCHECK(info.is_internal);
    info.path =
        FilePath(FILE_PATH_LITERAL("internal-chromoting"));
    info.mime_types.push_back("pepper-application/x-chromoting");
    info.entry_points.get_interface = remoting::PPP_GetInterface;
    info.entry_points.initialize_module = remoting::PPP_InitializeModule;
    info.entry_points.shutdown_module = remoting::PPP_ShutdownModule;

    plugin_info->push_back(info);
  }
#endif
}

bool PepperPluginRegistry::RunOutOfProcessForPlugin(
    const FilePath& path) const {
  // TODO(brettw) don't recompute this every time. But since this Pepper
  // switch is only for development, it's OK for now.
  std::vector<PepperPluginInfo> plugins;
  GetList(&plugins);
  for (size_t i = 0; i < plugins.size(); ++i) {
    if (path == plugins[i].path)
      return plugins[i].is_out_of_process;
  }
  return false;
}

webkit::ppapi::PluginModule* PepperPluginRegistry::GetModule(
    const FilePath& path) {
  NonOwningModuleMap::iterator it = live_modules_.find(path);
  if (it == live_modules_.end())
    return NULL;
  return it->second;
}

void PepperPluginRegistry::AddLiveModule(const FilePath& path,
                                         webkit::ppapi::PluginModule* module) {
  DCHECK(live_modules_.find(path) == live_modules_.end());
  live_modules_[path] = module;
}

void PepperPluginRegistry::PluginModuleDestroyed(
    webkit::ppapi::PluginModule* destroyed_module) {
  // Modules aren't destroyed very often and there are normally at most a
  // couple of them. So for now we just do a brute-force search.
  for (NonOwningModuleMap::iterator i = live_modules_.begin();
       i != live_modules_.end(); ++i) {
    if (i->second == destroyed_module) {
      live_modules_.erase(i);
      return;
    }
  }
  NOTREACHED();  // Should have always found the module above.
}

PepperPluginRegistry::~PepperPluginRegistry() {
  // Explicitly clear all preloaded modules first. This will cause callbacks
  // to erase these modules from the live_modules_ list, and we don't want
  // that to happen implicitly out-of-order.
  preloaded_modules_.clear();

  DCHECK(live_modules_.empty());
}

PepperPluginRegistry::PepperPluginRegistry() {
  InternalPluginInfoList internal_plugin_info;
  GetInternalPluginInfo(&internal_plugin_info);

  // Register modules for these suckers.
  for (InternalPluginInfoList::const_iterator it =
         internal_plugin_info.begin();
       it != internal_plugin_info.end();
       ++it) {
    const FilePath& path = it->path;
    scoped_refptr<webkit::ppapi::PluginModule> module(
        new webkit::ppapi::PluginModule(this));
    if (!module->InitAsInternalPlugin(it->entry_points)) {
      DLOG(ERROR) << "Failed to load pepper module: " << path.value();
      continue;
    }
    module->set_name(it->name);
    preloaded_modules_[path] = module;
    AddLiveModule(path, module);
  }

  // Add the modules specified on the command line last so that they can
  // override the internal plugins.
  std::vector<PepperPluginInfo> plugins;
  GetPluginInfoFromSwitch(&plugins);
  GetExtraPlugins(&plugins);
  for (size_t i = 0; i < plugins.size(); ++i) {
    if (plugins[i].is_out_of_process)
      continue;  // Only preload in-process plugins.

    const FilePath& path = plugins[i].path;
    scoped_refptr<webkit::ppapi::PluginModule> module(
        new webkit::ppapi::PluginModule(this));
    // Must call this before bailing out later since the PluginModule's
    // destructor will call the corresponding Remove in the "continue" case.
    AddLiveModule(path, module);
    if (!module->InitAsLibrary(path)) {
      DLOG(ERROR) << "Failed to load pepper module: " << path.value();
      continue;
    }
    module->set_name(plugins[i].name);
    preloaded_modules_[path] = module;
  }
}
<commit_msg>Fix pepper plugin description regression<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/pepper_plugin_registry.h"

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "remoting/client/plugin/pepper_entrypoints.h"

const char* PepperPluginRegistry::kPDFPluginName = "Chrome PDF Viewer";
const char* PepperPluginRegistry::kPDFPluginMimeType = "application/pdf";
const char* PepperPluginRegistry::kPDFPluginExtension = "pdf";
const char* PepperPluginRegistry::kPDFPluginDescription =
    "Portable Document Format";

const char* PepperPluginRegistry::kNaClPluginName = "Chrome NaCl";
const char* PepperPluginRegistry::kNaClPluginMimeType =
    "application/x-nacl";
const char* PepperPluginRegistry::kNaClPluginExtension = "nexe";
const char* PepperPluginRegistry::kNaClPluginDescription =
    "Native Client Executable";

PepperPluginInfo::PepperPluginInfo()
    : is_internal(false),
      is_out_of_process(false) {
}

PepperPluginInfo::~PepperPluginInfo() {}

// static
PepperPluginRegistry* PepperPluginRegistry::GetInstance() {
  static PepperPluginRegistry* registry = NULL;
  // This object leaks.  It is a temporary hack to work around a crash.
  // http://code.google.com/p/chromium/issues/detail?id=63234
  if (!registry)
    registry = new PepperPluginRegistry;
  return registry;
}

// static
void PepperPluginRegistry::GetList(std::vector<PepperPluginInfo>* plugins) {
  InternalPluginInfoList internal_plugin_info;
  GetInternalPluginInfo(&internal_plugin_info);
  for (InternalPluginInfoList::const_iterator it =
         internal_plugin_info.begin();
       it != internal_plugin_info.end();
       ++it) {
    plugins->push_back(*it);
  }

  GetPluginInfoFromSwitch(plugins);
  GetExtraPlugins(plugins);
}

// static
void PepperPluginRegistry::PreloadModules() {
  std::vector<PepperPluginInfo> plugins;
  GetList(&plugins);
  for (size_t i = 0; i < plugins.size(); ++i) {
    if (!plugins[i].is_internal && !plugins[i].is_out_of_process) {
      base::NativeLibrary library = base::LoadNativeLibrary(plugins[i].path);
      LOG_IF(WARNING, !library) << "Unable to load plugin "
                                << plugins[i].path.value();
    }
  }
}

// static
void PepperPluginRegistry::GetPluginInfoFromSwitch(
    std::vector<PepperPluginInfo>* plugins) {
  const std::string value =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
          switches::kRegisterPepperPlugins);
  if (value.empty())
    return;

  bool out_of_process =
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kPpapiOutOfProcess);

  // FORMAT:
  // command-line = <plugin-entry> + *( LWS + "," + LWS + <plugin-entry> )
  // plugin-entry = <file-path> + ["#" + <name> + ["#" + <description>]] +
  //                *1( LWS + ";" + LWS + <mime-type> )

  std::vector<std::string> modules;
  base::SplitString(value, ',', &modules);
  for (size_t i = 0; i < modules.size(); ++i) {
    std::vector<std::string> parts;
    base::SplitString(modules[i], ';', &parts);
    if (parts.size() < 2) {
      DLOG(ERROR) << "Required mime-type not found";
      continue;
    }

    std::vector<std::string> name_parts;
    base::SplitString(parts[0], '#', &name_parts);

    PepperPluginInfo plugin;
    plugin.is_out_of_process = out_of_process;
#if defined(OS_WIN)
    // This means we can't provide plugins from non-ASCII paths, but
    // since this switch is only for development I don't think that's
    // too awful.
    plugin.path = FilePath(ASCIIToUTF16(name_parts[0]));
#else
    plugin.path = FilePath(name_parts[0]);
#endif
    if (name_parts.size() > 1)
      plugin.name = name_parts[1];
    if (name_parts.size() > 2) {
      plugin.description = name_parts[2];
      plugin.type_descriptions = name_parts[2];
    }
    for (size_t j = 1; j < parts.size(); ++j)
      plugin.mime_types.push_back(parts[j]);

    plugins->push_back(plugin);
  }
}

// static
void PepperPluginRegistry::GetExtraPlugins(
    std::vector<PepperPluginInfo>* plugins) {
  // Once we're sandboxed, we can't know if the PDF plugin is
  // available or not; but (on Linux) this function is always called
  // once before we're sandboxed.  So the first time through test if
  // the file is available and then skip the check on subsequent calls
  // if yes.
  static bool skip_pdf_file_check = false;
  FilePath path;
  if (PathService::Get(chrome::FILE_PDF_PLUGIN, &path)) {
    if (skip_pdf_file_check || file_util::PathExists(path)) {
      PepperPluginInfo pdf;
      pdf.path = path;
      pdf.name = kPDFPluginName;
      pdf.mime_types.push_back(kPDFPluginMimeType);
      pdf.file_extensions = kPDFPluginExtension;
      pdf.type_descriptions = kPDFPluginDescription;
      plugins->push_back(pdf);

      skip_pdf_file_check = true;
    }
  }

  // Verify that we enable nacl on the command line.  The name of the
  // switch varies between the browser and renderer process.
  bool enable_nacl =
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl) ||
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kInternalNaCl);

  static bool skip_nacl_file_check = false;
  if (enable_nacl && PathService::Get(chrome::FILE_NACL_PLUGIN, &path)) {
    if (skip_nacl_file_check || file_util::PathExists(path)) {
      PepperPluginInfo nacl;
      nacl.path = path;
      nacl.name = kNaClPluginName;
      nacl.mime_types.push_back(kNaClPluginMimeType);

      // TODO(bbudge) Remove this mime type after NaCl tree has been updated.
      const char* kNaClPluginOldMimeType =
          "application/x-ppapi-nacl-srpc";
      nacl.mime_types.push_back(kNaClPluginOldMimeType);

      nacl.file_extensions = kNaClPluginExtension;
      nacl.type_descriptions = kNaClPluginDescription;
      plugins->push_back(nacl);

      skip_nacl_file_check = true;
    }
  }
}

PepperPluginRegistry::InternalPluginInfo::InternalPluginInfo() {
  is_internal = true;
}

// static
void PepperPluginRegistry::GetInternalPluginInfo(
    InternalPluginInfoList* plugin_info) {
  // Currently, to centralize the internal plugin registration logic, we
  // hardcode the list of plugins, mimetypes, and registration information
  // in this function.  This is gross, but because the GetList() function is
  // called from both the renderer and browser the other option is to force a
  // special register function for each plugin to be called by both
  // RendererMain() and BrowserMain().  This seemed like the better tradeoff.
  //
  // TODO(ajwong): Think up a better way to maintain the plugin registration
  // information. Perhaps by construction of a singly linked list of
  // plugin initializers that is built with static initializers?

#if defined(ENABLE_REMOTING)
  if (CommandLine::ForCurrentProcess()->HasSwitch(
      switches::kEnableRemoting)) {
    InternalPluginInfo info;
    // Add the chromoting plugin.
    DCHECK(info.is_internal);
    info.path =
        FilePath(FILE_PATH_LITERAL("internal-chromoting"));
    info.mime_types.push_back("pepper-application/x-chromoting");
    info.entry_points.get_interface = remoting::PPP_GetInterface;
    info.entry_points.initialize_module = remoting::PPP_InitializeModule;
    info.entry_points.shutdown_module = remoting::PPP_ShutdownModule;

    plugin_info->push_back(info);
  }
#endif
}

bool PepperPluginRegistry::RunOutOfProcessForPlugin(
    const FilePath& path) const {
  // TODO(brettw) don't recompute this every time. But since this Pepper
  // switch is only for development, it's OK for now.
  std::vector<PepperPluginInfo> plugins;
  GetList(&plugins);
  for (size_t i = 0; i < plugins.size(); ++i) {
    if (path == plugins[i].path)
      return plugins[i].is_out_of_process;
  }
  return false;
}

webkit::ppapi::PluginModule* PepperPluginRegistry::GetModule(
    const FilePath& path) {
  NonOwningModuleMap::iterator it = live_modules_.find(path);
  if (it == live_modules_.end())
    return NULL;
  return it->second;
}

void PepperPluginRegistry::AddLiveModule(const FilePath& path,
                                         webkit::ppapi::PluginModule* module) {
  DCHECK(live_modules_.find(path) == live_modules_.end());
  live_modules_[path] = module;
}

void PepperPluginRegistry::PluginModuleDestroyed(
    webkit::ppapi::PluginModule* destroyed_module) {
  // Modules aren't destroyed very often and there are normally at most a
  // couple of them. So for now we just do a brute-force search.
  for (NonOwningModuleMap::iterator i = live_modules_.begin();
       i != live_modules_.end(); ++i) {
    if (i->second == destroyed_module) {
      live_modules_.erase(i);
      return;
    }
  }
  NOTREACHED();  // Should have always found the module above.
}

PepperPluginRegistry::~PepperPluginRegistry() {
  // Explicitly clear all preloaded modules first. This will cause callbacks
  // to erase these modules from the live_modules_ list, and we don't want
  // that to happen implicitly out-of-order.
  preloaded_modules_.clear();

  DCHECK(live_modules_.empty());
}

PepperPluginRegistry::PepperPluginRegistry() {
  InternalPluginInfoList internal_plugin_info;
  GetInternalPluginInfo(&internal_plugin_info);

  // Register modules for these suckers.
  for (InternalPluginInfoList::const_iterator it =
         internal_plugin_info.begin();
       it != internal_plugin_info.end();
       ++it) {
    const FilePath& path = it->path;
    scoped_refptr<webkit::ppapi::PluginModule> module(
        new webkit::ppapi::PluginModule(this));
    if (!module->InitAsInternalPlugin(it->entry_points)) {
      DLOG(ERROR) << "Failed to load pepper module: " << path.value();
      continue;
    }
    module->set_name(it->name);
    preloaded_modules_[path] = module;
    AddLiveModule(path, module);
  }

  // Add the modules specified on the command line last so that they can
  // override the internal plugins.
  std::vector<PepperPluginInfo> plugins;
  GetPluginInfoFromSwitch(&plugins);
  GetExtraPlugins(&plugins);
  for (size_t i = 0; i < plugins.size(); ++i) {
    if (plugins[i].is_out_of_process)
      continue;  // Only preload in-process plugins.

    const FilePath& path = plugins[i].path;
    scoped_refptr<webkit::ppapi::PluginModule> module(
        new webkit::ppapi::PluginModule(this));
    // Must call this before bailing out later since the PluginModule's
    // destructor will call the corresponding Remove in the "continue" case.
    AddLiveModule(path, module);
    if (!module->InitAsLibrary(path)) {
      DLOG(ERROR) << "Failed to load pepper module: " << path.value();
      continue;
    }
    module->set_name(plugins[i].name);
    preloaded_modules_[path] = module;
  }
}
<|endoftext|>