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

#include "chrome/browser/ui/webui/ntp/android/partner_bookmarks_shim.h"

#include "base/lazy_instance.h"
#include "base/prefs/pref_service.h"
#include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace {

// PartnerModelKeeper is used as a singleton to store an immutable hierarchy
// of partner bookmarks.  The hierarchy is retrieved from the partner bookmarks
// provider and doesn't depend on the user profile.
// The retrieved hierarchy persists
// PartnerBookmarksShim is responsible to applying and storing the user changes
// (deletions/renames) in the user profile, thus keeping the hierarchy intact.
struct PartnerModelKeeper {
  scoped_ptr<BookmarkNode> partner_bookmarks_root;
  bool loaded;

  PartnerModelKeeper()
    : loaded(false) {}
};

base::LazyInstance<PartnerModelKeeper> g_partner_model_keeper =
    LAZY_INSTANCE_INITIALIZER;

const void* kPartnerBookmarksShimUserDataKey =
    &kPartnerBookmarksShimUserDataKey;

// Dictionary keys for entries in the kPartnerBookmarksMapping pref.
static const char kMappingUrl[] = "url";
static const char kMappingProviderTitle[] = "provider_title";
static const char kMappingTitle[] = "mapped_title";

}  // namespace

// static
PartnerBookmarksShim* PartnerBookmarksShim::BuildForBrowserContext(
    content::BrowserContext* browser_context) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  PartnerBookmarksShim* data =
      reinterpret_cast<PartnerBookmarksShim*>(
          browser_context->GetUserData(kPartnerBookmarksShimUserDataKey));
  if (data)
    return data;

  data = new PartnerBookmarksShim(
      Profile::FromBrowserContext(browser_context)->GetPrefs());
  browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, data);
  data->ReloadNodeMapping();
  return data;
}

// static
void PartnerBookmarksShim::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterListPref(
      prefs::kPartnerBookmarkMappings,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}

bool PartnerBookmarksShim::IsLoaded() const {
  return g_partner_model_keeper.Get().loaded;
}

bool PartnerBookmarksShim::HasPartnerBookmarks() const {
  DCHECK(IsLoaded());
  return g_partner_model_keeper.Get().partner_bookmarks_root.get() != NULL;
}

bool PartnerBookmarksShim::IsReachable(const BookmarkNode* node) const {
  DCHECK(HasPartnerBookmarks());
  DCHECK(IsPartnerBookmark(node));
  for (const BookmarkNode* i = node; i != NULL; i = i->parent()) {
    const NodeRenamingMapKey key(i->url(), i->GetTitle());
    NodeRenamingMap::const_iterator remap = node_rename_remove_map_.find(key);
    if (remap != node_rename_remove_map_.end() && remap->second.empty())
      return false;
  }
  return true;
}

void PartnerBookmarksShim::RemoveBookmark(const BookmarkNode* node) {
  RenameBookmark(node, base::string16());
}

void PartnerBookmarksShim::RenameBookmark(const BookmarkNode* node,
                                          const base::string16& title) {
  const NodeRenamingMapKey key(node->url(), node->GetTitle());
  node_rename_remove_map_[key] = title;
  SaveNodeMapping();
  FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
                    PartnerShimChanged(this));
}

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

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

const BookmarkNode* PartnerBookmarksShim::GetNodeByID(int64 id) const {
  DCHECK(IsLoaded());
  DCHECK(HasPartnerBookmarks());
  if (!HasPartnerBookmarks())
    return NULL;
  return GetNodeByID(GetPartnerBookmarksRoot(), id);
}

base::string16 PartnerBookmarksShim::GetTitle(const BookmarkNode* node) const {
  DCHECK(node);
  DCHECK(IsPartnerBookmark(node));

  const NodeRenamingMapKey key(node->url(), node->GetTitle());
  NodeRenamingMap::const_iterator i = node_rename_remove_map_.find(key);
  if (i != node_rename_remove_map_.end())
    return i->second;

  return node->GetTitle();
}

