Skip to content

Instantly share code, notes, and snippets.

@uazo
Last active February 20, 2021 11:37
Show Gist options
  • Save uazo/00ebe2196e2587a5b16fd4584226939f to your computer and use it in GitHub Desktop.
Save uazo/00ebe2196e2587a5b16fd4584226939f to your computer and use it in GitHub Desktop.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
index 7772b54b5e..52f506bc02 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
@@ -4,10 +4,13 @@
package org.chromium.chrome.browser.bookmarks;
+import org.chromium.base.Log;
+
import android.content.Intent;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Build;
import android.os.SystemClock;
import android.provider.Browser;
import android.Manifest.permission;
@@ -32,6 +35,8 @@ import org.chromium.components.url_formatter.SchemeDisplay;
import org.chromium.components.url_formatter.UrlFormatter;
import org.chromium.content_public.browser.WebContents;
+import org.chromium.base.ContentUriUtils;
+import org.chromium.chrome.R;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.ui.base.PageTransition;
@@ -611,20 +616,44 @@ public class BookmarkBridge {
public void exportBookmarks(WindowAndroid window) {
assert mIsNativeBookmarkModelLoaded;
// check if we have the correct write permission
- if (window.hasPermission(permission.WRITE_EXTERNAL_STORAGE)) {
- exportBookmarksImpl();
+ if (window.hasPermission(permission.WRITE_EXTERNAL_STORAGE) ||
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ exportBookmarksImpl(window);
} else {
String[] requestPermissions = new String[] {permission.WRITE_EXTERNAL_STORAGE};
window.requestPermissions(requestPermissions, (permissions, grantResults) -> {
if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- exportBookmarksImpl();
+ exportBookmarksImpl(window);
}
});
};
}
- private void exportBookmarksImpl() {
- BookmarkBridgeJni.get().exportBookmarks(mNativeBookmarkBridge, BookmarkBridge.this);
+ private void exportBookmarksImpl(WindowAndroid window) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ Intent fileSelector = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ fileSelector.addCategory(Intent.CATEGORY_OPENABLE);
+ fileSelector.setType("*/*");
+ fileSelector.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ // see https://issuetracker.google.com/issues/37136466
+ // intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, "content://com.android.externalstorage.documents/document/primary:.myfolder");
+ fileSelector.putExtra(Intent.EXTRA_TITLE, "bookmarks.html");
+ window.showIntent(fileSelector,
+ new WindowAndroid.IntentCallback() {
+ @Override
+ public void onIntentCompleted(WindowAndroid window, int resultCode, Intent data) {
+ if (data == null) return;
+ Uri filePath = data.getData();
+ BookmarkBridgeJni.get().exportBookmarks(mNativeBookmarkBridge, BookmarkBridge.this,
+ window, filePath.toString());
+ }
+ },
+ null);
+ } else {
+ BookmarkBridgeJni.get().exportBookmarks(mNativeBookmarkBridge,
+ BookmarkBridge.this, window, "");
+ }
}
/**
@@ -1049,21 +1078,31 @@ public class BookmarkBridge {
}
@CalledByNative
- public void bookmarksExported(String bookmarksPath) {
- Context context = ContextUtils.getApplicationContext();
-
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("file://" + bookmarksPath));
- intent.putExtra(Browser.EXTRA_APPLICATION_ID,
- context.getPackageName());
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, PageTransition.AUTO_BOOKMARK);
+ public void bookmarksExported(WindowAndroid window, String bookmarksPath, boolean success) {
+ Uri uri = Uri.parse(bookmarksPath);
+ Log.i("bookmarksExported", "uri=" + uri.toString() + " " +
+ "path=" + uri.getPath());
- // If the bookmark manager is shown in a tab on a phone (rather than in a separate
- // activity) the component name may be null. Send the intent through
- // ChromeLauncherActivity instead to avoid crashing. See crbug.com/615012.
- intent.setClass(context, ChromeLauncherActivity.class);
-
- IntentHandler.startActivityForTrustedIntent(intent);
+ if (success == false) {
+ window.showError(R.string.saving_file_error);
+ } else {
+ Context context = ContextUtils.getApplicationContext();
+
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ ContentUriUtils.isContentUri(bookmarksPath) ?
+ Uri.parse(bookmarksPath) : Uri.parse("file://" + bookmarksPath));
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID,
+ context.getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, PageTransition.AUTO_BOOKMARK);
+
+ // If the bookmark manager is shown in a tab on a phone (rather than in a separate
+ // activity) the component name may be null. Send the intent through
+ // ChromeLauncherActivity instead to avoid crashing. See crbug.com/615012.
+ intent.setClass(context, ChromeLauncherActivity.class);
+
+ IntentHandler.startActivityForTrustedIntent(intent);
+ }
}
private static List<Pair<Integer, Integer>> createPairsList(int[] left, int[] right) {
@@ -1134,7 +1173,8 @@ public class BookmarkBridge {
void getChildIDs(long nativeBookmarkBridge, BookmarkBridge caller, long id, int type,
List<BookmarkId> bookmarksList);
void importBookmarks(long nativeBookmarkBridge, BookmarkBridge caller, WindowAndroid window);
- void exportBookmarks(long nativeBookmarkBridge, BookmarkBridge caller);
+ void exportBookmarks(long nativeBookmarkBridge, BookmarkBridge caller, WindowAndroid window,
+ String export_path);
BookmarkId getChildAt(
long nativeBookmarkBridge, BookmarkBridge caller, long id, int type, int index);
int getTotalBookmarkCount(
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.cc b/chrome/browser/android/bookmarks/bookmark_bridge.cc
index 6c90e7784c..151f77f28a 100644
--- a/chrome/browser/android/bookmarks/bookmark_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.cc
@@ -135,6 +135,40 @@ bool CanImportURL(const GURL& url) {
namespace {
+class FileBookmarksExportObserver: public BookmarksExportObserver {
+ public:
+ FileBookmarksExportObserver(
+ const JavaParamRef<jobject>& obj,
+ ui::WindowAndroid* window,
+ const std::string& export_path) :
+ obj_(ScopedJavaGlobalRef<jobject>(obj)),
+ window_(window),
+ export_path_(export_path) {}
+
+ void OnExportFinished(Result result) override {
+ if (result == Result::kSuccess) {
+ LOG(INFO) << "Bookmarks exported successfully to " << export_path_;
+ } else if (result == Result::kCouldNotCreateFile) {
+ LOG(ERROR) << "Bookmarks export: could not create file " << export_path_;
+ } else if (result == Result::kCouldNotWriteHeader) {
+ LOG(ERROR) << "Bookmarks export: could not write header";
+ } else if (result == Result::kCouldNotWriteNodes) {
+ LOG(ERROR) << "Bookmarks export: could not write nodes";
+ }
+
+ JNIEnv* env = AttachCurrentThread();
+ Java_BookmarkBridge_bookmarksExported(env, obj_, window_->GetJavaObject(),
+ ConvertUTF8ToJavaString(env, export_path_),
+ result == Result::kSuccess);
+ delete this;
+ }
+
+ private:
+ const ScopedJavaGlobalRef<jobject> obj_;
+ ui::WindowAndroid* window_;
+ const std::string export_path_;
+};
+
const int kInvalidId = -1;
class BookmarkTitleComparer {
@@ -649,10 +683,20 @@ void BookmarkBridge::ImportBookmarks(JNIEnv* env,
}
void BookmarkBridge::ExportBookmarks(JNIEnv* env,
- const JavaParamRef<jobject>& obj) {
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jobject>& java_window,
+ const JavaParamRef<jstring>& j_export_path) {
DCHECK(IsLoaded());
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ ui::WindowAndroid* window =
+ ui::WindowAndroid::FromJavaWindowAndroid(java_window);
+ CHECK(window);
+
+ base::string16 export_path =
+ base::android::ConvertJavaStringToUTF16(env, j_export_path);
+
+ export_path_ = base::FilePath::FromUTF16Unsafe(export_path);
if (export_path_.empty()) {
if (!base::android::GetDownloadsDirectory(&export_path_)) {
LOG(ERROR) << "Could not retrieve downloads directory for bookmarks export";
@@ -661,12 +705,8 @@ void BookmarkBridge::ExportBookmarks(JNIEnv* env,
export_path_ = export_path_.Append(FILE_PATH_LITERAL("bookmarks.html"));
}
- bookmark_html_writer::WriteBookmarks(profile_, export_path_, NULL);
-
- Java_BookmarkBridge_bookmarksExported(env, obj, ConvertUTF8ToJavaString(env, export_path_.MaybeAsASCII()));
-
- //NOTE: nothing will be written if write permission has not been granted before
- LOG(INFO) << "Bookmarks exported successfully to " << export_path_;
+ observer_ = new FileBookmarksExportObserver(obj, window, export_path_.MaybeAsASCII());
+ bookmark_html_writer::WriteBookmarks(profile_, export_path_, observer_);
}
// Attempts to create a TemplateURL from the provided data. |title| is optional.
@@ -689,10 +729,10 @@ void BookmarkBridge::FileSelected(const base::FilePath& path, int index,
void* params) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
- base::BindOnce(&BookmarkBridge::FileSelectedImpl,
+ base::BindOnce(&BookmarkBridge::FileSelectedImpl,
base::Unretained(this),
path),
- base::BindOnce(&BookmarkBridge::FileSelectedImplOnUIThread,
+ base::BindOnce(&BookmarkBridge::FileSelectedImplOnUIThread,
base::Unretained(this),
path));
}
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.h b/chrome/browser/android/bookmarks/bookmark_bridge.h
index b87fd636e4..706783671c 100644
--- a/chrome/browser/android/bookmarks/bookmark_bridge.h
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.h
@@ -25,6 +25,7 @@
#include "components/prefs/pref_change_registrar.h"
#include "components/search_engines/template_url.h"
#include "ui/shell_dialogs/select_file_dialog.h"
+#include "chrome/browser/bookmarks/bookmark_html_writer.h"
namespace bookmarks {
class BookmarkModel;
@@ -155,7 +156,9 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver,
const base::android::JavaParamRef<jobject>& java_window);
void ExportBookmarks(JNIEnv* env,
- const base::android::JavaParamRef<jobject>& obj);
+ const base::android::JavaParamRef<jobject>& obj,
+ const base::android::JavaParamRef<jobject>& java_window,
+ const base::android::JavaParamRef<jstring>& j_export_path);
void SetBookmarkTitle(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
@@ -328,6 +331,7 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver,
Profile* profile_;
base::FilePath export_path_;
+ BookmarksExportObserver* observer_; // weak
JavaObjectWeakGlobalRef weak_java_ref_;
bookmarks::BookmarkModel* bookmark_model_; // weak
bookmarks::ManagedBookmarkService* managed_bookmark_service_; // weak
diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc
index df6456e7dc..8d9e89016d 100644
--- a/chrome/browser/bookmarks/bookmark_html_writer.cc
+++ b/chrome/browser/bookmarks/bookmark_html_writer.cc
@@ -40,6 +40,8 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/favicon_size.h"
+#include "components/download/internal/common/android/download_collection_bridge.h"
+
using bookmarks::BookmarkCodec;
using bookmarks::BookmarkNode;
using content::BrowserThread;
@@ -239,8 +241,14 @@ class Writer : public base::RefCountedThreadSafe<Writer> {
// Opens the file, returning true on success.
bool OpenFile() {
- int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
- file_.reset(new base::File(path_, flags));
+ int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
+ base::File::FLAG_OPEN_TRUNCATED;
+ if (path_.IsContentUri()) {
+ file_ = std::make_unique<base::File>(
+ download::DownloadCollectionBridge::OpenIntermediateUri(path_));
+ } else {
+ file_.reset(new base::File(path_, flags));
+ }
if (!file_->IsValid()) {
PLOG(ERROR) << "Could not create " << path_;
return false;
diff --git a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
index 680a72377e..3fa1a137a5 100644
--- a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
+++ b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
@@ -225,10 +225,12 @@ public class DownloadCollectionBridge {
ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
ParcelFileDescriptor pfd =
resolver.openFileDescriptor(Uri.parse(intermediateUri), "rw");
+ try {
ContentValues updateValues = new ContentValues();
updateValues.put("date_expires", getNewExpirationTime());
ContextUtils.getApplicationContext().getContentResolver().update(
Uri.parse(intermediateUri), updateValues, null, null);
+ } catch(Exception dontcare) {} // don't care
return pfd.detachFd();
} catch (Exception e) {
Log.e(TAG, "Cannot open intermediate Uri.", e);
diff --git a/components/user_scripts/browser/user_script_loader.cc b/components/user_scripts/browser/user_script_loader.cc
index 72fb8f15ce..4975cc0ece 100755
--- a/components/user_scripts/browser/user_script_loader.cc
+++ b/components/user_scripts/browser/user_script_loader.cc
@@ -67,7 +67,7 @@ using blink::mojom::NativeFileSystemStatus;
namespace {
-bool invalidChar(char c)
+bool invalidChar(unsigned char c)
{
return !(c>=0 && c <128);
}
diff --git a/ui/android/java/strings/android_ui_strings.grd b/ui/android/java/strings/android_ui_strings.grd
index 9122b7a029..2211ffcbb6 100644
--- a/ui/android/java/strings/android_ui_strings.grd
+++ b/ui/android/java/strings/android_ui_strings.grd
@@ -174,6 +174,9 @@
<message name="IDS_OPENING_FILE_ERROR" desc="Toast when the browser is unable to open a file for upload. [CHAR-LIMIT=32]">
Failed to open selected file
</message>
+ <message name="IDS_SAVING_FILE_ERROR" desc="Toast when the browser is unable to save a file. [CHAR-LIMIT=32]">
+ Failed to save selected file
+ </message>
<!-- Clipboard -->
<message name="IDS_COPY_TO_CLIPBOARD_FAILURE_MESSAGE" desc="Notification for when copying to the clipboard fails. [CHAR-LIMIT=64]">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment