Update to Chromium //base at Chromium commit e3a753f17bac62738b0dbf0b36510f767b081e4b. This gets us to sometime during September 9, 2015. TBR=jamesr@chromium.org Review URL: https://codereview.chromium.org/2050803003 .
diff --git a/BUILD.gn b/BUILD.gn index bfc256f..86db541 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -186,6 +186,8 @@ "deferred_sequenced_task_runner.h", "environment.cc", "environment.h", + "feature_list.cc", + "feature_list.h", "file_descriptor_posix.h", "file_version_info.h", "file_version_info_mac.h", @@ -1588,6 +1590,14 @@ ] DEPRECATED_java_in_dir = "android/java/src" + if (is_debug) { + java_files = [ + "android/java/debug_src/org/chromium/base/IncrementalInstall.java", + "android/java/debug_src/org/chromium/base/Reflect.java", + ] + } else { + java_files = [ "android/java/release_src/org/chromium/base/IncrementalInstall.java" ] + } # A new version of NativeLibraries.java (with the actual correct values) # will be created when creating an apk.
diff --git a/OWNERS b/OWNERS index bcaa81b..932fa89 100644 --- a/OWNERS +++ b/OWNERS
@@ -26,3 +26,7 @@ per-file *android*=nyquist@chromium.org per-file *android*=rmcilroy@chromium.org per-file *android*=yfriedman@chromium.org + +# For FeatureList API: +per-file feature_list*=asvitkine@chromium.org +per-file feature_list*=isherman@chromium.org
diff --git a/allocator/BUILD.gn b/allocator/BUILD.gn index 5a1e9df..b6b7fc2 100644 --- a/allocator/BUILD.gn +++ b/allocator/BUILD.gn
@@ -61,29 +61,31 @@ libs = [ rebase_path("$target_gen_dir/allocator/libcmt.lib") ] } - action("prep_libc") { - script = "prep_libc.py" - outputs = [ - "$target_gen_dir/allocator/libcmt.lib", - ] - args = [ - visual_studio_path + "/vc/lib", - rebase_path("$target_gen_dir/allocator"), - current_cpu, - ] - } + if (!is_component_build) { + action("prep_libc") { + script = "prep_libc.py" + outputs = [ + "$target_gen_dir/allocator/libcmt.lib", + ] + args = [ + visual_studio_path + "/vc/lib", + rebase_path("$target_gen_dir/allocator"), + current_cpu, + ] + } - source_set("allocator_shim") { - sources = [ - "allocator_shim_win.cc", - ] - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] + source_set("allocator_shim") { + sources = [ + "allocator_shim_win.cc", + ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] - public_configs = [ ":nocmt" ] - deps = [ - ":prep_libc", - ] + public_configs = [ ":nocmt" ] + deps = [ + ":prep_libc", + ] + } } }
diff --git a/android/animation_frame_time_histogram.cc b/android/animation_frame_time_histogram.cc index 0d79619..2cf7516 100644 --- a/android/animation_frame_time_histogram.cc +++ b/android/animation_frame_time_histogram.cc
@@ -10,9 +10,9 @@ // static void SaveHistogram(JNIEnv* env, - jobject jcaller, - jstring j_histogram_name, - jlongArray j_frame_times_ms, + const JavaParamRef<jobject>& jcaller, + const JavaParamRef<jstring>& j_histogram_name, + const JavaParamRef<jlongArray>& j_frame_times_ms, jint j_count) { jlong *frame_times_ms = env->GetLongArrayElements(j_frame_times_ms, NULL); std::string histogram_name = base::android::ConvertJavaStringToUTF8(
diff --git a/android/application_status_listener.cc b/android/application_status_listener.cc index 3e6fbf8..5aee781 100644 --- a/android/application_status_listener.cc +++ b/android/application_status_listener.cc
@@ -66,7 +66,7 @@ } static void OnApplicationStateChange(JNIEnv* env, - jclass clazz, + const JavaParamRef<jclass>& clazz, jint new_state) { ApplicationState application_state = static_cast<ApplicationState>(new_state); ApplicationStatusListener::NotifyApplicationStateChange(application_state);
diff --git a/android/command_line_android.cc b/android/command_line_android.cc index 3a2b1f2..e196aed 100644 --- a/android/command_line_android.cc +++ b/android/command_line_android.cc
@@ -31,18 +31,21 @@ } // namespace -static void Reset(JNIEnv* env, jclass clazz) { +static void Reset(JNIEnv* env, const JavaParamRef<jclass>& clazz) { CommandLine::Reset(); } -static jboolean HasSwitch(JNIEnv* env, jclass clazz, jstring jswitch) { +static jboolean HasSwitch(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jswitch) { std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); return CommandLine::ForCurrentProcess()->HasSwitch(switch_string); } -static ScopedJavaLocalRef<jstring> GetSwitchValue(JNIEnv* env, - jclass clazz, - jstring jswitch) { +static ScopedJavaLocalRef<jstring> GetSwitchValue( + JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jswitch) { std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueNative( switch_string)); @@ -51,21 +54,27 @@ return ConvertUTF8ToJavaString(env, value); } -static void AppendSwitch(JNIEnv* env, jclass clazz, jstring jswitch) { +static void AppendSwitch(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jswitch) { std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); CommandLine::ForCurrentProcess()->AppendSwitch(switch_string); } -static void AppendSwitchWithValue(JNIEnv* env, jclass clazz, - jstring jswitch, jstring jvalue) { +static void AppendSwitchWithValue(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jswitch, + const JavaParamRef<jstring>& jvalue) { std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); std::string value_string (ConvertJavaStringToUTF8(env, jvalue)); CommandLine::ForCurrentProcess()->AppendSwitchASCII(switch_string, value_string); } -static void AppendSwitchesAndArguments(JNIEnv* env, jclass clazz, - jobjectArray array) { +static void AppendSwitchesAndArguments( + JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jobjectArray>& array) { AppendJavaStringArrayToCommandLine(env, array, false); }
diff --git a/android/cpu_features.cc b/android/cpu_features.cc index 6a18695..c139b78 100644 --- a/android/cpu_features.cc +++ b/android/cpu_features.cc
@@ -10,11 +10,11 @@ namespace base { namespace android { -jint GetCoreCount(JNIEnv*, jclass) { +jint GetCoreCount(JNIEnv*, const JavaParamRef<jclass>&) { return android_getCpuCount(); } -jlong GetCpuFeatures(JNIEnv*, jclass) { +jlong GetCpuFeatures(JNIEnv*, const JavaParamRef<jclass>&) { return android_getCpuFeatures(); }
diff --git a/android/field_trial_list.cc b/android/field_trial_list.cc index 3ff3811..9731a48 100644 --- a/android/field_trial_list.cc +++ b/android/field_trial_list.cc
@@ -13,15 +13,18 @@ using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF8ToJavaString; -static ScopedJavaLocalRef<jstring> FindFullName(JNIEnv* env, - jclass clazz, - jstring jtrial_name) { +static ScopedJavaLocalRef<jstring> FindFullName( + JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jtrial_name) { std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name)); return ConvertUTF8ToJavaString( env, base::FieldTrialList::FindFullName(trial_name)); } -static jboolean TrialExists(JNIEnv* env, jclass clazz, jstring jtrial_name) { +static jboolean TrialExists(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jtrial_name) { std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name)); return base::FieldTrialList::TrialExists(trial_name); }
diff --git a/android/important_file_writer_android.cc b/android/important_file_writer_android.cc index bcbd785..f324738 100644 --- a/android/important_file_writer_android.cc +++ b/android/important_file_writer_android.cc
@@ -15,9 +15,9 @@ namespace android { static jboolean WriteFileAtomically(JNIEnv* env, - jclass /* clazz */, - jstring file_name, - jbyteArray data) { + const JavaParamRef<jclass>& /* clazz */, + const JavaParamRef<jstring>& file_name, + const JavaParamRef<jbyteArray>& data) { // This is called on the UI thread during shutdown to save tab data, so // needs to enable IO. base::ThreadRestrictions::ScopedAllowIO allow_io;
diff --git a/android/java/debug_src/org/chromium/base/IncrementalInstall.java b/android/java/debug_src/org/chromium/base/IncrementalInstall.java new file mode 100644 index 0000000..71b2b87 --- /dev/null +++ b/android/java/debug_src/org/chromium/base/IncrementalInstall.java
@@ -0,0 +1,44 @@ +// 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.base; + +import android.content.Context; + +import java.io.File; +import java.util.List; + +/** + * Contains hooks for making Incremental Install work. Incremental Install is + * where native libraries and (TODO) .dex files are pushed out-of-band to + * external storage rather than packaged with the app. + */ +public final class IncrementalInstall { + private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incremental-app-"; + + public static void initialize(Context context) { + File incrementalAppDir = new File(MANAGED_DIR_PREFIX + context.getPackageName()); + File incrementalLibDir = new File(incrementalAppDir, "lib"); + injectNativeLibDir(context.getClassLoader(), incrementalLibDir); + } + + @SuppressWarnings("unchecked") + private static void injectNativeLibDir(ClassLoader loader, File nativeLibDir) { + try { + Object dexPathList = Reflect.getField(loader, "pathList"); + Object currentDirs = Reflect.getField(dexPathList, "nativeLibraryDirectories"); + if (currentDirs instanceof List) { + List<File> dirsAsList = (List<File>) currentDirs; + dirsAsList.add(nativeLibDir); + } else { + File[] dirsAsArray = (File[]) currentDirs; + File[] newDirs = new File[] {nativeLibDir}; + Reflect.setField(dexPathList, "nativeLibraryDirectories", + Reflect.concatArrays(dirsAsArray, newDirs)); + } + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } +}
diff --git a/android/java/debug_src/org/chromium/base/Reflect.java b/android/java/debug_src/org/chromium/base/Reflect.java new file mode 100644 index 0000000..b705771 --- /dev/null +++ b/android/java/debug_src/org/chromium/base/Reflect.java
@@ -0,0 +1,73 @@ +// 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.base; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; + +/** + * Reflection helper methods. + */ +final class Reflect { + /** + * Sets the value of an object's field (even if it's not visible). + * + * @param instance The object containing the field to set. + * @param name The name of the field to set. + * @param value The new value for the field. + */ + static void setField(Object instance, String name, Object value) throws NoSuchFieldException { + Field field = findField(instance, name); + try { + field.setAccessible(true); + field.set(instance, value); + } catch (IllegalAccessException e) { + // This shouldn't happen. + } + } + + /** + * Retrieves the value of an object's field (even if it's not visible). + * + * @param instance The object containing the field to set. + * @param name The name of the field to set. + * @return The field's value. Primitive values are returned as their boxed type. + */ + static Object getField(Object instance, String name) throws NoSuchFieldException { + Field field = findField(instance, name); + try { + field.setAccessible(true); + return field.get(instance); + } catch (IllegalAccessException e) { + // This shouldn't happen. + } + return null; + } + + /** + * Concatenates two arrays into a new array. The arrays must be of the same type. + */ + static Object[] concatArrays(Object[] left, Object[] right) { + Object[] result = (Object[]) (Array.newInstance( + left.getClass().getComponentType(), left.length + right.length)); + System.arraycopy(left, 0, result, 0, left.length); + System.arraycopy(right, 0, result, left.length, right.length); + return result; + } + + /** + * Finds the Field with the given name for the given object, traversing superclasses if + * necessary. + */ + private static Field findField(Object instance, String name) throws NoSuchFieldException { + for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { + try { + return clazz.getDeclaredField(name); + } catch (NoSuchFieldException e) { + } + } + throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass()); + } +}
diff --git a/android/java/release_src/org/chromium/base/IncrementalInstall.java b/android/java/release_src/org/chromium/base/IncrementalInstall.java new file mode 100644 index 0000000..09dfd41 --- /dev/null +++ b/android/java/release_src/org/chromium/base/IncrementalInstall.java
@@ -0,0 +1,14 @@ +// 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.base; + +import android.content.Context; + +/** + * Contains empty stubs for IncrementalInstall. + */ +public final class IncrementalInstall { + public static void initialize(Context context) {} +}
diff --git a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/android/java/src/org/chromium/base/ApiCompatibilityUtils.java index c2d596e..a119acb 100644 --- a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java +++ b/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -12,6 +12,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -429,4 +430,28 @@ } return drawable; } + + /** + * @see android.content.res.Resources#getColor(int id). + */ + @SuppressWarnings("deprecation") + public static int getColor(Resources res, int id) throws NotFoundException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return res.getColor(id, null); + } else { + return res.getColor(id); + } + } + + /** + * @see android.content.res.Resources#getColorStateList(int id). + */ + @SuppressWarnings("deprecation") + public static ColorStateList getColorStateList(Resources res, int id) throws NotFoundException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return res.getColorStateList(id, null); + } else { + return res.getColorStateList(id); + } + } }
diff --git a/android/java/src/org/chromium/base/BaseChromiumApplication.java b/android/java/src/org/chromium/base/BaseChromiumApplication.java index b71b646..ffa3c22 100644 --- a/android/java/src/org/chromium/base/BaseChromiumApplication.java +++ b/android/java/src/org/chromium/base/BaseChromiumApplication.java
@@ -19,6 +19,7 @@ @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); + IncrementalInstall.initialize(this); } /**
diff --git a/android/java/src/org/chromium/base/Log.java b/android/java/src/org/chromium/base/Log.java index 3b46cdc..2a77211 100644 --- a/android/java/src/org/chromium/base/Log.java +++ b/android/java/src/org/chromium/base/Log.java
@@ -17,7 +17,7 @@ * the origin of logs, and enable or disable logging in different parts of the code. * </p> * <p> - * @see usage documentation: <a href="README_logging.md">README_logging.md</a>. + * Usage documentation: {@code //docs/logging.md}. * </p> */ public class Log { @@ -60,7 +60,12 @@ return "[" + getCallOrigin() + "] " + formatLog(messageTemplate, params); } - /** Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}. */ + /** + * Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}. + * + * Note: Has no effect on whether logs are sent or not. Use a method with + * {@link RemovableInRelease} to log something in Debug builds only. + */ public static boolean isLoggable(String tag, int level) { return android.util.Log.isLoggable(tag, level); } @@ -79,14 +84,12 @@ * one is a {@link Throwable}, its trace will be printed. */ private static void verbose(String tag, String messageTemplate, Object... args) { - if (Log.isLoggable(tag, Log.VERBOSE)) { - String message = formatLogWithStack(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.v(tag, message, tr); - } else { - android.util.Log.v(tag, message); - } + String message = formatLogWithStack(messageTemplate, args); + Throwable tr = getThrowableToLog(args); + if (tr != null) { + android.util.Log.v(tag, message, tr); + } else { + android.util.Log.v(tag, message); } } @@ -165,14 +168,12 @@ * one is a {@link Throwable}, its trace will be printed. */ private static void debug(String tag, String messageTemplate, Object... args) { - if (isLoggable(tag, Log.DEBUG)) { - String message = formatLogWithStack(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.d(tag, message, tr); - } else { - android.util.Log.d(tag, message); - } + String message = formatLogWithStack(messageTemplate, args); + Throwable tr = getThrowableToLog(args); + if (tr != null) { + android.util.Log.d(tag, message, tr); + } else { + android.util.Log.d(tag, message); } } @@ -246,14 +247,12 @@ */ @VisibleForTesting public static void i(String tag, String messageTemplate, Object... args) { - if (Log.isLoggable(tag, Log.INFO)) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.i(tag, message, tr); - } else { - android.util.Log.i(tag, message); - } + String message = formatLog(messageTemplate, args); + Throwable tr = getThrowableToLog(args); + if (tr != null) { + android.util.Log.i(tag, message, tr); + } else { + android.util.Log.i(tag, message); } } @@ -268,14 +267,12 @@ */ @VisibleForTesting public static void w(String tag, String messageTemplate, Object... args) { - if (Log.isLoggable(tag, Log.WARN)) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.w(tag, message, tr); - } else { - android.util.Log.w(tag, message); - } + String message = formatLog(messageTemplate, args); + Throwable tr = getThrowableToLog(args); + if (tr != null) { + android.util.Log.w(tag, message, tr); + } else { + android.util.Log.w(tag, message); } } @@ -290,14 +287,12 @@ */ @VisibleForTesting public static void e(String tag, String messageTemplate, Object... args) { - if (Log.isLoggable(tag, Log.ERROR)) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.e(tag, message, tr); - } else { - android.util.Log.e(tag, message); - } + String message = formatLog(messageTemplate, args); + Throwable tr = getThrowableToLog(args); + if (tr != null) { + android.util.Log.e(tag, message, tr); + } else { + android.util.Log.e(tag, message); } } @@ -316,14 +311,12 @@ */ @VisibleForTesting public static void wtf(String tag, String messageTemplate, Object... args) { - if (Log.isLoggable(tag, Log.ASSERT)) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.wtf(tag, message, tr); - } else { - android.util.Log.wtf(tag, message); - } + String message = formatLog(messageTemplate, args); + Throwable tr = getThrowableToLog(args); + if (tr != null) { + android.util.Log.wtf(tag, message, tr); + } else { + android.util.Log.wtf(tag, message); } }
diff --git a/android/java/src/org/chromium/base/PathUtils.java b/android/java/src/org/chromium/base/PathUtils.java index 58b54ff..6f29732 100644 --- a/android/java/src/org/chromium/base/PathUtils.java +++ b/android/java/src/org/chromium/base/PathUtils.java
@@ -23,8 +23,7 @@ private static final int DATA_DIRECTORY = 0; private static final int DATABASE_DIRECTORY = 1; private static final int CACHE_DIRECTORY = 2; - private static final int DOWNLOADS_DIRECTORY = 3; - private static final int NUM_DIRECTORIES = 4; + private static final int NUM_DIRECTORIES = 3; private static AsyncTask<String, Void, String[]> sDirPathFetchTask; private static File sThumbnailDirectory; @@ -50,12 +49,7 @@ paths[DATABASE_DIRECTORY] = appContext.getDatabasePath("foo").getParent(); // TODO(wnwen): Find a way to avoid calling this function in renderer process. if (appContext.getCacheDir() != null) { - // These paths are only available in the browser process. paths[CACHE_DIRECTORY] = appContext.getCacheDir().getPath(); - paths[DOWNLOADS_DIRECTORY] = Environment - .getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOWNLOADS) - .getPath(); } return paths; } @@ -121,8 +115,8 @@ @SuppressWarnings("unused") @CalledByNative private static String getDownloadsDirectory(Context appContext) { - assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; - return getDirectoryPath(DOWNLOADS_DIRECTORY); + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + .getPath(); } /**
diff --git a/android/java/src/org/chromium/base/README_logging.md b/android/java/src/org/chromium/base/README_logging.md deleted file mode 100644 index 6104bf0..0000000 --- a/android/java/src/org/chromium/base/README_logging.md +++ /dev/null
@@ -1,181 +0,0 @@ -## Logging ## - -Logging used to be done using Android's [android.util.Log] -(http://developer.android.com/reference/android/util/Log.html). - -A wrapper on that is now available: org.chromium.base.Log. It is designed to write logs as -belonging to logical groups going beyond single classes, and to make it easy to switch logging on -or off for individual groups. - -Usage: - - private static final String TAG = "cr.YourModuleTag"; - ... - Log.i(TAG, "Logged INFO message."); - Log.d(TAG, "Some DEBUG info: %s", data); - -Output: - - I/cr.YourModuleTag: ( 999): Logged INFO message - D/cr.YourModuleTag: ( 999): [MyClass.java:42] Some DEBUG info: data's toString output - -Here, **TAG** will be a feature or package name, "MediaRemote" or "NFC" for example. In most -cases, the class name is not needed. - -**Caveat:** Property keys are limited to 23 characters. If the tag is too long, `Log#isLoggable` -throws a RuntimeException. - -### Verbose and Debug logs have special handling ### - -* `Log.v` and `Log.d` Calls made using `org.chromium.base.Log` are stripped - out of production binaries using Proguard. There is no way to get those logs - in release builds. - -* The file name and line number will be prepended to the log message. - For higher priority logs, those are not added for performance concerns. - -* By default, Verbose and Debug logs are not enabled, see guarding: - -### Log calls are guarded: Tag groups can be enabled or disabled using ADB ### - - adb shell setprop log.tag.cr.YourModuleTag <LEVEL> - -Level here is either `VERBOSE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `ASSERT`, or `SUPPRESS` -By default, the level for all tags is `INFO`. - -### An exception trace is printed when the exception is the last parameter ### - -As with `java.util.Log`, putting a throwable as last parameter will dump the corresponding stack -trace: - - Log.i(TAG, "An error happened: %s", e) - - I/cr.YourModuleTag: ( 999): An error happened: This is the exception's message - I/cr.YourModuleTag: ( 999): java.lang.Exception: This is the exception's message - I/cr.YourModuleTag: ( 999): at foo.bar.MyClass.test(MyClass.java:42) - I/cr.YourModuleTag: ( 999): ... - -Having the exception as last parameter doesn't prevent it from being used for string formatting. - -### Logging Best Practices - -#### Rule #1: Never log PII (Personal Identification Information): - -This is a huge concern, because other applications can access the log and extract a lot of data -from your own by doing so. Even if JellyBean restricted this, people are going to run your -application on rooted devices and allow some apps to access it. Also anyone with USB access to the -device can use ADB to get the full logcat and get the same data right now. - -If you really need to print something , print a series of Xs instead (e.g. "XXXXXX"), or print a -truncated hash of the PII instead. Truncation is required to make it harder for an attacker to -recover the full data through rainbow tables and similar methods. - -Similarly, avoid dumping API keys, cookies, etc... - -#### Rule #2: Do not write debug logs in production code: - -The kernel log buffer is global and of limited size. Any extra debug log you add to your activity -or service makes it more difficult to diagnose problems on other parts of the system, because they -tend to push the interesting bit out of the buffer too soon. This is a recurring problem on -Android, so avoid participating into it. - -Logs can be disabled using system properties. Because log messages might not be -written, the cost of creating them should also be avoided. This can be done using three -complementary ways: - -- Use string formatting instead of concatenations - - // BAD - Log.d(TAG, "I " + preference + " writing logs."); - - // BETTER - Log.d(TAG, "I %s writing logs.", preference); - - If logging is disabled, the function's arguments will still have to be computed and provided - as input. The first call above will always lead to the creation of a `StringBuilder` and a few - concatenations, while the second just passes the arguments and won't need that. - -- Guard expensive calls - - Sometimes the values to log aren't readily available and need to be computed specially. This - should be avoided when logging is disabled. - - Using `Log#isLoggable` will return whether logging for a specific tag is allowed or not. It is - the call used inside the log functions and using allows to know when running the expensive - functions is needed. - - if (Log.isLoggable(TAG, Log.DEBUG) { - Log.d(TAG, "Something happened: %s", dumpDom(tab)); - } - - For more info, See the [android framework documentation] - (http://developer.android.com/tools/debugging/debugging-log.html). - - Using a debug constant is a less flexible, but more perfomance oriented alternative. - - static private final boolean DEBUG = false; // set to 'true' to enable debug - ... - if (DEBUG) { - Log.i(TAG, createThatExpensiveLogMessage(activity)) - } - - Because the variable is a `static final` that can be evaluated at compile time, the Java - compiler will optimize out all guarded calls from the generated `.class` file. Changing it - however requires editing each of the files for which debug should be enabled and recompiling, - while the previous method can enable or disable debugging for a whole feature without changing - any source file. - -- Annotate debug functions with the `@RemovableInRelease` annotation. - - That annotation tells Proguard to assume that a given function has no side effects, and is - called only for its returned value. If this value is unused, the call will be removed. If the - function is not called at all, it will also be removed. Since Proguard is already used to - strip debug and verbose calls out of release builds, this annotation allows it to have a - deeper action by removing also function calls used to generate the log call's arguments. - - /* If that function is only used in Log.d calls, proguard should completely remove it from - * the release builds. */ - @RemovableInRelease - private static String getSomeDebugLogString(Thing[] things) { - /* Still needs to be guarded to avoid impacting debug builds, or in case it's used for - * some other log levels. But at least it is done only once, inside the function. */ - if (!Log.isLoggable(TAG, Log.DEBUG)) return null; - - StringBuilder sb = new StringBuilder("Reporting " + thing.length + " things:"); - for (Thing thing : things) { - sb.append('\n').append(thing.id).append(' ').append(report.foo); - } - return sb.toString(); - } - - public void bar() { - ... - Log.d(TAG, getSomeDebugLogString(things)); /* In debug builds, the function does nothing - * is debug is disabled, and the entire line - * is removed in release builds. */ - } - - Again, this is useful only if the input to that function are variables already available in - the scope. The idea is to move computations, concatenations, etc. to a place where that can be - removed when not needed, without invading the main function's logic. - -#### Rule #3: Favor small log messages - -This is still related to the global fixed-sized kernel buffer used to keep all logs. Try to make -your log information as terse as possible. This reduces the risk of pushing interesting log data -out of the buffer when something really nasty happens. It's really better to have a single-line -log message, than several ones. I.e. don't use: - - Log.GROUP.d(TAG, "field1 = %s", value1); - Log.GROUP.d(TAG, "field2 = %s", value2); - Log.GROUP.d(TAG, "field3 = %s", value3); - -Instead, write this as: - - Log.d(TAG, "field1 = %s, field2 = %s, field3 = %s", value1, value2, value3); - -That doesn't seem to be much different if you count overall character counts, but each independent -log entry also implies a small, but non-trivial header, in the kernel log buffer. -And since every byte count, you can also try something even shorter, as in: - - Log.d(TAG, "fields [%s,%s,%s]", value1, value2, value3);
diff --git a/android/java/src/org/chromium/base/library_loader/LegacyLinker.java b/android/java/src/org/chromium/base/library_loader/LegacyLinker.java index 8e6776a..05c861d 100644 --- a/android/java/src/org/chromium/base/library_loader/LegacyLinker.java +++ b/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
@@ -71,76 +71,54 @@ return new LegacyLinker(); } - // Used internally to initialize the linker's data. Assume lock is held. + // Used internally to initialize the linker's data. Assumes lock is held. + // Loads JNI, and sets mMemoryDeviceConfig and mBrowserUsesSharedRelro. private void ensureInitializedLocked() { assert Thread.holdsLock(mLock); - if (mInitialized) { + if (mInitialized || !NativeLibraries.sUseLinker) { return; } - if (NativeLibraries.sUseLinker) { - // Load libchromium_android_linker.so. - loadLinkerJNILibrary(); + // On first call, load libchromium_android_linker.so. Cannot be done in the + // constructor because instantiation occurs on the UI thread. + loadLinkerJniLibrary(); - if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) { - if (SysUtils.isLowEndDevice()) { - mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW; - } else { - mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL; - } + if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) { + if (SysUtils.isLowEndDevice()) { + mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW; + } else { + mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL; } + } - switch (BROWSER_SHARED_RELRO_CONFIG) { - case BROWSER_SHARED_RELRO_CONFIG_NEVER: - mBrowserUsesSharedRelro = false; - break; - case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY: - if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) { - mBrowserUsesSharedRelro = true; - Log.w(TAG, "Low-memory device: shared RELROs used in all processes"); - } else { - mBrowserUsesSharedRelro = false; - } - break; - case BROWSER_SHARED_RELRO_CONFIG_ALWAYS: - Log.w(TAG, "Beware: shared RELROs used in all processes!"); + // Cannot run in the constructor because SysUtils.isLowEndDevice() relies + // on CommandLine, which may not be available at instantiation. + switch (BROWSER_SHARED_RELRO_CONFIG) { + case BROWSER_SHARED_RELRO_CONFIG_NEVER: + mBrowserUsesSharedRelro = false; + break; + case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY: + if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) { mBrowserUsesSharedRelro = true; - break; - default: - assert false : "Unreached"; - break; - } - } else { - if (DEBUG) { - Log.i(TAG, "Linker disabled"); - } + Log.w(TAG, "Low-memory device: shared RELROs used in all processes"); + } else { + mBrowserUsesSharedRelro = false; + } + break; + case BROWSER_SHARED_RELRO_CONFIG_ALWAYS: + Log.w(TAG, "Beware: shared RELROs used in all processes!"); + mBrowserUsesSharedRelro = true; + break; + default: + assert false : "Unreached"; + break; } mInitialized = true; } /** - * Call this method to determine if this chromium project must - * use this linker. If not, System.loadLibrary() should be used to load - * libraries instead. - */ - @Override - public boolean isUsed() { - // Only GYP targets that are APKs and have the 'use_chromium_linker' variable - // defined as 1 will use this linker. For all others (the default), the - // auto-generated NativeLibraries.sUseLinker variable will be false. - if (!NativeLibraries.sUseLinker) { - return false; - } - - synchronized (mLock) { - ensureInitializedLocked(); - return true; - } - } - - /** * Call this method to determine if the linker will try to use shared RELROs * for the browser process. */ @@ -187,10 +165,8 @@ if (DEBUG) { Log.i(TAG, String.format(Locale.US, - "mInBrowserProcess=%s mBrowserUsesSharedRelro=%s mWaitForSharedRelros=%s", - mInBrowserProcess ? "true" : "false", - mBrowserUsesSharedRelro ? "true" : "false", - mWaitForSharedRelros ? "true" : "false")); + "mInBrowserProcess=%b mBrowserUsesSharedRelro=%b mWaitForSharedRelros=%b", + mInBrowserProcess, mBrowserUsesSharedRelro, mWaitForSharedRelros)); } if (mLoadedLibraries == null) { @@ -315,6 +291,7 @@ Log.i(TAG, "disableSharedRelros() called"); } synchronized (mLock) { + ensureInitializedLocked(); mInBrowserProcess = false; mWaitForSharedRelros = false; mBrowserUsesSharedRelro = false; @@ -441,7 +418,9 @@ } // In service processes, close all file descriptors from the map now. - if (!mInBrowserProcess) closeLibInfoMap(relroMap); + if (!mInBrowserProcess) { + closeLibInfoMap(relroMap); + } if (DEBUG) { Log.i(TAG, "Linker.useSharedRelrosLocked() exiting");
diff --git a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/android/java/src/org/chromium/base/library_loader/LibraryLoader.java index e581300..4fa8020 100644 --- a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java +++ b/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -219,13 +219,10 @@ assert !mInitialized; long startTime = SystemClock.uptimeMillis(); - Linker linker = Linker.getInstance(); - boolean useChromiumLinker = linker.isUsed(); - if (useChromiumLinker) { - // Determine the APK file path. - String apkFilePath = getLibraryApkPath(context); + if (Linker.isUsed()) { // Load libraries using the Chromium linker. + Linker linker = Linker.getInstance(); linker.prepareLibraryLoad(); for (String library : NativeLibraries.LIBRARIES) { @@ -242,8 +239,8 @@ String libFilePath = System.mapLibraryName(library); if (linker.isInZipFile()) { // Load directly from the APK. - zipFilePath = apkFilePath; - Log.i(TAG, "Loading " + library + " from within " + apkFilePath); + zipFilePath = getLibraryApkPath(context); + Log.i(TAG, "Loading " + library + " from within " + zipFilePath); } else { // The library is in its own file. Log.i(TAG, "Loading " + library);
diff --git a/android/java/src/org/chromium/base/library_loader/Linker.java b/android/java/src/org/chromium/base/library_loader/Linker.java index c696288..9c280e8 100644 --- a/android/java/src/org/chromium/base/library_loader/Linker.java +++ b/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -148,6 +148,9 @@ // Log tag for this class. private static final String TAG = "cr.library_loader"; + // Name of the library that contains our JNI code. + private static final String LINKER_JNI_LIBRARY = "chromium_android_linker"; + // Constants used to control the behaviour of the browser process with // regards to the shared RELRO section. Not applicable to ModernLinker. // NEVER -> The browser never uses it itself. @@ -180,9 +183,6 @@ // Not used by ModernLinker. protected int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT; - // Name of the library that contains our JNI code. - protected static final String LINKER_JNI_LIBRARY = "chromium_android_linker"; - // Set to true to enable debug logs. protected static final boolean DEBUG = false; @@ -257,7 +257,7 @@ * * @return true if native library linker tests are enabled. */ - public static boolean areLinkerTestsEnabled() { + public static boolean areTestsEnabled() { return NativeLibraries.sEnableLinkerTests; } @@ -292,7 +292,7 @@ * * @param type LINKER_IMPLEMENTATION_LEGACY or LINKER_IMPLEMENTATION_MODERN */ - public static final void setLinkerImplementationForTesting(int type) { + public static final void setImplementationForTesting(int type) { // Sanity check. This method may only be called during tests. assertLinkerTestsAreEnabled(); assertForTesting( @@ -318,18 +318,21 @@ * * @return LINKER_IMPLEMENTATION_LEGACY or LINKER_IMPLEMENTATION_MODERN */ - public int getLinkerImplementationForTesting() { + public int getImplementationForTesting() { // Sanity check. This method may only be called during tests. assertLinkerTestsAreEnabled(); - assertForTesting(sSingleton != null); - if (sSingleton instanceof ModernLinker) { - return LINKER_IMPLEMENTATION_MODERN; - } else if (sSingleton instanceof LegacyLinker) { - return LINKER_IMPLEMENTATION_LEGACY; + synchronized (sSingletonLock) { + assertForTesting(sSingleton != null); + + if (sSingleton instanceof ModernLinker) { + return LINKER_IMPLEMENTATION_MODERN; + } else if (sSingleton instanceof LegacyLinker) { + return LINKER_IMPLEMENTATION_LEGACY; + } + Log.e(TAG, "Invalid linker: " + sSingleton.getClass().getName()); + return 0; } - Log.e(TAG, "Invalid linker: " + sSingleton.getClass().getName()); - return 0; } /** @@ -358,7 +361,7 @@ */ public void setTestRunnerClassNameForTesting(String testRunnerClassName) { if (DEBUG) { - Log.i(TAG, "setTestRunnerByClassNameForTesting(" + testRunnerClassName + ") called"); + Log.i(TAG, "setTestRunnerClassNameForTesting(" + testRunnerClassName + ") called"); } // Sanity check. This method may only be called during tests. assertLinkerTestsAreEnabled(); @@ -387,6 +390,42 @@ } /** + * Set up the Linker for a test. + * Convenience function that calls setImplementationForTesting() to force an + * implementation, and then setTestRunnerClassNameForTesting() to set the test + * class name. + * + * On first call, instantiates a Linker of the requested type and sets its test + * runner class name. On subsequent calls, checks that the singleton produced by + * the first call matches the requested type and test runner class name. + */ + public static void setupForTesting(int type, String testRunnerClassName) { + if (DEBUG) { + Log.i(TAG, "setupForTesting(" + type + ", " + testRunnerClassName + ") called"); + } + // Sanity check. This method may only be called during tests. + assertLinkerTestsAreEnabled(); + + synchronized (sSingletonLock) { + // If this is the first call, configure the Linker to the given type and test class. + if (sSingleton == null) { + setImplementationForTesting(type); + sSingleton.setTestRunnerClassNameForTesting(testRunnerClassName); + return; + } + + // If not the first call, check that the Linker configuration matches this request. + assertForTesting(sSingleton.getImplementationForTesting() == type); + String ourTestRunnerClassName = sSingleton.getTestRunnerClassNameForTesting(); + if (testRunnerClassName == null) { + assertForTesting(ourTestRunnerClassName == null); + } else { + assertForTesting(ourTestRunnerClassName.equals(testRunnerClassName)); + } + } + } + + /** * Instantiate and run the current TestRunner, if any. The TestRunner implementation * must be instantiated _after_ all libraries are loaded to ensure that its * native methods are properly registered. @@ -471,7 +510,7 @@ * In a component build, the suffix ".cr" is added to each library name, so * if the initial load fails we retry with a suffix. */ - protected void loadLinkerJNILibrary() { + protected static void loadLinkerJniLibrary() { String libName = "lib" + LINKER_JNI_LIBRARY + ".so"; if (DEBUG) { Log.i(TAG, "Loading " + libName); @@ -479,7 +518,7 @@ try { System.loadLibrary(LINKER_JNI_LIBRARY); } catch (UnsatisfiedLinkError e) { - Log.w(TAG, "Couldn't load " + libName + ", trying " + libName + ".so"); + Log.w(TAG, "Couldn't load " + libName + ", trying " + libName + ".cr"); System.loadLibrary(LINKER_JNI_LIBRARY + ".cr"); } } @@ -553,7 +592,7 @@ * Call this method to determine if the chromium project must load the library * directly from a zip file. */ - public boolean isInZipFile() { + public static boolean isInZipFile() { // The auto-generated NativeLibraries.sUseLibraryInZipFile variable will be true // if the library remains embedded in the APK zip file on the target. return NativeLibraries.sUseLibraryInZipFile; @@ -564,7 +603,11 @@ * use this linker. If not, System.loadLibrary() should be used to load * libraries instead. */ - public abstract boolean isUsed(); + public static boolean isUsed() { + // The auto-generated NativeLibraries.sUseLinker variable will be true if the + // build has not explicitly disabled Linker features. + return NativeLibraries.sUseLinker; + } /** * Call this method to determine if the linker will try to use shared RELROs
diff --git a/android/java/src/org/chromium/base/library_loader/ModernLinker.java b/android/java/src/org/chromium/base/library_loader/ModernLinker.java index a437779..4b01186 100644 --- a/android/java/src/org/chromium/base/library_loader/ModernLinker.java +++ b/android/java/src/org/chromium/base/library_loader/ModernLinker.java
@@ -70,32 +70,20 @@ return new ModernLinker(); } - // Used internally to initialize the linker's data. Assume lock is held. + // Used internally to initialize the linker's data. Assumes lock is held. private void ensureInitializedLocked() { assert Thread.holdsLock(mLock); assert NativeLibraries.sUseLinker; - // On first call, load libchromium_android_linker.so. + // On first call, load libchromium_android_linker.so. Cannot be done in the + // constructor because the instance is constructed on the UI thread. if (!mInitialized) { - loadLinkerJNILibrary(); + loadLinkerJniLibrary(); mInitialized = true; } } /** - * Call this method to determine if this chromium project must - * use this linker. If not, System.loadLibrary() should be used to load - * libraries instead. - */ - @Override - public boolean isUsed() { - // Only GYP targets that are APKs and have the 'use_chromium_linker' variable - // defined as 1 will use this linker. For all others (the default), the - // auto-generated NativeLibraries.sUseLinker variable will be false. - return NativeLibraries.sUseLinker; - } - - /** * Call this method to determine if the linker will try to use shared RELROs * for the browser process. */
diff --git a/android/jni_generator/golden_sample_for_tests_jni.h b/android/jni_generator/golden_sample_for_tests_jni.h index 8979697..fd1a632 100644 --- a/android/jni_generator/golden_sample_for_tests_jni.h +++ b/android/jni_generator/golden_sample_for_tests_jni.h
@@ -41,14 +41,16 @@ // Step 2: method stubs. -static jlong Init(JNIEnv* env, jobject jcaller, - jstring param); +static jlong Init(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + const JavaParamRef<jstring>& param); static jlong Java_org_chromium_example_jni_1generator_SampleForTests_nativeInit( JNIEnv* env, jobject jcaller, jstring param) { - return Init(env, jcaller, param); + return Init(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jstring>(env, param)); } static void @@ -58,46 +60,51 @@ jlong nativeCPPClass) { CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, jcaller); + return native->Destroy(env, JavaParamRef<jobject>(env, jcaller)); } -static jdouble GetDoubleFunction(JNIEnv* env, jobject jcaller); +static jdouble GetDoubleFunction(JNIEnv* env, + const JavaParamRef<jobject>& jcaller); static jdouble Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetDoubleFunction( JNIEnv* env, jobject jcaller) { - return GetDoubleFunction(env, jcaller); + return GetDoubleFunction(env, JavaParamRef<jobject>(env, jcaller)); } -static jfloat GetFloatFunction(JNIEnv* env, jclass jcaller); +static jfloat GetFloatFunction(JNIEnv* env, + const JavaParamRef<jclass>& jcaller); static jfloat Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetFloatFunction( JNIEnv* env, jclass jcaller) { - return GetFloatFunction(env, jcaller); + return GetFloatFunction(env, JavaParamRef<jclass>(env, jcaller)); } -static void SetNonPODDatatype(JNIEnv* env, jobject jcaller, - jobject rect); +static void SetNonPODDatatype(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + const JavaParamRef<jobject>& rect); static void Java_org_chromium_example_jni_1generator_SampleForTests_nativeSetNonPODDatatype( JNIEnv* env, jobject jcaller, jobject rect) { - return SetNonPODDatatype(env, jcaller, rect); + return SetNonPODDatatype(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jobject>(env, rect)); } -static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, - jobject jcaller); +static ScopedJavaLocalRef<jobject> GetNonPODDatatype( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller); static jobject Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetNonPODDatatype( JNIEnv* env, jobject jcaller) { - return GetNonPODDatatype(env, jcaller).Release(); + return GetNonPODDatatype(env, JavaParamRef<jobject>(env, jcaller)).Release(); } static jint @@ -107,7 +114,7 @@ jlong nativeCPPClass) { CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, jcaller); + return native->Method(env, JavaParamRef<jobject>(env, jcaller)); } static jdouble @@ -118,7 +125,7 @@ CPPClass::InnerClass* native = reinterpret_cast<CPPClass::InnerClass*>(nativePtr); CHECK_NATIVE_PTR(env, jcaller, native, "MethodOtherP0", 0); - return native->MethodOtherP0(env, jcaller); + return native->MethodOtherP0(env, JavaParamRef<jobject>(env, jcaller)); } static void @@ -129,7 +136,8 @@ jobject b) { CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); CHECK_NATIVE_PTR(env, jcaller, native, "AddStructB"); - return native->AddStructB(env, jcaller, b); + return native->AddStructB(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jobject>(env, b)); } static void @@ -139,7 +147,8 @@ jlong nativeCPPClass) { CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); CHECK_NATIVE_PTR(env, jcaller, native, "IterateAndDoSomethingWithStructB"); - return native->IterateAndDoSomethingWithStructB(env, jcaller); + return native->IterateAndDoSomethingWithStructB( + env, JavaParamRef<jobject>(env, jcaller)); } static jstring @@ -149,7 +158,8 @@ jlong nativeCPPClass) { CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); CHECK_NATIVE_PTR(env, jcaller, native, "ReturnAString", NULL); - return native->ReturnAString(env, jcaller).Release(); + return native->ReturnAString(env, JavaParamRef<jobject>(env, jcaller)) + .Release(); } static base::subtle::AtomicWord g_SampleForTests_javaMethod = 0;
diff --git a/android/jni_generator/jni_generator.py b/android/jni_generator/jni_generator.py index 33f83ec..5d262c8 100755 --- a/android/jni_generator/jni_generator.py +++ b/android/jni_generator/jni_generator.py
@@ -135,6 +135,19 @@ return 'jobject' +def WrapCTypeForDeclaration(c_type): + """Wrap the C datatype in a JavaRef if required.""" + if re.match(RE_SCOPED_JNI_TYPES, c_type): + return 'const JavaParamRef<' + c_type + '>&' + else: + return c_type + + +def JavaDataTypeToCForDeclaration(java_type): + """Returns a JavaRef-wrapped C datatype for the given java type.""" + return WrapCTypeForDeclaration(JavaDataTypeToC(java_type)) + + def JavaDataTypeToCForCalledByNativeParam(java_type): """Returns a C datatype to be when calling from native.""" if java_type == 'int': @@ -527,10 +540,9 @@ return called_by_natives -# Regex to match the JNI return types that should be included in a -# ScopedJavaLocalRef. -RE_SCOPED_JNI_RETURN_TYPES = re.compile( - 'jobject|jclass|jstring|jthrowable|.*Array') +# Regex to match the JNI types that should be wrapped in a JavaRef. +RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array') + # Regex to match a string like "@CalledByNative public void foo(int bar)". RE_CALLED_BY_NATIVE = re.compile( @@ -1005,18 +1017,36 @@ return '\n'.join(all_namespaces) + '\n' return '' - def GetJNIFirstParam(self, native): - ret = [] + def GetJNIFirstParamType(self, native): if native.type == 'method': - ret = ['jobject jcaller'] + return 'jobject' elif native.type == 'function': if native.static: - ret = ['jclass jcaller'] + return 'jclass' else: - ret = ['jobject jcaller'] - return ret + return 'jobject' + + def GetJNIFirstParam(self, native, for_declaration): + c_type = self.GetJNIFirstParamType(native) + if for_declaration: + c_type = WrapCTypeForDeclaration(c_type) + return [c_type + ' jcaller'] def GetParamsInDeclaration(self, native): + """Returns the params for the forward declaration. + + Args: + native: the native dictionary describing the method. + + Returns: + A string containing the params. + """ + return ',\n '.join(self.GetJNIFirstParam(native, True) + + [JavaDataTypeToCForDeclaration(param.datatype) + ' ' + + param.name + for param in native.params]) + + def GetParamsInStub(self, native): """Returns the params for the stub declaration. Args: @@ -1025,7 +1055,7 @@ Returns: A string containing the params. """ - return ',\n '.join(self.GetJNIFirstParam(native) + + return ',\n '.join(self.GetJNIFirstParam(native, False) + [JavaDataTypeToC(param.datatype) + ' ' + param.name for param in native.params]) @@ -1056,6 +1086,16 @@ 'JAVA_NAME': java_name} return template.substitute(values) + def GetJavaParamRefForCall(self, c_type, name): + return Template('JavaParamRef<${TYPE}>(env, ${NAME})').substitute({ + 'TYPE': c_type, + 'NAME': name, + }) + + def GetJNIFirstParamForCall(self, native): + c_type = self.GetJNIFirstParamType(native) + return [self.GetJavaParamRefForCall(c_type, 'jcaller')] + def GetNativeStub(self, native): is_method = native.type == 'method' @@ -1065,8 +1105,14 @@ params = native.params params_in_call = [] if not self.options.pure_native_methods: - params_in_call = ['env', 'jcaller'] - params_in_call = ', '.join(params_in_call + [p.name for p in params]) + params_in_call = ['env'] + self.GetJNIFirstParamForCall(native) + for p in params: + c_type = JavaDataTypeToC(p.datatype) + if re.match(RE_SCOPED_JNI_TYPES, c_type): + params_in_call.append(self.GetJavaParamRefForCall(c_type, p.name)) + else: + params_in_call.append(p.name) + params_in_call = ', '.join(params_in_call) if self.options.native_exports: stub_visibility = 'extern "C" __attribute__((visibility("default")))\n' @@ -1074,7 +1120,7 @@ stub_visibility = 'static ' return_type = return_declaration = JavaDataTypeToC(native.return_type) post_call = '' - if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): + if re.match(RE_SCOPED_JNI_TYPES, return_type): post_call = '.Release()' return_declaration = 'ScopedJavaLocalRef<' + return_type + '>' values = { @@ -1082,6 +1128,7 @@ 'RETURN_DECLARATION': return_declaration, 'NAME': native.name, 'PARAMS': self.GetParamsInDeclaration(native), + 'PARAMS_IN_STUB': self.GetParamsInStub(native), 'PARAMS_IN_CALL': params_in_call, 'POST_CALL': post_call, 'STUB_NAME': self.GetStubName(native), @@ -1099,7 +1146,7 @@ }) template = Template("""\ ${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, - ${PARAMS}) { + ${PARAMS_IN_STUB}) { ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; @@ -1109,7 +1156,7 @@ template = Template(""" static ${RETURN_DECLARATION} ${NAME}(JNIEnv* env, ${PARAMS}); -${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS}) { +${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { return ${NAME}(${PARAMS_IN_CALL})${POST_CALL}; } """) @@ -1157,7 +1204,7 @@ if return_type != 'void': pre_call = ' ' + pre_call return_declaration = return_type + ' ret =' - if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): + if re.match(RE_SCOPED_JNI_TYPES, return_type): return_type = 'ScopedJavaLocalRef<' + return_type + '>' return_clause = 'return ' + return_type + '(env, ret);' else:
diff --git a/android/jni_generator/jni_generator_helper.h b/android/jni_generator/jni_generator_helper.h index b9736e1..1fdc4ee 100644 --- a/android/jni_generator/jni_generator_helper.h +++ b/android/jni_generator/jni_generator_helper.h
@@ -36,5 +36,6 @@ } // namespace jni_generator using base::android::ScopedJavaLocalRef; +using base::android::JavaParamRef; #endif // BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_
diff --git a/android/jni_generator/sample_for_tests.cc b/android/jni_generator/sample_for_tests.cc index c5820ab..ee8d6db 100644 --- a/android/jni_generator/sample_for_tests.cc +++ b/android/jni_generator/sample_for_tests.cc
@@ -67,22 +67,27 @@ } // Static free functions declared and called directly from java. -static jlong Init(JNIEnv* env, jobject caller, jstring param) { +static jlong Init(JNIEnv* env, + const JavaParamRef<jobject>& caller, + const JavaParamRef<jstring>& param) { return 0; } -static jdouble GetDoubleFunction(JNIEnv*, jobject) { +static jdouble GetDoubleFunction(JNIEnv*, const JavaParamRef<jobject>&) { return 0; } -static jfloat GetFloatFunction(JNIEnv*, jclass) { +static jfloat GetFloatFunction(JNIEnv*, const JavaParamRef<jclass>&) { return 0; } -static void SetNonPODDatatype(JNIEnv*, jobject, jobject) { -} +static void SetNonPODDatatype(JNIEnv*, + const JavaParamRef<jobject>&, + const JavaParamRef<jobject>&) {} -static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv*, jobject) { +static ScopedJavaLocalRef<jobject> GetNonPODDatatype( + JNIEnv*, + const JavaParamRef<jobject>&) { return ScopedJavaLocalRef<jobject>(); }
diff --git a/android/jni_generator/testEagerCalledByNativesOption.golden b/android/jni_generator/testEagerCalledByNativesOption.golden index cdd6151..b01cd39 100644 --- a/android/jni_generator/testEagerCalledByNativesOption.golden +++ b/android/jni_generator/testEagerCalledByNativesOption.golden
@@ -36,7 +36,7 @@ jint arg1) { Test* native = reinterpret_cast<Test*>(nativeTest); CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, jcaller, arg1); + return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1); } namespace {
diff --git a/android/jni_generator/testInnerClassNatives.golden b/android/jni_generator/testInnerClassNatives.golden index d6676c4..ad140e2 100644 --- a/android/jni_generator/testInnerClassNatives.golden +++ b/android/jni_generator/testInnerClassNatives.golden
@@ -28,11 +28,11 @@ // Step 2: method stubs. -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); static jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden index a9e645c..0a890e7 100644 --- a/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden +++ b/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
@@ -29,17 +29,17 @@ // Step 2: method stubs. -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); static jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); static jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testInnerClassNativesMultiple.golden b/android/jni_generator/testInnerClassNativesMultiple.golden index 5f54364..268f794 100644 --- a/android/jni_generator/testInnerClassNativesMultiple.golden +++ b/android/jni_generator/testInnerClassNativesMultiple.golden
@@ -30,18 +30,18 @@ // Step 2: method stubs. -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); static jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); static jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testJNIInitNativeNameOption.golden b/android/jni_generator/testJNIInitNativeNameOption.golden index 084f08d..46a57ac 100644 --- a/android/jni_generator/testJNIInitNativeNameOption.golden +++ b/android/jni_generator/testJNIInitNativeNameOption.golden
@@ -33,7 +33,7 @@ jint arg1) { Test* native = reinterpret_cast<Test*>(nativeTest); CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, jcaller, arg1); + return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1); } // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testJarJarRemapping.golden b/android/jni_generator/testJarJarRemapping.golden index b626a8a..fe13952 100644 --- a/android/jni_generator/testJarJarRemapping.golden +++ b/android/jni_generator/testJarJarRemapping.golden
@@ -27,40 +27,44 @@ // Step 2: method stubs. -static void Test(JNIEnv* env, jclass jcaller, - jobject t); +static void Test(JNIEnv* env, const JavaParamRef<jclass>& jcaller, + const JavaParamRef<jobject>& t); static void Java_com_test_jni_1generator_Example_nativeTest(JNIEnv* env, jclass jcaller, jobject t) { - return Test(env, jcaller, t); + return Test(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jobject>(env, t)); } -static void Test2(JNIEnv* env, jclass jcaller, - jobject t); +static void Test2(JNIEnv* env, const JavaParamRef<jclass>& jcaller, + const JavaParamRef<jobject>& t); static void Java_com_test_jni_1generator_Example_nativeTest2(JNIEnv* env, jclass jcaller, jobject t) { - return Test2(env, jcaller, t); + return Test2(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jobject>(env, t)); } -static void Test3(JNIEnv* env, jclass jcaller, - jobject t); +static void Test3(JNIEnv* env, const JavaParamRef<jclass>& jcaller, + const JavaParamRef<jobject>& t); static void Java_com_test_jni_1generator_Example_nativeTest3(JNIEnv* env, jclass jcaller, jobject t) { - return Test3(env, jcaller, t); + return Test3(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jobject>(env, t)); } -static void Test4(JNIEnv* env, jclass jcaller, - jobject t); +static void Test4(JNIEnv* env, const JavaParamRef<jclass>& jcaller, + const JavaParamRef<jobject>& t); static void Java_com_test_jni_1generator_Example_nativeTest4(JNIEnv* env, jclass jcaller, jobject t) { - return Test4(env, jcaller, t); + return Test4(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jobject>(env, t)); } // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testMultipleJNIAdditionalImport.golden b/android/jni_generator/testMultipleJNIAdditionalImport.golden index eb15190..6de8c21 100644 --- a/android/jni_generator/testMultipleJNIAdditionalImport.golden +++ b/android/jni_generator/testMultipleJNIAdditionalImport.golden
@@ -27,15 +27,17 @@ // Step 2: method stubs. -static void DoSomething(JNIEnv* env, jclass jcaller, - jobject callback1, - jobject callback2); +static void DoSomething(JNIEnv* env, const JavaParamRef<jclass>& jcaller, + const JavaParamRef<jobject>& callback1, + const JavaParamRef<jobject>& callback2); static void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* env, jclass jcaller, jobject callback1, jobject callback2) { - return DoSomething(env, jcaller, callback1, callback2); + return DoSomething(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jobject>(env, callback1), JavaParamRef<jobject>(env, + callback2)); } static base::subtle::AtomicWord g_Foo_calledByNative = 0;
diff --git a/android/jni_generator/testNativeExportsOption.golden b/android/jni_generator/testNativeExportsOption.golden index 68736ad..13cd2b9 100644 --- a/android/jni_generator/testNativeExportsOption.golden +++ b/android/jni_generator/testNativeExportsOption.golden
@@ -36,7 +36,7 @@ jint arg1) { Test* native = reinterpret_cast<Test*>(nativeTest); CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); - return native->StaticMethod(env, jcaller, arg1); + return native->StaticMethod(env, JavaParamRef<jobject>(env, jcaller), arg1); } extern "C" __attribute__((visibility("default"))) @@ -48,25 +48,25 @@ jint arg1) { Test* native = reinterpret_cast<Test*>(nativeTest); CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, jcaller, arg1); + return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1); } -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); extern "C" __attribute__((visibility("default"))) jint Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); extern "C" __attribute__((visibility("default"))) jint Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0;
diff --git a/android/jni_generator/testNativeExportsOptionalOption.golden b/android/jni_generator/testNativeExportsOptionalOption.golden index 6e2b655..e2cf051 100644 --- a/android/jni_generator/testNativeExportsOptionalOption.golden +++ b/android/jni_generator/testNativeExportsOptionalOption.golden
@@ -36,7 +36,7 @@ jint arg1) { Test* native = reinterpret_cast<Test*>(nativeTest); CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); - return native->StaticMethod(env, jcaller, arg1); + return native->StaticMethod(env, JavaParamRef<jobject>(env, jcaller), arg1); } extern "C" __attribute__((visibility("default"))) @@ -48,25 +48,25 @@ jint arg1) { Test* native = reinterpret_cast<Test*>(nativeTest); CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, jcaller, arg1); + return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1); } -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); extern "C" __attribute__((visibility("default"))) jint Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); extern "C" __attribute__((visibility("default"))) jint Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0;
diff --git a/android/jni_generator/testNatives.golden b/android/jni_generator/testNatives.golden index b19c337..f9538a3 100644 --- a/android/jni_generator/testNatives.golden +++ b/android/jni_generator/testNatives.golden
@@ -27,10 +27,10 @@ // Step 2: method stubs. -static jint Init(JNIEnv* env, jobject jcaller); +static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); static jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, jcaller); + return Init(env, JavaParamRef<jobject>(env, jcaller)); } static void Java_org_chromium_TestJni_nativeDestroy(JNIEnv* env, @@ -39,7 +39,7 @@ ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, jcaller); + return native->Destroy(env, JavaParamRef<jobject>(env, jcaller)); } static jlong Java_org_chromium_TestJni_nativeAddBookmark(JNIEnv* env, @@ -52,58 +52,67 @@ ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmark", 0); - return native->AddBookmark(env, jcaller, url, title, isFolder, parentId); + return native->AddBookmark(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jstring>(env, url), JavaParamRef<jstring>(env, title), + isFolder, parentId); } -static ScopedJavaLocalRef<jstring> GetDomainAndRegistry(JNIEnv* env, jclass - jcaller, - jstring url); +static ScopedJavaLocalRef<jstring> GetDomainAndRegistry(JNIEnv* env, const + JavaParamRef<jclass>& jcaller, + const JavaParamRef<jstring>& url); static jstring Java_org_chromium_TestJni_nativeGetDomainAndRegistry(JNIEnv* env, jclass jcaller, jstring url) { - return GetDomainAndRegistry(env, jcaller, url).Release(); + return GetDomainAndRegistry(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jstring>(env, url)).Release(); } -static void CreateHistoricalTabFromState(JNIEnv* env, jclass jcaller, - jbyteArray state, +static void CreateHistoricalTabFromState(JNIEnv* env, const + JavaParamRef<jclass>& jcaller, + const JavaParamRef<jbyteArray>& state, jint tab_index); static void Java_org_chromium_TestJni_nativeCreateHistoricalTabFromState(JNIEnv* env, jclass jcaller, jbyteArray state, jint tab_index) { - return CreateHistoricalTabFromState(env, jcaller, state, tab_index); + return CreateHistoricalTabFromState(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jbyteArray>(env, state), tab_index); } -static ScopedJavaLocalRef<jbyteArray> GetStateAsByteArray(JNIEnv* env, jobject - jcaller, - jobject view); +static ScopedJavaLocalRef<jbyteArray> GetStateAsByteArray(JNIEnv* env, const + JavaParamRef<jobject>& jcaller, + const JavaParamRef<jobject>& view); static jbyteArray Java_org_chromium_TestJni_nativeGetStateAsByteArray(JNIEnv* env, jobject jcaller, jobject view) { - return GetStateAsByteArray(env, jcaller, view).Release(); + return GetStateAsByteArray(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jobject>(env, view)).Release(); } static ScopedJavaLocalRef<jobjectArray> GetAutofillProfileGUIDs(JNIEnv* env, - jclass jcaller); + const JavaParamRef<jclass>& jcaller); static jobjectArray Java_org_chromium_TestJni_nativeGetAutofillProfileGUIDs(JNIEnv* env, jclass jcaller) { - return GetAutofillProfileGUIDs(env, jcaller).Release(); + return GetAutofillProfileGUIDs(env, JavaParamRef<jclass>(env, + jcaller)).Release(); } -static void SetRecognitionResults(JNIEnv* env, jobject jcaller, +static void SetRecognitionResults(JNIEnv* env, const JavaParamRef<jobject>& + jcaller, jint sessionId, - jobjectArray results); + const JavaParamRef<jobjectArray>& results); static void Java_org_chromium_TestJni_nativeSetRecognitionResults(JNIEnv* env, jobject jcaller, jint sessionId, jobjectArray results) { - return SetRecognitionResults(env, jcaller, sessionId, results); + return SetRecognitionResults(env, JavaParamRef<jobject>(env, jcaller), + sessionId, JavaParamRef<jobjectArray>(env, results)); } static jlong Java_org_chromium_TestJni_nativeAddBookmarkFromAPI(JNIEnv* env, @@ -119,24 +128,29 @@ ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmarkFromAPI", 0); - return native->AddBookmarkFromAPI(env, jcaller, url, created, isBookmark, - date, favicon, title, visits); + return native->AddBookmarkFromAPI(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jstring>(env, url), JavaParamRef<jobject>(env, created), + JavaParamRef<jobject>(env, isBookmark), JavaParamRef<jobject>(env, date), + JavaParamRef<jbyteArray>(env, favicon), JavaParamRef<jstring>(env, title), + JavaParamRef<jobject>(env, visits)); } -static jint FindAll(JNIEnv* env, jobject jcaller, - jstring find); +static jint FindAll(JNIEnv* env, const JavaParamRef<jobject>& jcaller, + const JavaParamRef<jstring>& find); static jint Java_org_chromium_TestJni_nativeFindAll(JNIEnv* env, jobject jcaller, jstring find) { - return FindAll(env, jcaller, find); + return FindAll(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jstring>(env, find)); } -static ScopedJavaLocalRef<jobject> GetInnerClass(JNIEnv* env, jclass jcaller); +static ScopedJavaLocalRef<jobject> GetInnerClass(JNIEnv* env, const + JavaParamRef<jclass>& jcaller); static jobject Java_org_chromium_TestJni_nativeGetInnerClass(JNIEnv* env, jclass jcaller) { - return GetInnerClass(env, jcaller).Release(); + return GetInnerClass(env, JavaParamRef<jclass>(env, jcaller)).Release(); } static jobject Java_org_chromium_TestJni_nativeQueryBitmap(JNIEnv* env, @@ -149,8 +163,10 @@ ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "QueryBitmap", NULL); - return native->QueryBitmap(env, jcaller, projection, selection, selectionArgs, - sortOrder).Release(); + return native->QueryBitmap(env, JavaParamRef<jobject>(env, jcaller), + JavaParamRef<jobjectArray>(env, projection), JavaParamRef<jstring>(env, + selection), JavaParamRef<jobjectArray>(env, selectionArgs), + JavaParamRef<jstring>(env, sortOrder)).Release(); } static void Java_org_chromium_TestJni_nativeGotOrientation(JNIEnv* env, @@ -162,17 +178,19 @@ DataFetcherImplAndroid* native = reinterpret_cast<DataFetcherImplAndroid*>(nativeDataFetcherImplAndroid); CHECK_NATIVE_PTR(env, jcaller, native, "GotOrientation"); - return native->GotOrientation(env, jcaller, alpha, beta, gamma); + return native->GotOrientation(env, JavaParamRef<jobject>(env, jcaller), alpha, + beta, gamma); } -static ScopedJavaLocalRef<jthrowable> MessWithJavaException(JNIEnv* env, jclass - jcaller, - jthrowable e); +static ScopedJavaLocalRef<jthrowable> MessWithJavaException(JNIEnv* env, const + JavaParamRef<jclass>& jcaller, + const JavaParamRef<jthrowable>& e); static jthrowable Java_org_chromium_TestJni_nativeMessWithJavaException(JNIEnv* env, jclass jcaller, jthrowable e) { - return MessWithJavaException(env, jcaller, e).Release(); + return MessWithJavaException(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jthrowable>(env, e)).Release(); } // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testNativesLong.golden b/android/jni_generator/testNativesLong.golden index 1235616..d5b67ba 100644 --- a/android/jni_generator/testNativesLong.golden +++ b/android/jni_generator/testNativesLong.golden
@@ -32,7 +32,7 @@ ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, jcaller); + return native->Destroy(env, JavaParamRef<jobject>(env, jcaller)); } // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testSingleJNIAdditionalImport.golden b/android/jni_generator/testSingleJNIAdditionalImport.golden index 3536139..1b2895e 100644 --- a/android/jni_generator/testSingleJNIAdditionalImport.golden +++ b/android/jni_generator/testSingleJNIAdditionalImport.golden
@@ -27,13 +27,14 @@ // Step 2: method stubs. -static void DoSomething(JNIEnv* env, jclass jcaller, - jobject callback); +static void DoSomething(JNIEnv* env, const JavaParamRef<jclass>& jcaller, + const JavaParamRef<jobject>& callback); static void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* env, jclass jcaller, jobject callback) { - return DoSomething(env, jcaller, callback); + return DoSomething(env, JavaParamRef<jclass>(env, jcaller), + JavaParamRef<jobject>(env, callback)); } static base::subtle::AtomicWord g_Foo_calledByNative = 0;
diff --git a/android/junit/src/org/chromium/base/LogTest.java b/android/junit/src/org/chromium/base/LogTest.java index e5ce239..6f4e406 100644 --- a/android/junit/src/org/chromium/base/LogTest.java +++ b/android/junit/src/org/chromium/base/LogTest.java
@@ -9,19 +9,16 @@ import static org.junit.Assert.assertTrue; import org.chromium.testing.local.LocalRobolectricTestRunner; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowLog; import java.util.List; /** Unit tests for {@link Log}. */ @RunWith(LocalRobolectricTestRunner.class) -@Config(manifest = Config.NONE, shadows = {LogTest.PermissiveShadowLog.class}) +@Config(manifest = Config.NONE) public class LogTest { /** Tests that the computed call origin is the correct one. */ @Test @@ -74,139 +71,4 @@ assertEquals(t2, logs.get(logs.size() - 1).throwable); assertEquals("Bar MyThrowable MyOtherThrowable", logs.get(logs.size() - 1).msg); } - - public void verboseLoggingTest() { - PermissiveShadowLog.setLevel(Log.VERBOSE); - List<ShadowLog.LogItem> logs = ShadowLog.getLogs(); - - Log.wtf("Foo", "Bar"); - Log.e("Foo", "Bar"); - Log.w("Foo", "Bar"); - Log.i("Foo", "Bar"); - Log.d("Foo", "Bar"); - Log.v("Foo", "Bar"); - - assertEquals(Log.ASSERT, logs.get(0).type); - assertEquals(Log.ERROR, logs.get(1).type); - assertEquals(Log.WARN, logs.get(2).type); - assertEquals(Log.INFO, logs.get(3).type); - assertEquals(Log.DEBUG, logs.get(4).type); - assertEquals(Log.VERBOSE, logs.get(5).type); - assertEquals(6, logs.size()); - } - - @Test - public void debugLoggingTest() { - PermissiveShadowLog.setLevel(Log.DEBUG); - List<ShadowLog.LogItem> logs = ShadowLog.getLogs(); - - Log.wtf("Foo", "Bar"); - Log.e("Foo", "Bar"); - Log.w("Foo", "Bar"); - Log.i("Foo", "Bar"); - Log.d("Foo", "Bar"); - Log.v("Foo", "Bar"); - - assertEquals(Log.ASSERT, logs.get(0).type); - assertEquals(Log.ERROR, logs.get(1).type); - assertEquals(Log.WARN, logs.get(2).type); - assertEquals(Log.INFO, logs.get(3).type); - assertEquals(Log.DEBUG, logs.get(4).type); - assertEquals(5, logs.size()); - } - - @Test - public void infoLoggingTest() { - PermissiveShadowLog.setLevel(Log.INFO); - List<ShadowLog.LogItem> logs = ShadowLog.getLogs(); - - Log.wtf("Foo", "Bar"); - Log.e("Foo", "Bar"); - Log.w("Foo", "Bar"); - Log.i("Foo", "Bar"); - Log.d("Foo", "Bar"); - Log.v("Foo", "Bar"); - - assertEquals(Log.ASSERT, logs.get(0).type); - assertEquals(Log.ERROR, logs.get(1).type); - assertEquals(Log.WARN, logs.get(2).type); - assertEquals(Log.INFO, logs.get(3).type); - assertEquals(4, logs.size()); - } - - @Test - public void warnLoggingTest() { - PermissiveShadowLog.setLevel(Log.WARN); - List<ShadowLog.LogItem> logs = ShadowLog.getLogs(); - - Log.wtf("Foo", "Bar"); - Log.e("Foo", "Bar"); - Log.w("Foo", "Bar"); - Log.i("Foo", "Bar"); - Log.d("Foo", "Bar"); - Log.v("Foo", "Bar"); - - assertEquals(Log.ASSERT, logs.get(0).type); - assertEquals(Log.ERROR, logs.get(1).type); - assertEquals(Log.WARN, logs.get(2).type); - assertEquals(3, logs.size()); - } - - @Test - public void errorLoggingTest() { - PermissiveShadowLog.setLevel(Log.ERROR); - List<ShadowLog.LogItem> logs = ShadowLog.getLogs(); - - Log.wtf("Foo", "Bar"); - Log.e("Foo", "Bar"); - Log.w("Foo", "Bar"); - Log.i("Foo", "Bar"); - Log.d("Foo", "Bar"); - Log.v("Foo", "Bar"); - - assertEquals(Log.ASSERT, logs.get(0).type); - assertEquals(Log.ERROR, logs.get(1).type); - assertEquals(2, logs.size()); - } - - @Test - public void assertLoggingTest() { - PermissiveShadowLog.setLevel(Log.ASSERT); - List<ShadowLog.LogItem> logs = ShadowLog.getLogs(); - - Log.wtf("Foo", "Bar"); - Log.e("Foo", "Bar"); - Log.w("Foo", "Bar"); - Log.i("Foo", "Bar"); - Log.d("Foo", "Bar"); - Log.v("Foo", "Bar"); - - assertEquals(Log.ASSERT, logs.get(0).type); - assertEquals(1, logs.size()); - } - - @Before - public void beforeTest() { - PermissiveShadowLog.reset(); - } - - /** Needed to allow debug/verbose logging that is disabled by default. */ - @Implements(android.util.Log.class) - public static class PermissiveShadowLog extends ShadowLog { - private static int sLevel = Log.VERBOSE; - - /** Sets the log level for all tags. */ - public static void setLevel(int level) { - sLevel = level; - } - - @Implementation - public static boolean isLoggable(String tag, int level) { - return level >= sLevel; - } - - public static void reset() { - sLevel = Log.VERBOSE; - } - } }
diff --git a/android/library_loader/library_loader_hooks.cc b/android/library_loader/library_loader_hooks.cc index 1f745ec..4a61012 100644 --- a/android/library_loader/library_loader_hooks.cc +++ b/android/library_loader/library_loader_hooks.cc
@@ -57,7 +57,7 @@ static void RegisterChromiumAndroidLinkerRendererHistogram( JNIEnv* env, - jobject jcaller, + const JavaParamRef<jobject>& jcaller, jboolean requested_shared_relro, jboolean load_at_fixed_address_failed, jlong library_load_time_ms) { @@ -89,7 +89,7 @@ static void RecordChromiumAndroidLinkerBrowserHistogram( JNIEnv* env, - jobject jcaller, + const JavaParamRef<jobject>& jcaller, jboolean is_using_browser_shared_relros, jboolean load_at_fixed_address_failed, jint library_load_from_apk_status, @@ -121,13 +121,15 @@ g_registration_callback = func; } -static void InitCommandLine(JNIEnv* env, - jobject jcaller, - jobjectArray init_command_line) { +static void InitCommandLine( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + const JavaParamRef<jobjectArray>& init_command_line) { InitNativeCommandLineFromJavaArray(env, init_command_line); } -static jboolean LibraryLoaded(JNIEnv* env, jobject jcaller) { +static jboolean LibraryLoaded(JNIEnv* env, + const JavaParamRef<jobject>& jcaller) { if (g_registration_callback == NULL) { return true; } @@ -141,7 +143,9 @@ } } -static jboolean ForkAndPrefetchNativeLibrary(JNIEnv* env, jclass clazz) { +static jboolean ForkAndPrefetchNativeLibrary( + JNIEnv* env, + const JavaParamRef<jclass>& clazz) { return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(); } @@ -153,7 +157,9 @@ g_library_version_number = strdup(version_number); } -ScopedJavaLocalRef<jstring> GetVersionNumber(JNIEnv* env, jobject jcaller) { +ScopedJavaLocalRef<jstring> GetVersionNumber( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller) { return ConvertUTF8ToJavaString(env, g_library_version_number); }
diff --git a/android/library_loader/library_prefetcher.cc b/android/library_loader/library_prefetcher.cc index 9b54843..4c47bed 100644 --- a/android/library_loader/library_prefetcher.cc +++ b/android/library_loader/library_prefetcher.cc
@@ -118,6 +118,12 @@ // static bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { +// Avoid forking with cygprofile instrumentation because the latter performs +// memory allocations. +#if defined(CYGPROFILE_INSTRUMENTATION) + return false; +#endif + // Looking for ranges is done before the fork, to avoid syscalls and/or memory // allocations in the forked process. The child process inherits the lock // state of its parent thread. It cannot rely on being able to acquire any @@ -126,6 +132,7 @@ std::vector<AddressRange> ranges; if (!FindRanges(&ranges)) return false; + pid_t pid = fork(); if (pid == 0) { setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
diff --git a/android/memory_pressure_listener_android.cc b/android/memory_pressure_listener_android.cc index 80c07bc..9d3dd46 100644 --- a/android/memory_pressure_listener_android.cc +++ b/android/memory_pressure_listener_android.cc
@@ -8,8 +8,9 @@ #include "jni/MemoryPressureListener_jni.h" // Defined and called by JNI. -static void OnMemoryPressure( - JNIEnv* env, jclass clazz, jint memory_pressure_level) { +static void OnMemoryPressure(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + jint memory_pressure_level) { base::MemoryPressureListener::NotifyMemoryPressure( static_cast<base::MemoryPressureListener::MemoryPressureLevel>( memory_pressure_level));
diff --git a/android/path_service_android.cc b/android/path_service_android.cc index 18ca70c..9972bbb 100644 --- a/android/path_service_android.cc +++ b/android/path_service_android.cc
@@ -13,7 +13,10 @@ namespace base { namespace android { -void Override(JNIEnv* env, jclass clazz, jint what, jstring path) { +void Override(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + jint what, + const JavaParamRef<jstring>& path) { FilePath file_path(ConvertJavaStringToUTF8(env, path)); PathService::Override(what, file_path); }
diff --git a/android/record_histogram.cc b/android/record_histogram.cc index 0688e4f..61bbf88 100644 --- a/android/record_histogram.cc +++ b/android/record_histogram.cc
@@ -174,8 +174,8 @@ } // namespace void RecordBooleanHistogram(JNIEnv* env, - jclass clazz, - jstring j_histogram_name, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& j_histogram_name, jint j_histogram_key, jboolean j_sample) { bool sample = static_cast<bool>(j_sample); @@ -185,8 +185,8 @@ } void RecordEnumeratedHistogram(JNIEnv* env, - jclass clazz, - jstring j_histogram_name, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& j_histogram_name, jint j_histogram_key, jint j_sample, jint j_boundary) { @@ -198,8 +198,8 @@ } void RecordCustomCountHistogram(JNIEnv* env, - jclass clazz, - jstring j_histogram_name, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& j_histogram_name, jint j_histogram_key, jint j_sample, jint j_min, @@ -214,8 +214,8 @@ } void RecordLinearCountHistogram(JNIEnv* env, - jclass clazz, - jstring j_histogram_name, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& j_histogram_name, jint j_histogram_key, jint j_sample, jint j_min, @@ -230,31 +230,32 @@ } void RecordSparseHistogram(JNIEnv* env, - jclass clazz, - jstring j_histogram_name, - jint j_histogram_key, - jint j_sample) { - int sample = static_cast<int>(j_sample); - g_histograms.Get() - .SparseHistogram(env, j_histogram_name, j_histogram_key) - ->Add(sample); + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& j_histogram_name, + jint j_histogram_key, + jint j_sample) { + int sample = static_cast<int>(j_sample); + g_histograms.Get() + .SparseHistogram(env, j_histogram_name, j_histogram_key) + ->Add(sample); } -void RecordCustomTimesHistogramMilliseconds(JNIEnv* env, - jclass clazz, - jstring j_histogram_name, - jint j_histogram_key, - jlong j_duration, - jlong j_min, - jlong j_max, - jint j_num_buckets) { +void RecordCustomTimesHistogramMilliseconds( + JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& j_histogram_name, + jint j_histogram_key, + jlong j_duration, + jlong j_min, + jlong j_max, + jint j_num_buckets) { g_histograms.Get() .CustomTimesHistogram(env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets) ->AddTime(TimeDelta::FromMilliseconds(static_cast<int64>(j_duration))); } -void Initialize(JNIEnv* env, jclass) { +void Initialize(JNIEnv* env, const JavaParamRef<jclass>&) { StatisticsRecorder::Initialize(); } @@ -262,10 +263,11 @@ // MetricsUtils.HistogramDelta. It should live in a test-specific file, but we // currently can't have test-specific native code packaged in test-specific Java // targets - see http://crbug.com/415945. -jint GetHistogramValueCountForTesting(JNIEnv* env, - jclass clazz, - jstring histogram_name, - jint sample) { +jint GetHistogramValueCountForTesting( + JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& histogram_name, + jint sample) { HistogramBase* histogram = StatisticsRecorder::FindHistogram( android::ConvertJavaStringToUTF8(env, histogram_name)); if (histogram == nullptr) {
diff --git a/android/record_user_action.cc b/android/record_user_action.cc index 6172f2e..1452341 100644 --- a/android/record_user_action.cc +++ b/android/record_user_action.cc
@@ -11,7 +11,9 @@ namespace base { namespace android { -static void RecordUserAction(JNIEnv* env, jclass clazz, jstring j_action) { +static void RecordUserAction(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& j_action) { RecordComputedAction(ConvertJavaStringToUTF8(env, j_action)); }
diff --git a/android/scoped_java_ref.h b/android/scoped_java_ref.h index 37e7fd3..6274969 100644 --- a/android/scoped_java_ref.h +++ b/android/scoped_java_ref.h
@@ -94,6 +94,27 @@ DISALLOW_COPY_AND_ASSIGN(JavaRef); }; +// Holds a local reference to a JNI method parameter. +// Method parameters should not be deleted, and so this class exists purely to +// wrap them as a JavaRef<T> in the JNI binding generator. Do not create +// instances manually. +template <typename T> +class JavaParamRef : public JavaRef<T> { + public: + // Assumes that |obj| is a parameter passed to a JNI method from Java. + // Does not assume ownership as parameters should not be deleted. + JavaParamRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj) {} + + ~JavaParamRef() {} + + // TODO(torne): remove this cast once we're using JavaRef consistently. + // http://crbug.com/506850 + operator T() const { return JavaRef<T>::obj(); } + + private: + DISALLOW_COPY_AND_ASSIGN(JavaParamRef); +}; + // Holds a local reference to a Java object. The local reference is scoped // to the lifetime of this object. // Instances of this class may hold onto any JNIEnv passed into it until @@ -207,7 +228,12 @@ this->Reset(NULL, other.obj()); } - template<typename U> + template <typename U> + void Reset(JNIEnv* env, const JavaParamRef<U>& other) { + this->Reset(env, other.obj()); + } + + template <typename U> void Reset(JNIEnv* env, U obj) { implicit_cast<T>(obj); // Ensure U is assignable to T this->SetNewGlobalRef(env, obj);
diff --git a/android/trace_event_binding.cc b/android/trace_event_binding.cc index 3c5ee17..f761a64 100644 --- a/android/trace_event_binding.cc +++ b/android/trace_event_binding.cc
@@ -69,23 +69,26 @@ } // namespace -static void RegisterEnabledObserver(JNIEnv* env, jclass clazz) { +static void RegisterEnabledObserver(JNIEnv* env, + const JavaParamRef<jclass>& clazz) { bool enabled = trace_event::TraceLog::GetInstance()->IsEnabled(); base::android::Java_TraceEvent_setEnabled(env, enabled); trace_event::TraceLog::GetInstance()->AddEnabledStateObserver( g_trace_enabled_state_observer_.Pointer()); } -static void StartATrace(JNIEnv* env, jclass clazz) { +static void StartATrace(JNIEnv* env, const JavaParamRef<jclass>& clazz) { base::trace_event::TraceLog::GetInstance()->StartATrace(); } -static void StopATrace(JNIEnv* env, jclass clazz) { +static void StopATrace(JNIEnv* env, const JavaParamRef<jclass>& clazz) { base::trace_event::TraceLog::GetInstance()->StopATrace(); } -static void Instant(JNIEnv* env, jclass clazz, - jstring jname, jstring jarg) { +static void Instant(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jname, + const JavaParamRef<jstring>& jarg) { TraceEventDataConverter converter(env, jname, jarg); if (converter.arg()) { TRACE_EVENT_COPY_INSTANT1(kJavaCategory, converter.name(), @@ -97,8 +100,10 @@ } } -static void Begin(JNIEnv* env, jclass clazz, - jstring jname, jstring jarg) { +static void Begin(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jname, + const JavaParamRef<jstring>& jarg) { TraceEventDataConverter converter(env, jname, jarg); if (converter.arg()) { TRACE_EVENT_COPY_BEGIN1(kJavaCategory, converter.name(), @@ -108,8 +113,10 @@ } } -static void End(JNIEnv* env, jclass clazz, - jstring jname, jstring jarg) { +static void End(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jname, + const JavaParamRef<jstring>& jarg) { TraceEventDataConverter converter(env, jname, jarg); if (converter.arg()) { TRACE_EVENT_COPY_END1(kJavaCategory, converter.name(), @@ -119,20 +126,26 @@ } } -static void BeginToplevel(JNIEnv* env, jclass clazz) { +static void BeginToplevel(JNIEnv* env, const JavaParamRef<jclass>& clazz) { TRACE_EVENT_BEGIN0(kToplevelCategory, kLooperDispatchMessage); } -static void EndToplevel(JNIEnv* env, jclass clazz) { +static void EndToplevel(JNIEnv* env, const JavaParamRef<jclass>& clazz) { TRACE_EVENT_END0(kToplevelCategory, kLooperDispatchMessage); } -static void StartAsync(JNIEnv* env, jclass clazz, jstring jname, jlong jid) { +static void StartAsync(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jname, + jlong jid) { TraceEventDataConverter converter(env, jname, nullptr); TRACE_EVENT_COPY_ASYNC_BEGIN0(kJavaCategory, converter.name(), jid); } -static void FinishAsync(JNIEnv* env, jclass clazz, jstring jname, jlong jid) { +static void FinishAsync(JNIEnv* env, + const JavaParamRef<jclass>& clazz, + const JavaParamRef<jstring>& jname, + jlong jid) { TraceEventDataConverter converter(env, jname, nullptr); TRACE_EVENT_COPY_ASYNC_END0(kJavaCategory, converter.name(), jid); }
diff --git a/base_unittests.isolate b/base_unittests.isolate index c4c0fb2..208501f 100644 --- a/base_unittests.isolate +++ b/base_unittests.isolate
@@ -25,7 +25,6 @@ 'variables': { 'files': [ '../testing/test_env.py', - '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)', ], }, }],
diff --git a/feature_list.cc b/feature_list.cc new file mode 100644 index 0000000..16eba67 --- /dev/null +++ b/feature_list.cc
@@ -0,0 +1,113 @@ +// 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. + +#include "base/feature_list.h" + +#include <vector> + +#include "base/logging.h" +#include "base/strings/string_split.h" + +namespace base { + +namespace { + +// Pointer to the FeatureList instance singleton that was set via +// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to +// have more control over initialization timing. Leaky. +FeatureList* g_instance = nullptr; + +// Splits a comma-separated string containing feature names into a vector. +std::vector<std::string> SplitFeatureListString(const std::string& input) { + return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); +} + +} // namespace + +FeatureList::FeatureList() : initialized_(false) {} + +FeatureList::~FeatureList() {} + +void FeatureList::InitializeFromCommandLine( + const std::string& enable_features, + const std::string& disable_features) { + DCHECK(!initialized_); + + // Process disabled features first, so that disabled ones take precedence over + // enabled ones (since RegisterOverride() uses insert()). + for (const auto& feature_name : SplitFeatureListString(disable_features)) { + RegisterOverride(feature_name, OVERRIDE_DISABLE_FEATURE); + } + for (const auto& feature_name : SplitFeatureListString(enable_features)) { + RegisterOverride(feature_name, OVERRIDE_ENABLE_FEATURE); + } +} + +// static +bool FeatureList::IsEnabled(const Feature& feature) { + return GetInstance()->IsFeatureEnabled(feature); +} + +// static +FeatureList* FeatureList::GetInstance() { + return g_instance; +} + +// static +void FeatureList::SetInstance(scoped_ptr<FeatureList> instance) { + DCHECK(!g_instance); + instance->FinalizeInitialization(); + + // Note: Intentional leak of global singleton. + g_instance = instance.release(); +} + +// static +void FeatureList::ClearInstanceForTesting() { + delete g_instance; + g_instance = nullptr; +} + +void FeatureList::FinalizeInitialization() { + DCHECK(!initialized_); + initialized_ = true; +} + +bool FeatureList::IsFeatureEnabled(const Feature& feature) { + DCHECK(initialized_); + DCHECK(CheckFeatureIdentity(feature)) << feature.name; + + auto it = overrides_.find(feature.name); + if (it != overrides_.end()) { + const OverrideEntry& entry = it->second; + // TODO(asvitkine) Expand this section as more support is added. + return entry.overridden_state == OVERRIDE_ENABLE_FEATURE; + } + // Otherwise, return the default state. + return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; +} + +void FeatureList::RegisterOverride(const std::string& feature_name, + OverrideState overridden_state) { + DCHECK(!initialized_); + overrides_.insert(make_pair(feature_name, OverrideEntry(overridden_state))); +} + +bool FeatureList::CheckFeatureIdentity(const Feature& feature) { + AutoLock auto_lock(feature_identity_tracker_lock_); + + auto it = feature_identity_tracker_.find(feature.name); + if (it == feature_identity_tracker_.end()) { + // If it's not tracked yet, register it. + feature_identity_tracker_[feature.name] = &feature; + return true; + } + // Compare address of |feature| to the existing tracked entry. + return it->second == &feature; +} + +FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state) + : overridden_state(overridden_state) {} + +} // namespace base
diff --git a/feature_list.h b/feature_list.h new file mode 100644 index 0000000..6e4ad58 --- /dev/null +++ b/feature_list.h
@@ -0,0 +1,162 @@ +// 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. + +#ifndef BASE_FEATURE_LIST_H_ +#define BASE_FEATURE_LIST_H_ + +#include <map> +#include <string> + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" + +namespace base { + +// Specifies whether a given feature is enabled or disabled by default. +enum FeatureState { + FEATURE_DISABLED_BY_DEFAULT, + FEATURE_ENABLED_BY_DEFAULT, +}; + +// The Feature struct is used to define the default state for a feature. See +// comment below for more details. There must only ever be one struct instance +// for a given feature name - generally defined as a constant global variable or +// file static. +struct BASE_EXPORT Feature { + // The name of the feature. This should be unique to each feature and is used + // for enabling/disabling features via command line flags and experiments. + const char* const name; + + // The default state (i.e. enabled or disabled) for this feature. + const FeatureState default_state; +}; + +// The FeatureList class is used to determine whether a given feature is on or +// off. It provides an authoritative answer, taking into account command-line +// overrides and experimental control. +// +// The basic use case is for any feature that can be toggled (e.g. through +// command-line or an experiment) to have a defined Feature struct, e.g.: +// +// struct base::Feature kMyGreatFeature { +// "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT +// }; +// +// Then, client code that wishes to query the state of the feature would check: +// +// if (base::FeatureList::IsEnabled(kMyGreatFeature)) { +// // Feature code goes here. +// } +// +// Behind the scenes, the above call would take into account any command-line +// flags to enable or disable the feature, any experiments that may control it +// and finally its default state (in that order of priority), to determine +// whether the feature is on. +// +// Features can be explicitly forced on or off by specifying a list of comma- +// separated feature names via the following command-line flags: +// +// --enable-features=Feature5,Feature7 +// --disable-features=Feature1,Feature2,Feature3 +// +// After initialization (which should be done single-threaded), the FeatureList +// API is thread safe. +// +// Note: This class is a singleton, but does not use base/memory/singleton.h in +// order to have control over its initialization sequence. Specifically, the +// intended use is to create an instance of this class and fully initialize it, +// before setting it as the singleton for a process, via SetInstance(). +class BASE_EXPORT FeatureList { + public: + FeatureList(); + ~FeatureList(); + + // Initializes feature overrides via command-line flags |enable_features| and + // |disable_features|, each of which is a comma-separated list of features to + // enable or disable, respectively. If a feature appears on both lists, then + // it will be disabled. Must only be invoked during the initialization phase + // (before FinalizeInitialization() has been called). + void InitializeFromCommandLine(const std::string& enable_features, + const std::string& disable_features); + + // Returns whether the given |feature| is enabled. Must only be called after + // the singleton instance has been registered via SetInstance(). Additionally, + // a feature with a given name must only have a single corresponding Feature + // struct, which is checked in builds with DCHECKs enabled. + static bool IsEnabled(const Feature& feature); + + // Returns the singleton instance of FeatureList. Will return null until an + // instance is registered via SetInstance(). + static FeatureList* GetInstance(); + + // Registers the given |instance| to be the singleton feature list for this + // process. This should only be called once and |instance| must not be null. + static void SetInstance(scoped_ptr<FeatureList> instance); + + // Clears the previously-registered singleton instance for tests. + static void ClearInstanceForTesting(); + + private: + FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity); + + // Specifies whether a feature override enables or disables the feature. + enum OverrideState { + OVERRIDE_DISABLE_FEATURE, + OVERRIDE_ENABLE_FEATURE, + }; + + // Finalizes the initialization state of the FeatureList, so that no further + // overrides can be registered. This is called by SetInstance() on the + // singleton feature list that is being registered. + void FinalizeInitialization(); + + // Returns whether the given |feature| is enabled. This is invoked by the + // public FeatureList::IsEnabled() static function on the global singleton. + // Requires the FeatureList to have already been fully initialized. + bool IsFeatureEnabled(const Feature& feature); + + // Registers an override for feature |feature_name|. The override specifies + // whether the feature should be on or off (via |overridden_state|), which + // will take precedence over the feature's default state. + void RegisterOverride(const std::string& feature_name, + OverrideState overridden_state); + + // Verifies that there's only a single definition of a Feature struct for a + // given feature name. Keeps track of the first seen Feature struct for each + // feature. Returns false when called on a Feature struct with a different + // address than the first one it saw for that feature name. Used only from + // DCHECKs and tests. + bool CheckFeatureIdentity(const Feature& feature); + + struct OverrideEntry { + // The overridden enable (on/off) state of the feature. + const OverrideState overridden_state; + + // TODO(asvitkine): Expand this as more support is added. + + explicit OverrideEntry(OverrideState overridden_state); + }; + // Map from feature name to an OverrideEntry struct for the feature, if it + // exists. + std::map<std::string, OverrideEntry> overrides_; + + // Locked map that keeps track of seen features, to ensure a single feature is + // only defined once. This verification is only done in builds with DCHECKs + // enabled. + Lock feature_identity_tracker_lock_; + std::map<std::string, const Feature*> feature_identity_tracker_; + + // Whether this object has been fully initialized. This gets set to true as a + // result of FinalizeInitialization(). + bool initialized_; + + DISALLOW_COPY_AND_ASSIGN(FeatureList); +}; + +} // namespace base + +#endif // BASE_FEATURE_LIST_H_
diff --git a/feature_list_unittest.cc b/feature_list_unittest.cc new file mode 100644 index 0000000..c9423ce --- /dev/null +++ b/feature_list_unittest.cc
@@ -0,0 +1,108 @@ +// 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. + +#include "base/feature_list.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +const char kFeatureOnByDefaultName[] = "OnByDefault"; +struct Feature kFeatureOnByDefault { + kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT +}; + +const char kFeatureOffByDefaultName[] = "OffByDefault"; +struct Feature kFeatureOffByDefault { + kFeatureOffByDefaultName, FEATURE_DISABLED_BY_DEFAULT +}; + +} // namespace + +class FeatureListTest : public testing::Test { + public: + FeatureListTest() : feature_list_(nullptr) { + RegisterFeatureListInstance(make_scoped_ptr(new FeatureList)); + } + ~FeatureListTest() override { ClearFeatureListInstance(); } + + void RegisterFeatureListInstance(scoped_ptr<FeatureList> feature_list) { + feature_list_ = feature_list.get(); + FeatureList::SetInstance(feature_list.Pass()); + } + void ClearFeatureListInstance() { + FeatureList::ClearInstanceForTesting(); + feature_list_ = nullptr; + } + + FeatureList* feature_list() { return feature_list_; } + + private: + // Weak. Owned by the FeatureList::SetInstance(). + FeatureList* feature_list_; + + DISALLOW_COPY_AND_ASSIGN(FeatureListTest); +}; + +TEST_F(FeatureListTest, DefaultStates) { + EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); + EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); +} + +TEST_F(FeatureListTest, InitializeFromCommandLine) { + struct { + const char* enable_features; + const char* disable_features; + bool expected_feature_on_state; + bool expected_feature_off_state; + } test_cases[] = { + {"", "", true, false}, + {"OffByDefault", "", true, true}, + {"OffByDefault", "OnByDefault", false, true}, + {"OnByDefault,OffByDefault", "", true, true}, + {"", "OnByDefault,OffByDefault", false, false}, + // In the case an entry is both, disable takes precedence. + {"OnByDefault", "OnByDefault,OffByDefault", false, false}, + }; + + for (size_t i = 0; i < arraysize(test_cases); ++i) { + const auto& test_case = test_cases[i]; + + ClearFeatureListInstance(); + scoped_ptr<FeatureList> feature_list(new FeatureList); + feature_list->InitializeFromCommandLine(test_case.enable_features, + test_case.disable_features); + RegisterFeatureListInstance(feature_list.Pass()); + + EXPECT_EQ(test_case.expected_feature_on_state, + FeatureList::IsEnabled(kFeatureOnByDefault)) + << i; + EXPECT_EQ(test_case.expected_feature_off_state, + FeatureList::IsEnabled(kFeatureOffByDefault)) + << i; + } +} + +TEST_F(FeatureListTest, CheckFeatureIdentity) { + // Tests that CheckFeatureIdentity() correctly detects when two different + // structs with the same feature name are passed to it. + + // Call it twice for each feature at the top of the file, since the first call + // makes it remember the entry and the second call will verify it. + EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); + EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); + EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); + EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); + + // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which + // should return false. + struct Feature kFeatureOnByDefault2 { + kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT + }; + EXPECT_FALSE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault2)); +} + +} // namespace base
diff --git a/json/string_escape.cc b/json/string_escape.cc index 469f9f9..f5d6a76 100644 --- a/json/string_escape.cc +++ b/json/string_escape.cc
@@ -59,6 +59,14 @@ case '<': dest->append("\\u003C"); break; + // Escape the "Line Separator" and "Paragraph Separator" characters, since + // they should be treated like a new line \r or \n. + case 0x2028: + dest->append("\\u2028"); + break; + case 0x2029: + dest->append("\\u2029"); + break; default: return false; }
diff --git a/json/string_escape_unittest.cc b/json/string_escape_unittest.cc index 100373f..738a657 100644 --- a/json/string_escape_unittest.cc +++ b/json/string_escape_unittest.cc
@@ -15,12 +15,13 @@ const char* to_escape; const char* escaped; } cases[] = { - {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, - {"a\b\f\n\r\t\v\1\\.\"z", - "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, - {"b\x0f\x7f\xf0\xff!", // \xf0\xff is not a valid UTF-8 unit. - "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"}, - {"c<>d", "c\\u003C>d"}, + {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, + {"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, + {"b\x0f\x7f\xf0\xff!", // \xf0\xff is not a valid UTF-8 unit. + "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"}, + {"c<>d", "c\\u003C>d"}, + {"Hello\xe2\x80\xa8world", "Hello\\u2028world"}, + {"\xe2\x80\xa9purple", "\\u2029purple"}, }; for (size_t i = 0; i < arraysize(cases); ++i) { @@ -73,12 +74,13 @@ const wchar_t* to_escape; const char* escaped; } cases[] = { - {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"}, - {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, - {L"a\b\f\n\r\t\v\1\\.\"z", - "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, - {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"}, - {L"c<>d", "c\\u003C>d"}, + {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"}, + {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, + {L"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, + {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"}, + {L"c<>d", "c\\u003C>d"}, + {L"Hello\u2028world", "Hello\\u2028world"}, + {L"\u2029purple", "\\u2029purple"}, }; for (size_t i = 0; i < arraysize(cases); ++i) {
diff --git a/logging.cc b/logging.cc index 97ee134..c6992b9 100644 --- a/logging.cc +++ b/logging.cc
@@ -545,8 +545,8 @@ LogMessage::~LogMessage() { #if !defined(OFFICIAL_BUILD) && !defined(OS_NACL) && !defined(__UCLIBC__) && \ !defined(FNL_MUSL) - if (severity_ == LOG_FATAL) { - // Include a stack trace on a fatal. + if (severity_ == LOG_FATAL && !base::debug::BeingDebugged()) { + // Include a stack trace on a fatal, unless a debugger is attached. base::debug::StackTrace trace; stream_ << std::endl; // Newline to separate from log message. trace.OutputToStream(&stream_); @@ -640,7 +640,11 @@ // information, and displaying message boxes when the application is // hosed can cause additional problems. #ifndef NDEBUG - DisplayDebugMessageInDialog(stream_.str()); + if (!base::debug::BeingDebugged()) { + // Displaying a dialog is unnecessary when debugging and can complicate + // debugging. + DisplayDebugMessageInDialog(stream_.str()); + } #endif // Crash the process to generate a dump. base::debug::BreakDebugger();
diff --git a/mac/foundation_util.h b/mac/foundation_util.h index 353ed7c..6e8505d 100644 --- a/mac/foundation_util.h +++ b/mac/foundation_util.h
@@ -373,6 +373,14 @@ // Converts |str| to a FilePath. Returns an empty path if |str| is nil. BASE_EXPORT FilePath NSStringToFilePath(NSString* str); +#if defined(__OBJC__) +// Converts |range| to an NSRange, returning the new range in |range_out|. +// Returns true if conversion was successful, false if the values of |range| +// could not be converted to NSUIntegers. +BASE_EXPORT bool CFRangeToNSRange(CFRange range, + NSRange* range_out) WARN_UNUSED_RESULT; +#endif // defined(__OBJC__) + } // namespace mac } // namespace base
diff --git a/mac/foundation_util.mm b/mac/foundation_util.mm index 27d6e7c..bd5d514 100644 --- a/mac/foundation_util.mm +++ b/mac/foundation_util.mm
@@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/mac/bundle_locations.h" #include "base/mac/mac_logging.h" +#include "base/numerics/safe_conversions.h" #include "base/strings/sys_string_conversions.h" #if !defined(OS_IOS) @@ -430,6 +431,19 @@ return FilePath([str fileSystemRepresentation]); } +bool CFRangeToNSRange(CFRange range, NSRange* range_out) { + if (base::IsValueInRangeForNumericType<decltype(range_out->location)>( + range.location) && + base::IsValueInRangeForNumericType<decltype(range_out->length)>( + range.length) && + base::IsValueInRangeForNumericType<decltype(range_out->location)>( + range.location + range.length)) { + *range_out = NSMakeRange(range.location, range.length); + return true; + } + return false; +} + } // namespace mac } // namespace base
diff --git a/mac/foundation_util_unittest.mm b/mac/foundation_util_unittest.mm index e60a0f6..c688442 100644 --- a/mac/foundation_util_unittest.mm +++ b/mac/foundation_util_unittest.mm
@@ -317,6 +317,18 @@ EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b")); } +TEST(FoundationUtilTest, CFRangeToNSRange) { + NSRange range_out; + EXPECT_TRUE(CFRangeToNSRange(CFRangeMake(10, 5), &range_out)); + EXPECT_EQ(10UL, range_out.location); + EXPECT_EQ(5UL, range_out.length); + EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, 5), &range_out)); + EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(5, -1), &range_out)); + EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, -1), &range_out)); + EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MAX, LONG_MAX), &range_out)); + EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MIN, LONG_MAX), &range_out)); +} + TEST(StringNumberConversionsTest, FormatNSInteger) { // The PRI[dxu]NS macro assumes that NSInteger is a typedef to "int" on // 32-bit architecture and a typedef to "long" on 64-bit architecture
diff --git a/memory/BUILD.gn b/memory/BUILD.gn index 4250631..bd94bb2 100644 --- a/memory/BUILD.gn +++ b/memory/BUILD.gn
@@ -69,6 +69,15 @@ sources -= [ "shared_memory_posix.cc" ] } + if (is_ios) { + set_sources_assignment_filter([]) + sources += [ + "shared_memory_handle_mac.cc", + "shared_memory_mac.cc", + ] + set_sources_assignment_filter(sources_assignment_filter) + } + if (is_android) { deps = [ "//third_party/ashmem",
diff --git a/memory/memory_pressure_listener.cc b/memory/memory_pressure_listener.cc index 2a1be74..8071d37 100644 --- a/memory/memory_pressure_listener.cc +++ b/memory/memory_pressure_listener.cc
@@ -32,6 +32,10 @@ ObserverListThreadSafe<MemoryPressureListener>, LeakyLazyObserverListTraits> g_observers = LAZY_INSTANCE_INITIALIZER; +// All memory pressure notifications within this process will be suppressed if +// this variable is set to 1. +subtle::Atomic32 g_notifications_suppressed = 0; + } // namespace MemoryPressureListener::MemoryPressureListener( @@ -54,8 +58,20 @@ DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE); TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure", "level", memory_pressure_level); + if (AreNotificationsSuppressed()) + return; g_observers.Get().Notify(FROM_HERE, &MemoryPressureListener::Notify, memory_pressure_level); } +// static +bool MemoryPressureListener::AreNotificationsSuppressed() { + return subtle::Acquire_Load(&g_notifications_suppressed) == 1; +} + +// static +void MemoryPressureListener::SetNotificationsSuppressed(bool suppress) { + subtle::Release_Store(&g_notifications_suppressed, suppress ? 1 : 0); +} + } // namespace base
diff --git a/memory/memory_pressure_listener.h b/memory/memory_pressure_listener.h index 6adaeee..83d47d7 100644 --- a/memory/memory_pressure_listener.h +++ b/memory/memory_pressure_listener.h
@@ -72,6 +72,12 @@ // Intended for use by the platform specific implementation. static void NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level); + // These methods should not be used anywhere else but in memory measurement + // code, where they are intended to maintain stable conditions across + // measurements. + static bool AreNotificationsSuppressed(); + static void SetNotificationsSuppressed(bool suppressed); + private: void Notify(MemoryPressureLevel memory_pressure_level);
diff --git a/memory/memory_pressure_listener_unittest.cc b/memory/memory_pressure_listener_unittest.cc new file mode 100644 index 0000000..1bcab2f --- /dev/null +++ b/memory/memory_pressure_listener_unittest.cc
@@ -0,0 +1,52 @@ +// 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. + +#include "base/memory/memory_pressure_listener.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace base { + +using MockCallback = + testing::MockFunction<void(MemoryPressureListener::MemoryPressureLevel)>; + +TEST(MemoryPressureListenerTest, NotifyMemoryPressure) { + MessageLoopForUI message_loop; + MockCallback callback; + scoped_ptr<MemoryPressureListener> listener(new MemoryPressureListener( + Bind(&MockCallback::Call, Unretained(&callback)))); + + // Memory pressure notifications are not suppressed by default. + EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed()); + EXPECT_CALL(callback, + Call(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE)) + .Times(1); + MemoryPressureListener::NotifyMemoryPressure( + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); + message_loop.RunUntilIdle(); + + // Enable suppressing memory pressure notifications. + MemoryPressureListener::SetNotificationsSuppressed(true); + EXPECT_TRUE(MemoryPressureListener::AreNotificationsSuppressed()); + EXPECT_CALL(callback, Call(testing::_)).Times(0); + MemoryPressureListener::NotifyMemoryPressure( + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); + message_loop.RunUntilIdle(); + + // Disable suppressing memory pressure notifications. + MemoryPressureListener::SetNotificationsSuppressed(false); + EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed()); + EXPECT_CALL(callback, + Call(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)) + .Times(1); + MemoryPressureListener::NotifyMemoryPressure( + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); + message_loop.RunUntilIdle(); + + listener.reset(); +} + +} // namespace base
diff --git a/memory/shared_memory_handle.h b/memory/shared_memory_handle.h index 7af8729..01a7f75 100644 --- a/memory/shared_memory_handle.h +++ b/memory/shared_memory_handle.h
@@ -9,7 +9,7 @@ #if defined(OS_WIN) #include <windows.h> -#elif defined(OS_MACOSX) && !defined(OS_IOS) +#elif defined(OS_MACOSX) #include <sys/types.h> #include "base/base_export.h" #include "base/file_descriptor_posix.h" @@ -27,7 +27,7 @@ // the underlying OS handle to a shared memory segment. #if defined(OS_WIN) typedef HANDLE SharedMemoryHandle; -#elif defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS)) +#elif defined(OS_POSIX) && !defined(OS_MACOSX) typedef FileDescriptor SharedMemoryHandle; #else class BASE_EXPORT SharedMemoryHandle {
diff --git a/memory/shared_memory_handle_mac.cc b/memory/shared_memory_handle_mac.cc index 86f5c54..05e04d7 100644 --- a/memory/shared_memory_handle_mac.cc +++ b/memory/shared_memory_handle_mac.cc
@@ -8,7 +8,6 @@ #include "base/posix/eintr_wrapper.h" -#if defined(OS_MACOSX) && !defined(OS_IOS) namespace base { static_assert(sizeof(SharedMemoryHandle::Type) <= @@ -83,4 +82,3 @@ } } // namespace base -#endif // defined(OS_MACOSX) && !defined(OS_IOS)
diff --git a/message_loop/message_pump_android.cc b/message_loop/message_pump_android.cc index babd17b..a0eee12 100644 --- a/message_loop/message_pump_android.cc +++ b/message_loop/message_pump_android.cc
@@ -21,8 +21,10 @@ // ---------------------------------------------------------------------------- // This method can not move to anonymous namespace as it has been declared as // 'static' in system_message_handler_jni.h. -static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate, - jlong delayed_scheduled_time_ticks) { +static void DoRunLoopOnce(JNIEnv* env, + const JavaParamRef<jobject>& obj, + jlong native_delegate, + jlong delayed_scheduled_time_ticks) { base::MessagePump::Delegate* delegate = reinterpret_cast<base::MessagePump::Delegate*>(native_delegate); DCHECK(delegate);
diff --git a/pickle.cc b/pickle.cc index d4487d4..7c8ffed 100644 --- a/pickle.cc +++ b/pickle.cc
@@ -254,9 +254,8 @@ header_size_(other.header_size_), capacity_after_header_(0), write_offset_(other.write_offset_) { - size_t payload_size = header_size_ + other.header_->payload_size; - Resize(payload_size); - memcpy(header_, other.header_, payload_size); + Resize(other.header_->payload_size); + memcpy(header_, other.header_, header_size_ + other.header_->payload_size); } Pickle::~Pickle() {
diff --git a/pickle.h b/pickle.h index 40a983b..b6ec116 100644 --- a/pickle.h +++ b/pickle.h
@@ -296,6 +296,7 @@ } inline void WriteBytesCommon(const void* data, size_t length); + FRIEND_TEST_ALL_PREFIXES(PickleTest, DeepCopyResize); FRIEND_TEST_ALL_PREFIXES(PickleTest, Resize); FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNext); FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextWithIncompleteHeader);
diff --git a/pickle_unittest.cc b/pickle_unittest.cc index b0a8f21..6f9fcc7 100644 --- a/pickle_unittest.cc +++ b/pickle_unittest.cc
@@ -428,4 +428,18 @@ EXPECT_EQ(data, outdata); } +// Checks that when a pickle is deep-copied, the result is not larger than +// needed. +TEST(PickleTest, DeepCopyResize) { + Pickle pickle; + while (pickle.capacity_after_header() != pickle.payload_size()) + pickle.WriteBool(true); + + // Make a deep copy. + Pickle pickle2(pickle); + + // Check that there isn't any extraneous capacity. + EXPECT_EQ(pickle.capacity_after_header(), pickle2.capacity_after_header()); +} + } // namespace base
diff --git a/power_monitor/power_monitor_device_source_android.cc b/power_monitor/power_monitor_device_source_android.cc index 4d9eb52..9671c30 100644 --- a/power_monitor/power_monitor_device_source_android.cc +++ b/power_monitor/power_monitor_device_source_android.cc
@@ -19,15 +19,15 @@ namespace android { // Native implementation of PowerMonitor.java. -void OnBatteryChargingChanged(JNIEnv* env, jclass clazz) { +void OnBatteryChargingChanged(JNIEnv* env, const JavaParamRef<jclass>& clazz) { ProcessPowerEventHelper(PowerMonitorSource::POWER_STATE_EVENT); } -void OnMainActivityResumed(JNIEnv* env, jclass clazz) { +void OnMainActivityResumed(JNIEnv* env, const JavaParamRef<jclass>& clazz) { ProcessPowerEventHelper(PowerMonitorSource::RESUME_EVENT); } -void OnMainActivitySuspended(JNIEnv* env, jclass clazz) { +void OnMainActivitySuspended(JNIEnv* env, const JavaParamRef<jclass>& clazz) { ProcessPowerEventHelper(PowerMonitorSource::SUSPEND_EVENT); }
diff --git a/process/kill.h b/process/kill.h index dbd32e1..b6873e6 100644 --- a/process/kill.h +++ b/process/kill.h
@@ -21,11 +21,11 @@ // exit code arguments to KillProcess*(), use platform/application // specific values instead. enum TerminationStatus { - TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status - TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status - TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill - TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault - TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet + TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status + TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status + TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill + TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault + TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet #if defined(OS_CHROMEOS) // Used for the case when oom-killer kills a process on ChromeOS. TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM, @@ -35,8 +35,9 @@ // the termination status. We can't know if the termination was a crash or an // oom kill for sure, but we can use status of the strong process bindings as // a hint. - TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill + TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill #endif + TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched TERMINATION_STATUS_MAX_ENUM };
diff --git a/synchronization/condition_variable_posix.cc b/synchronization/condition_variable_posix.cc index 013284c..c9a2ec4 100644 --- a/synchronization/condition_variable_posix.cc +++ b/synchronization/condition_variable_posix.cc
@@ -42,6 +42,20 @@ } ConditionVariable::~ConditionVariable() { +#if defined(OS_MACOSX) + // This hack is necessary to avoid a fatal pthreads subsystem bug in the + // Darwin kernel. http://crbug.com/517681. + { + base::Lock lock; + base::AutoLock l(lock); + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1; + pthread_cond_timedwait_relative_np(&condition_, lock.lock_.native_handle(), + &ts); + } +#endif + int rv = pthread_cond_destroy(&condition_); DCHECK_EQ(0, rv); }
diff --git a/trace_event/memory_dump_manager.cc b/trace_event/memory_dump_manager.cc index f3739cb..71a9dae 100644 --- a/trace_event/memory_dump_manager.cc +++ b/trace_event/memory_dump_manager.cc
@@ -38,23 +38,27 @@ namespace { -// Throttle mmaps at a rate of once every kHeavyMmapsDumpsRate standard dumps. -const int kHeavyDumpsRate = 8; // 250 ms * 8 = 2000 ms. -const int kDumpIntervalMs = 250; const int kTraceEventNumArgs = 1; const char* kTraceEventArgNames[] = {"dumps"}; const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE}; StaticAtomicSequenceNumber g_next_guid; uint32 g_periodic_dumps_count = 0; +uint32 g_heavy_dumps_rate = 0; MemoryDumpManager* g_instance_for_testing = nullptr; void RequestPeriodicGlobalDump() { - MemoryDumpArgs::LevelOfDetail dump_level_of_detail = - g_periodic_dumps_count == 0 ? MemoryDumpArgs::LevelOfDetail::HIGH - : MemoryDumpArgs::LevelOfDetail::LOW; - if (++g_periodic_dumps_count == kHeavyDumpsRate) - g_periodic_dumps_count = 0; + MemoryDumpArgs::LevelOfDetail dump_level_of_detail; + if (g_heavy_dumps_rate == 0) { + dump_level_of_detail = MemoryDumpArgs::LevelOfDetail::LOW; + } else { + dump_level_of_detail = g_periodic_dumps_count == 0 + ? MemoryDumpArgs::LevelOfDetail::HIGH + : MemoryDumpArgs::LevelOfDetail::LOW; + + if (++g_periodic_dumps_count == g_heavy_dumps_rate) + g_periodic_dumps_count = 0; + } MemoryDumpArgs dump_args = {dump_level_of_detail}; MemoryDumpManager::GetInstance()->RequestGlobalDump( @@ -116,14 +120,7 @@ #endif #if (defined(OS_LINUX) && !defined(FNL_MUSL)) || defined(OS_ANDROID) - // The memory maps dump provider is currently disabled for security reasons - // and will be enabled once tracing is more secure (crbug.com/517906). - // It is still enabled for running benchmarks. - if (CommandLine::ForCurrentProcess()->HasSwitch( - "enable-memory-benchmarking")) { - RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance()); - } - + RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance()); RegisterDumpProvider(MallocDumpProvider::GetInstance()); system_allocator_pool_name_ = MallocDumpProvider::kAllocatedObjects; #endif @@ -419,18 +416,44 @@ subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); // TODO(primiano): This is a temporary hack to disable periodic memory dumps - // when running memory benchmarks until they can be enabled/disabled in - // base::trace_event::TraceConfig. See https://goo.gl/5Hj3o0. + // when running memory benchmarks until telemetry uses TraceConfig to + // enable/disable periodic dumps. // The same mechanism should be used to disable periodic dumps in tests. - if (delegate_->IsCoordinatorProcess() && - !CommandLine::ForCurrentProcess()->HasSwitch( - "enable-memory-benchmarking") && - !disable_periodic_dumps_for_testing_) { - g_periodic_dumps_count = 0; - periodic_dump_timer_.Start(FROM_HERE, - TimeDelta::FromMilliseconds(kDumpIntervalMs), - base::Bind(&RequestPeriodicGlobalDump)); + if (!delegate_->IsCoordinatorProcess() || + CommandLine::ForCurrentProcess()->HasSwitch( + "enable-memory-benchmarking") || + disable_periodic_dumps_for_testing_) { + return; } + + // Enable periodic dumps. At the moment the periodic support is limited to at + // most one low-detail periodic dump and at most one high-detail periodic + // dump. If both are specified the high-detail period must be an integer + // multiple of the low-level one. + g_periodic_dumps_count = 0; + const TraceConfig trace_config = + TraceLog::GetInstance()->GetCurrentTraceConfig(); + const TraceConfig::MemoryDumpConfig& config_list = + trace_config.memory_dump_config(); + if (config_list.empty()) + return; + + uint32 min_timer_period_ms = std::numeric_limits<uint32>::max(); + uint32 heavy_dump_period_ms = 0; + DCHECK_LE(config_list.size(), 2u); + for (const TraceConfig::MemoryDumpTriggerConfig& config : config_list) { + DCHECK(config.periodic_interval_ms); + if (config.level_of_detail == MemoryDumpArgs::LevelOfDetail::HIGH) + heavy_dump_period_ms = config.periodic_interval_ms; + min_timer_period_ms = + std::min(min_timer_period_ms, config.periodic_interval_ms); + } + DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms); + g_heavy_dumps_rate = heavy_dump_period_ms / min_timer_period_ms; + + periodic_dump_timer_.Start(FROM_HERE, + TimeDelta::FromMilliseconds(min_timer_period_ms), + base::Bind(&RequestPeriodicGlobalDump)); } void MemoryDumpManager::OnTraceLogDisabled() {
diff --git a/trace_event/memory_dump_manager_unittest.cc b/trace_event/memory_dump_manager_unittest.cc index 4d0372f..6200c2d 100644 --- a/trace_event/memory_dump_manager_unittest.cc +++ b/trace_event/memory_dump_manager_unittest.cc
@@ -25,8 +25,8 @@ namespace base { namespace trace_event { namespace { -MemoryDumpArgs high_detail_args = {MemoryDumpArgs::LevelOfDetail::HIGH}; -MemoryDumpArgs low_detail_args = {MemoryDumpArgs::LevelOfDetail::LOW}; +MemoryDumpArgs g_high_detail_args = {MemoryDumpArgs::LevelOfDetail::HIGH}; +MemoryDumpArgs g_low_detail_args = {MemoryDumpArgs::LevelOfDetail::LOW}; } // Testing MemoryDumpManagerDelegate which short-circuits dump requests locally @@ -44,6 +44,16 @@ } }; +class MemoryDumpManagerDelegateForPeriodicDumpTest + : public MemoryDumpManagerDelegateForTesting { + public: + MOCK_METHOD2(RequestGlobalMemoryDump, + void(const MemoryDumpRequestArgs& args, + const MemoryDumpCallback& callback)); + + virtual bool IsCoordinatorProcess() const { return true; } +}; + class MemoryDumpManagerTest : public testing::Test { public: void SetUp() override { @@ -53,7 +63,6 @@ MemoryDumpManager::SetInstanceForTesting(mdm_.get()); ASSERT_EQ(mdm_, MemoryDumpManager::GetInstance()); MemoryDumpManager::GetInstance()->Initialize(); - MemoryDumpManager::GetInstance()->SetDelegate(&delegate_); } void TearDown() override { @@ -72,19 +81,35 @@ } protected: + void SetDelegate(scoped_ptr<MemoryDumpManagerDelegateForTesting> delegate) { + delegate_ = delegate.Pass(); + MemoryDumpManager::GetInstance()->SetDelegate(delegate_.get()); + } + + // This enalbes tracing using the legacy category filter string. void EnableTracing(const char* category) { + if (!delegate_) { + delegate_.reset(new MemoryDumpManagerDelegateForTesting()); + MemoryDumpManager::GetInstance()->SetDelegate(delegate_.get()); + } TraceLog::GetInstance()->SetEnabled( TraceConfig(category, ""), TraceLog::RECORDING_MODE); } + void EnableTracingWithTraceConfig(const char* trace_config) { + DCHECK(delegate_); + TraceConfig tc(trace_config); + TraceLog::GetInstance()->SetEnabled(tc, TraceLog::RECORDING_MODE); + } + void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); } scoped_ptr<MemoryDumpManager> mdm_; bool last_callback_success_; + scoped_ptr<MemoryDumpManagerDelegateForTesting> delegate_; private: scoped_ptr<MessageLoop> message_loop_; - MemoryDumpManagerDelegateForTesting delegate_; // We want our singleton torn down after each test. ShadowingAtExitManager at_exit_manager_; @@ -170,7 +195,7 @@ EnableTracing("foo-and-bar-but-not-memory"); EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); // Now repeat enabling the memory category and check that the dumper is @@ -179,7 +204,7 @@ EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3).WillRepeatedly(Return(true)); for (int i = 0; i < 3; ++i) mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); mdm_->UnregisterDumpProvider(&mdp); @@ -188,7 +213,7 @@ EnableTracing(MemoryDumpManager::kTraceCategory); EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); TraceLog::GetInstance()->SetDisabled(); } @@ -205,7 +230,7 @@ Invoke(&mdp_high_detail, &MockDumpProvider::OnMemoryDump_CheckMemoryDumpArgs)); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); mdm_->UnregisterDumpProvider(&mdp_high_detail); @@ -221,7 +246,7 @@ Invoke(&mdp_low_detail, &MockDumpProvider::OnMemoryDump_CheckMemoryDumpArgs)); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - low_detail_args); + g_low_detail_args); DisableTracing(); mdm_->UnregisterDumpProvider(&mdp_low_detail); } @@ -244,7 +269,7 @@ for (int i = 0; i < 2; ++i) mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); } @@ -259,7 +284,7 @@ EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true)); EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); // Invert: enable mdp1 and disable mdp2. @@ -269,7 +294,7 @@ EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0); EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true)); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); // Enable both mdp1 and mdp2. @@ -278,7 +303,7 @@ EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true)); EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true)); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); } @@ -293,7 +318,7 @@ EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1); EnableTracing(MemoryDumpManager::kTraceCategory); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); } @@ -303,7 +328,7 @@ EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); EnableTracing(MemoryDumpManager::kTraceCategory); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); } @@ -314,7 +339,7 @@ EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); EnableTracing(MemoryDumpManager::kTraceCategory); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); } @@ -326,7 +351,7 @@ EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1); EnableTracing(MemoryDumpManager::kTraceCategory); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); DisableTracing(); } } @@ -366,7 +391,7 @@ Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this), MessageLoop::current()->task_runner(), run_loop.QuitClosure()); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args, callback); + g_high_detail_args, callback); // This nested message loop (|run_loop|) will be quit if and only if // the RequestGlobalDump callback is invoked. run_loop.Run(); @@ -414,7 +439,7 @@ for (int i = 0; i < 1 + MemoryDumpManager::kMaxConsecutiveFailuresCount; i++) { mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); } DisableTracing(); @@ -445,7 +470,7 @@ for (int i = 0; i < 4; i++) { mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); } DisableTracing(); @@ -476,7 +501,7 @@ for (int i = 0; i < 4; i++) { mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args); + g_high_detail_args); } DisableTracing(); @@ -526,7 +551,7 @@ EnableTracing(MemoryDumpManager::kTraceCategory); MemoryDumpRequestArgs request_args = {0, MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args}; + g_high_detail_args}; mdm_->CreateProcessDump(request_args, callback); run_loop.Run(); @@ -552,11 +577,65 @@ Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this), MessageLoop::current()->task_runner(), run_loop.QuitClosure()); mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - high_detail_args, callback); + g_high_detail_args, callback); run_loop.Run(); } EXPECT_FALSE(last_callback_success_); } +MATCHER(IsHighDetail, "") { + return arg.dump_args.level_of_detail == MemoryDumpArgs::LevelOfDetail::HIGH; +} + +MATCHER(IsLowDetail, "") { + return arg.dump_args.level_of_detail == MemoryDumpArgs::LevelOfDetail::LOW; +} + +TEST_F(MemoryDumpManagerTest, SchedulePeriodicDumpsFromTraceConfig) { + const char kMemoryDumpTraceConfigString[] = + "{" + "\"included_categories\":[" + "\"disabled-by-default-memory-infra\"" + "]," + "\"memory_dump_config\":{" + "\"triggers\":[" + "{" + "\"mode\":\"light\"," + "\"periodic_interval_ms\":1" + "}," + "{" + "\"mode\":\"detailed\"," + "\"periodic_interval_ms\":3" + "}" + "]" + "}" + "}"; + + RunLoop run_loop; + scoped_ptr<MemoryDumpManagerDelegateForPeriodicDumpTest> delegate( + new MemoryDumpManagerDelegateForPeriodicDumpTest()); + + auto quit_closure = run_loop.QuitClosure(); + testing::InSequence sequence; + EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsHighDetail(), _)) + .Times(1); + EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsLowDetail(), _)) + .Times(2); + EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsHighDetail(), _)) + .Times(1); + EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsLowDetail(), _)) + .Times(1) + .WillOnce(Invoke([quit_closure](const MemoryDumpRequestArgs& args, + const MemoryDumpCallback& callback) { + TraceLog::GetInstance()->SetDisabled(); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure); + })); + + SetDelegate(delegate.Pass()); + EnableTracingWithTraceConfig(kMemoryDumpTraceConfigString); + + run_loop.Run(); +} + } // namespace trace_event } // namespace base