bool PartnerBookmarksShim::IsPartnerBookmark(const BookmarkNode* node) const {
  DCHECK(IsLoaded());
  if (!HasPartnerBookmarks())
    return false;
  const BookmarkNode* parent = node;
  while (parent) {
    if (parent == GetPartnerBookmarksRoot())
      return true;
    parent = parent->parent();
  }
  return false;
}

const BookmarkNode* PartnerBookmarksShim::GetPartnerBookmarksRoot() const {
  return g_partner_model_keeper.Get().partner_bookmarks_root.get();
}

void PartnerBookmarksShim::SetPartnerBookmarksRoot(BookmarkNode* root_node) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  g_partner_model_keeper.Get().partner_bookmarks_root.reset(root_node);
  g_partner_model_keeper.Get().loaded = true;
  FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
                    PartnerShimLoaded(this));
}

PartnerBookmarksShim::NodeRenamingMapKey::NodeRenamingMapKey(
    const GURL& url, const base::string16& provider_title)
    : url_(url), provider_title_(provider_title) {}

PartnerBookmarksShim::NodeRenamingMapKey::~NodeRenamingMapKey() {}

bool operator<(const PartnerBookmarksShim::NodeRenamingMapKey& a,
               const PartnerBookmarksShim::NodeRenamingMapKey& b) {
  return (a.url_ < b.url_) ||
      (a.url_ == b.url_ && a.provider_title_ < b.provider_title_);
}

// static
void PartnerBookmarksShim::ClearInBrowserContextForTesting(
    content::BrowserContext* browser_context) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, 0);
}

// static
void PartnerBookmarksShim::ClearPartnerModelForTesting() {
  g_partner_model_keeper.Get().loaded = false;
  g_partner_model_keeper.Get().partner_bookmarks_root.reset(0);
}

PartnerBookmarksShim::PartnerBookmarksShim(PrefService* prefs)
    : prefs_(prefs),
      observers_(
          ObserverList<PartnerBookmarksShim::Observer>::NOTIFY_EXISTING_ONLY) {
}

PartnerBookmarksShim::~PartnerBookmarksShim() {
  FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
                    ShimBeingDeleted(this));
}

const BookmarkNode* PartnerBookmarksShim::GetNodeByID(
    const BookmarkNode* parent, int64 id) const {
  if (parent->id() == id)
    return parent;
  for (int i = 0, child_count = parent->child_count(); i < child_count; ++i) {
    const BookmarkNode* result = GetNodeByID(parent->GetChild(i), id);
    if (result)
      return result;
  }
  return NULL;
}

void PartnerBookmarksShim::ReloadNodeMapping() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  node_rename_remove_map_.clear();
  if (!prefs_)
    return;

  const base::ListValue* list =
      prefs_->GetList(prefs::kPartnerBookmarkMappings);
  if (!list)
    return;

  for (base::ListValue::const_iterator it = list->begin();
       it != list->end(); ++it) {
    const base::DictionaryValue* dict = NULL;
    if (!*it || !(*it)->GetAsDictionary(&dict)) {
      NOTREACHED();
      continue;
    }

    std::string url;
    base::string16 provider_title;
    base::string16 mapped_title;
    if (!dict->GetString(kMappingUrl, &url) ||
        !dict->GetString(kMappingProviderTitle, &provider_title) ||
        !dict->GetString(kMappingTitle, &mapped_title)) {
      NOTREACHED();
      continue;
    }

    const NodeRenamingMapKey key(GURL(url), provider_title);
    node_rename_remove_map_[key] = mapped_title;
  }
}

void PartnerBookmarksShim::SaveNodeMapping() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!prefs_)
    return;

  base::ListValue list;
  for (NodeRenamingMap::const_iterator i = node_rename_remove_map_.begin();
       i != node_rename_remove_map_.end();
       ++i) {
    base::DictionaryValue* dict = new base::DictionaryValue();
    dict->SetString(kMappingUrl, i->first.url().spec());
    dict->SetString(kMappingProviderTitle, i->first.provider_title());
    dict->SetString(kMappingTitle, i->second);
    list.Append(dict);
  }
  prefs_->Set(prefs::kPartnerBookmarkMappings, list);
}
<commit_msg>[Android] Fix for overzealous partner bookmarks shim.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ntp/android/partner_bookmarks_shim.h"

