Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java

Issue 839663003: Upstream partner customizations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed tests Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7c63a288d0ba98b491da7249423aa93f0e8f2ea
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java
@@ -0,0 +1,272 @@
+// Copyright 2015 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.
+
+package org.chromium.chrome.browser.partnerbookmarks;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+/**
+ * Reads bookmarks from the partner content provider (if any).
+*/
+public class PartnerBookmarksReader {
+ private static final String TAG = "PartnerBookmarksReader";
+
+ private static boolean sInitialized = false;
+ private static boolean sForceDisableEditing = false;
+
+ /** Root bookmark id reserved for the implied root of the bookmarks */
+ static final long ROOT_FOLDER_ID = 0;
+
+ /** ID used to indicate an invalid bookmark node. */
+ static final long INVALID_BOOKMARK_ID = -1;
+
+ // JNI c++ pointer
+ private long mNativePartnerBookmarksReader = 0;
+
+ /** The context (used to get a ContentResolver) */
+ protected Context mContext;
+
+ // TODO(aruslan): Move it out to a separate class that defines
+ // a partner bookmarks provider contract, see http://b/6399404
+ /** Object defining a partner bookmark. For this package only. */
+ static class Bookmark {
+ // To be provided by the bookmark extractors.
+ /** Local id of the read bookmark */
+ long mId;
+ /** Read id of the parent node */
+ long mParentId;
+ /** True if it's folder */
+ boolean mIsFolder;
+ /** URL of the bookmark. Required for non-folders. */
+ String mUrl;
+ /** Title of the bookmark. */
+ String mTitle;
+ /** .PNG Favicon of the bookmark. Optional. Not used for folders. */
+ byte[] mFavicon;
+ /** .PNG TouchIcon of the bookmark. Optional. Not used for folders. */
+ byte[] mTouchicon;
+
+ // For auxiliary use while reading.
+ /** Native id of the C++-processed bookmark */
+ long mNativeId = INVALID_BOOKMARK_ID;
+ /** The parent node if any */
+ Bookmark mParent;
+ /** Children nodes for the perfect garbage collection disaster */
+ ArrayList<Bookmark> mEntries = new ArrayList<Bookmark>();
+ }
+
+ /** Closable iterator for available bookmarks. */
+ protected interface BookmarkIterator extends Iterator<Bookmark> {
+ public void close();
+ }
+
+ /** Returns an iterator to the available bookmarks. Called by async task. */
+ protected BookmarkIterator getAvailableBookmarks() {
+ return PartnerBookmarksProviderIterator.createIfAvailable(
+ mContext.getContentResolver());
+ }
+
+ /**
+ * Creates the instance of the reader.
+ * @param context A Context object.
+ */
+ public PartnerBookmarksReader(Context context) {
+ mContext = context;
+ mNativePartnerBookmarksReader = nativeInit();
+ initializeAndDisableEditingIfNecessary();
+ }
+
+ /**
+ * Asynchronously read bookmarks from the partner content provider
+ */
+ public void readBookmarks() {
+ if (mNativePartnerBookmarksReader == 0) {
+ assert false : "readBookmarks called after nativeDestroy.";
+ return;
+ }
+ new ReadBookmarksTask().execute();
+ }
+
+ /**
+ * Called when the partner bookmark needs to be pushed.
+ * @param url The URL.
+ * @param title The title.
+ * @param isFolder True if it's a folder.
+ * @param parentId NATIVE parent folder id.
+ * @param favicon .PNG blob for icon; used if no touchicon is set.
+ * @param touchicon .PNG blob for icon.
+ * @return NATIVE id of a bookmark
+ */
+ private long onBookmarkPush(String url, String title, boolean isFolder, long parentId,
+ byte[] favicon, byte[] touchicon) {
+ return nativeAddPartnerBookmark(mNativePartnerBookmarksReader, url, title,
+ isFolder, parentId, favicon, touchicon);
+ }
+
+ /** Notifies the reader is complete and partner bookmarks should be submitted to the shim. */
+ protected void onBookmarksRead() {
+ nativePartnerBookmarksCreationComplete(mNativePartnerBookmarksReader);
+ nativeDestroy(mNativePartnerBookmarksReader);
+ mNativePartnerBookmarksReader = 0;
+ }
+
+ /** Handles fetching partner bookmarks in a background thread. */
+ private class ReadBookmarksTask extends AsyncTask<Void, Void, Void> {
+ private final Object mRootSync = new Object();
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ BookmarkIterator bookmarkIterator = getAvailableBookmarks();
+ if (bookmarkIterator == null) return null;
+
+ // Get a snapshot of the bookmarks.
+ LinkedHashMap<Long, Bookmark> idMap = new LinkedHashMap<Long, Bookmark>();
+ HashSet<String> urlSet = new HashSet<String>();
+
+ Bookmark rootBookmarksFolder = createRootBookmarksFolderBookmark();
+ idMap.put(ROOT_FOLDER_ID, rootBookmarksFolder);
+
+ while (bookmarkIterator.hasNext()) {
+ Bookmark bookmark = bookmarkIterator.next();
+ if (bookmark == null) continue;
+
+ // Check for duplicate ids.
+ if (idMap.containsKey(bookmark.mId)) {
+ Log.i(TAG, "Duplicate bookmark id: "
+ + bookmark.mId + ". Dropping bookmark.");
+ continue;
+ }
+
+ // Check for duplicate URLs.
+ if (!bookmark.mIsFolder && urlSet.contains(bookmark.mUrl)) {
+ Log.i(TAG, "More than one bookmark pointing to "
+ + bookmark.mUrl
+ + ". Keeping only the first one for consistency with Chromium.");
+ continue;
+ }
+
+ idMap.put(bookmark.mId, bookmark);
+ urlSet.add(bookmark.mUrl);
+ }
+ bookmarkIterator.close();
+
+ // Recreate the folder hierarchy and read it.
+ recreateFolderHierarchy(idMap);
+ if (rootBookmarksFolder.mEntries.size() == 0) {
+ Log.e(TAG, "ATTENTION: not using partner bookmarks as none were provided");
+ return null;
+ }
+ if (rootBookmarksFolder.mEntries.size() != 1) {
+ Log.e(TAG, "ATTENTION: more than one top-level partner bookmarks, ignored");
+ return null;
+ }
+
+ readBookmarkHierarchy(
+ rootBookmarksFolder,
+ new HashSet<PartnerBookmarksReader.Bookmark>());
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void v) {
+ synchronized (mRootSync) {
+ onBookmarksRead();
+ }
+ }
+
+ private void recreateFolderHierarchy(LinkedHashMap<Long, Bookmark> idMap) {
+ for (Bookmark bookmark : idMap.values()) {
+ if (bookmark.mId == ROOT_FOLDER_ID) continue;
+
+ // Look for invalid parent ids and self-cycles.
+ if (!idMap.containsKey(bookmark.mParentId) || bookmark.mParentId == bookmark.mId) {
+ bookmark.mParent = idMap.get(ROOT_FOLDER_ID);
+ bookmark.mParent.mEntries.add(bookmark);
+ continue;
+ }
+
+ bookmark.mParent = idMap.get(bookmark.mParentId);
+ bookmark.mParent.mEntries.add(bookmark);
+ }
+ }
+
+ private Bookmark createRootBookmarksFolderBookmark() {
+ Bookmark root = new Bookmark();
+ root.mId = ROOT_FOLDER_ID;
+ root.mTitle = "[IMPLIED_ROOT]";
+ root.mNativeId = INVALID_BOOKMARK_ID;
+ root.mParentId = ROOT_FOLDER_ID;
+ root.mIsFolder = true;
+ return root;
+ }
+
+ private void readBookmarkHierarchy(
+ Bookmark bookmark, HashSet<Bookmark> processedNodes) {
+ // Avoid cycles in the hierarchy that could lead to infinite loops.
+ if (processedNodes.contains(bookmark)) return;
+ processedNodes.add(bookmark);
+
+ if (bookmark.mId != ROOT_FOLDER_ID) {
+ try {
+ synchronized (mRootSync) {
+ bookmark.mNativeId =
+ onBookmarkPush(
+ bookmark.mUrl, bookmark.mTitle,
+ bookmark.mIsFolder, bookmark.mParentId,
+ bookmark.mFavicon, bookmark.mTouchicon);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Error inserting bookmark " + bookmark.mTitle, e);
+ }
+ if (bookmark.mNativeId == INVALID_BOOKMARK_ID) {
+ Log.e(TAG, "Error creating bookmark '" + bookmark.mTitle + "'.");
+ return;
+ }
+ }
+
+ if (bookmark.mIsFolder) {
+ for (Bookmark entry : bookmark.mEntries) {
+ if (entry.mParent != bookmark) {
+ Log.w(TAG, "Hierarchy error in bookmark '"
+ + bookmark.mTitle + "'. Skipping.");
+ continue;
+ }
+ entry.mParentId = bookmark.mNativeId;
+ readBookmarkHierarchy(entry, processedNodes);
+ }
+ }
+ }
+ }
+
+ /**
+ * Disables partner bookmarks editing.
+ */
+ public static void disablePartnerBookmarksEditing() {
+ sForceDisableEditing = true;
+ if (sInitialized) nativeDisablePartnerBookmarksEditing();
+ }
+
+ private static void initializeAndDisableEditingIfNecessary() {
+ sInitialized = true;
+ if (sForceDisableEditing) disablePartnerBookmarksEditing();
+ }
+
+ // JNI
+ private native long nativeInit();
+ private native void nativeReset(long nativePartnerBookmarksReader);
+ private native void nativeDestroy(long nativePartnerBookmarksReader);
+ private native long nativeAddPartnerBookmark(long nativePartnerBookmarksReader,
+ String url, String title, boolean isFolder, long parentId,
+ byte[] favicon, byte[] touchicon);
+ private native void nativePartnerBookmarksCreationComplete(long nativePartnerBookmarksReader);
+ private static native void nativeDisablePartnerBookmarksEditing();
+}

Powered by Google App Engine
This is Rietveld 408576698