#include "base/lazy_instance.h"
#include "base/prefs/pref_service.h"
#include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace {

// PartnerModelKeeper is used as a singleton to store an immutable hierarchy
// of partner bookmarks.  The hierarchy is retrieved from the partner bookmarks
// provider and doesn't depend on the user profile.
// The retrieved hierarchy persists
// PartnerBookmarksShim is responsible to applying and storing the user changes
// (deletions/renames) in the user profile, thus keeping the hierarchy intact.
struct PartnerModelKeeper {
  scoped_ptr<BookmarkNode> partner_bookmarks_root;
  bool loaded;

  PartnerModelKeeper()
    : loaded(false) {}
};

base::LazyInstance<PartnerModelKeeper> g_partner_model_keeper =
    LAZY_INSTANCE_INITIALIZER;

const void* kPartnerBookmarksShimUserDataKey =
    &kPartnerBookmarksShimUserDataKey;

// Dictionary keys for entries in the kPartnerBookmarksMapping pref.
static const char kMappingUrl[] = "url";
static const char kMappingProviderTitle[] = "provider_title";
static const char kMappingTitle[] = "mapped_title";

}  // namespace

// static
PartnerBookmarksShim* PartnerBookmarksShim::BuildForBrowserContext(
    content::BrowserContext* browser_context) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  PartnerBookmarksShim* data =
      reinterpret_cast<PartnerBookmarksShim*>(
          browser_context->GetUserData(kPartnerBookmarksShimUserDataKey));
  if (data)
    return data;

  data = new PartnerBookmarksShim(
      Profile::FromBrowserContext(browser_context)->GetPrefs());
  browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, data);
  data->ReloadNodeMapping();
  return data;
}

// static
void PartnerBookmarksShim::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterListPref(
      prefs::kPartnerBookmarkMappings,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}

bool PartnerBookmarksShim::IsLoaded() const {
  return g_partner_model_keeper.Get().loaded;
}

bool PartnerBookmarksShim::HasPartnerBookmarks() const {
  DCHECK(IsLoaded());
  return g_partner_model_keeper.Get().partner_bookmarks_root.get() != NULL;
}

bool PartnerBookmarksShim::IsReachable(const BookmarkNode* node) const {
  DCHECK(IsPartnerBookmark(node));
  if (!HasPartnerBookmarks())
    return false;
  for (const BookmarkNode* i = node; i != NULL; i = i->parent()) {
    const NodeRenamingMapKey key(i->url(), i->GetTitle());
    NodeRenamingMap::const_iterator remap = node_rename_remove_map_.find(key);
    if (remap != node_rename_remove_map_.end() && remap->second.empty())
      return false;
  }
  return true;
}

void PartnerBookmarksShim::RemoveBookmark(const BookmarkNode* node) {
  RenameBookmark(node, base::string16());
}

void PartnerBookmarksShim::RenameBookmark(const BookmarkNode* node,
                                          const base::string16& title) {
  const NodeRenamingMapKey key(node->url(), node->GetTitle());
  node_rename_remove_map_[key] = title;
  SaveNodeMapping();
  FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
                    PartnerShimChanged(this));
}

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

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

const BookmarkNode* PartnerBookmarksShim::GetNodeByID(int64 id) const {
  DCHECK(IsLoaded());
  if (!HasPartnerBookmarks())
    return NULL;
  return GetNodeByID(GetPartnerBookmarksRoot(), id);
}

base::string16 PartnerBookmarksShim::GetTitle(const BookmarkNode* node) const {
  DCHECK(node);
  DCHECK(IsPartnerBookmark(node));

  const NodeRenamingMapKey key(node->url(), node->GetTitle());
  NodeRenamingMap::const_iterator i = node_rename_remove_map_.find(key);
  if (i != node_rename_remove_map_.end())
    return i->second;

  return node->GetTitle();
}

bool PartnerBookmarksShim::IsPartnerBookmark(const BookmarkNode* node) const {
  DCHECK(IsLoaded());
  if (!HasPartnerBookmarks())
    return false;
  const BookmarkNode* parent = node;
  while (parent) {
    if (parent == GetPartnerBookmarksRoot())
      return true;
    parent = parent->parent();
  }
  return false;
}

const BookmarkNode* PartnerBookmarksShim::GetPartnerBookmarksRoot() const {
  return g_partner_model_keeper.Get().partner_bookmarks_root.get();
}

void PartnerBookmarksShim::SetPartnerBookmarksRoot(BookmarkNode* root_node) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  g_partner_model_keeper.Get().partner_bookmarks_root.reset(root_node);
  g_partner_model_keeper.Get().loaded = true;
  FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
                    PartnerShimLoaded(this));
}

PartnerBookmarksShim::NodeRenamingMapKey::NodeRenamingMapKey(
    const GURL& url, const base::string16& provider_title)
    : url_(url), provider_title_(provider_title) {}

PartnerBookmarksShim::NodeRenamingMapKey::~NodeRenamingMapKey() {}

bool operator<(const PartnerBookmarksShim::NodeRenamingMapKey& a,
               const PartnerBookmarksShim::NodeRenamingMapKey& b) {
  return (a.url_ < b.url_) ||
      (a.url_ == b.url_ && a.provider_title_ < b.provider_title_);
}

// static
void PartnerBookmarksShim::ClearInBrowserContextForTesting(
    content::BrowserContext* browser_context) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, 0);
}

// static
void PartnerBookmarksShim::ClearPartnerModelForTesting() {
  g_partner_model_keeper.Get().loaded = false;
  g_partner_model_keeper.Get().partner_bookmarks_root.reset(0);
}

PartnerBookmarksShim::PartnerBookmarksShim(PrefService* prefs)
    : prefs_(prefs),
      observers_(
          ObserverList<PartnerBookmarksShim::Observer>::NOTIFY_EXISTING_ONLY) {
}

PartnerBookmarksShim::~PartnerBookmarksShim() {
  FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
                    ShimBeingDeleted(this));
}

const BookmarkNode* PartnerBookmarksShim::GetNodeByID(
    const BookmarkNode* parent, int64 id) const {
  if (parent->id() == id)
    return parent;
  for (int i = 0, child_count = parent->child_count(); i < child_count; ++i) {
    const BookmarkNode* result = GetNodeByID(parent->GetChild(i), id);
    if (result)
      return result;
  }
  return NULL;
}

void PartnerBookmarksShim::ReloadNodeMapping() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  node_rename_remove_map_.clear();
  if (!prefs_)
    return;

  const base::ListValue* list =
      prefs_->GetList(prefs::kPartnerBookmarkMappings);
  if (!list)
    return;

  for (base::ListValue::const_iterator it = list->begin();
       it != list->end(); ++it) {
    const base::DictionaryValue* dict = NULL;
    if (!*it || !(*it)->GetAsDictionary(&dict)) {
      NOTREACHED();
      continue;
    }

    std::string url;
    base::string16 provider_title;
    base::string16 mapped_title;
    if (!dict->GetString(kMappingUrl, &url) ||
        !dict->GetString(kMappingProviderTitle, &provider_title) ||
        !dict->GetString(kMappingTitle, &mapped_title)) {
      NOTREACHED();
      continue;
    }

    const NodeRenamingMapKey key(GURL(url), provider_title);
    node_rename_remove_map_[key] = mapped_title;
  }
}

void PartnerBookmarksShim::SaveNodeMapping() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!prefs_)
    return;

  base::ListValue list;
  for (NodeRenamingMap::const_iterator i = node_rename_remove_map_.begin();
       i != node_rename_remove_map_.end();
       ++i) {
    base::DictionaryValue* dict = new base::DictionaryValue();
    dict->SetString(kMappingUrl, i->first.url().spec());
    dict->SetString(kMappingProviderTitle, i->first.provider_title());
    dict->SetString(kMappingTitle, i->second);
    list.Append(dict);
  }
  prefs_->Set(prefs::kPartnerBookmarkMappings, list);
}

{'fraction_non_alphanumeric': 0.11791290057518489, 'fraction_numerical': 0.002699847399929569, 'mean_word_length': 5.209548104956268, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2013070', 'n_tokens_mistral': 5450, 'n_tokens_neox': 5410, 'n_words': 980}