Commit 7130b4a4 authored by Samuel Huang's avatar Samuel Huang Committed by Commit Bot

[DFM] Add ModuleDescriptor.getLoadNativeOnGetImpl() to DFM natiive loading.

Previously DFM load (for native libraries / resources) takes place when
a DFM's Module.getImpl() is called. However, VR is an exception; its
Module.getImpl() is called early, before main library load (which is
needed by DFM load). A worked around was to defer loading at first, and
flush then in Module.doDeferredNativeRegistrations().

This CL cleans up the above by giving more control to each DFM to
specify whether to automatically load DFMs in Module.getImpl(). A DFM can
override the default (true) behavior to manage its own native loading,
which may take place on start-up, on first use, and/or on install.

This CL also makes the following changes to specific DFMs:
* VR: Disable load-on-getImpl, with explicit load added to
  VrShellDelegate.onNativeLibraryAvailable(), which gets called on
  start-up and on install.
* DevUI: Disables load-on-getImpl since Module.getImpl() doesn't get
  used anyway. Keep existing calls for load-on-use and load-on-install.

Other implementation details:
* Remove Module.doDeferredNativeRegistrations() and it list of deferred
  DFM loads.
* Add flag |load_native_on_get_impl| to be given in DFM target
  *_module_desc. This is compulsory iff DFM specifies native libraries
  or resources.
* Replace Module.loadNative() with Module.ensureNativeLoaded(), which
  is specific to a module (i.e., no longer static). The name emphasizes
  that repeated calls are simply ignored. It assumes that the DFM
  installed, and requires Chrome's native library to be loaded.
* Replace static map |Module.sInitializedModules| by per-module flag
  |mIsLoaded|.

Bug: 1048740
Change-Id: I2fa844239fc3d741d91d28f384b2bc75a712b574
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2044489
Commit-Queue: Samuel Huang <huangs@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#743321}
parent e1aa435b
...@@ -2072,6 +2072,7 @@ if (enable_vr) { ...@@ -2072,6 +2072,7 @@ if (enable_vr) {
# This is necessary since vr tests simulate a bundle build. # This is necessary since vr tests simulate a bundle build.
module_desc_java("vr_test_module_desc_java") { module_desc_java("vr_test_module_desc_java") {
module_name = "vr" module_name = "vr"
load_native_on_get_impl = false
} }
android_apk("vr_nfc_simulator_apk") { android_apk("vr_nfc_simulator_apk") {
......
...@@ -15,4 +15,8 @@ dev_ui_module_desc = { ...@@ -15,4 +15,8 @@ dev_ui_module_desc = {
"//chrome/android/features/dev_ui/internal/java/AndroidManifest.xml" "//chrome/android/features/dev_ui/internal/java/AndroidManifest.xml"
paks = [ "$root_gen_dir/chrome/dev_ui_resources.pak" ] paks = [ "$root_gen_dir/chrome/dev_ui_resources.pak" ]
pak_deps = [ "//chrome/browser/resources:dev_ui_paks" ] pak_deps = [ "//chrome/browser/resources:dev_ui_paks" ]
# DevUI DFM does not need to call Module.getImpl(), and manages loading on
# install and on use. Therefore disable auto-load on Module.getImpl().
load_native_on_get_impl = false
} }
...@@ -336,6 +336,7 @@ public class VrShellDelegate ...@@ -336,6 +336,7 @@ public class VrShellDelegate
* Called when the native library is first available. * Called when the native library is first available.
*/ */
public static void onNativeLibraryAvailable() { public static void onNativeLibraryAvailable() {
VrModule.ensureNativeLoaded();
VrModuleProvider.registerJni(); VrModuleProvider.registerJni();
VrShellDelegateJni.get().onLibraryAvailable(); VrShellDelegateJni.get().onLibraryAvailable();
} }
......
...@@ -13,4 +13,9 @@ vr_module_desc = { ...@@ -13,4 +13,9 @@ vr_module_desc = {
android_manifest = "//chrome/android/features/vr/java/AndroidManifest.xml" android_manifest = "//chrome/android/features/vr/java/AndroidManifest.xml"
native_deps = [ "//chrome/browser/vr:vr_ui" ] native_deps = [ "//chrome/browser/vr:vr_ui" ]
native_entrypoints = "//chrome/browser/vr/module_exports.lst" native_entrypoints = "//chrome/browser/vr/module_exports.lst"
# For VR, Module.getImpl() gets called before native library loads (required
# by native library load). Therefore disable auto-load on Module.getImpl();
# the VR DFM manages its own loading on start-up and on install.
load_native_on_get_impl = false
} }
...@@ -164,7 +164,6 @@ import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController; ...@@ -164,7 +164,6 @@ import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
import org.chromium.components.browser_ui.widget.textbubble.TextBubble; import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
import org.chromium.components.feature_engagement.EventConstants; import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.components.feature_engagement.Tracker; import org.chromium.components.feature_engagement.Tracker;
import org.chromium.components.module_installer.builder.Module;
import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.SelectionPopupController; import org.chromium.content_public.browser.SelectionPopupController;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
...@@ -733,11 +732,6 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -733,11 +732,6 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
IntentHandler.setTestIntentsEnabled( IntentHandler.setTestIntentsEnabled(
CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS)); CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS));
mIntentHandler = new IntentHandler(createIntentHandlerDelegate(), getPackageName()); mIntentHandler = new IntentHandler(createIntentHandlerDelegate(), getPackageName());
// This also ensures that subsequent native library and resource loading takes place
// immediately, needed by restored tabs that use DFM.
// TODO(https://crbug.com/1008162): Have the Module system register its own observer.
Module.doDeferredNativeRegistrations();
} }
@Override @Override
......
...@@ -23,6 +23,15 @@ template("chrome_bundle") { ...@@ -23,6 +23,15 @@ template("chrome_bundle") {
_extra_modules = [] _extra_modules = []
foreach(_module_desc, invoker.module_descs) { foreach(_module_desc, invoker.module_descs) {
assert(_package_id > 2, "Too many modules, ran out of package IDs!") assert(_package_id > 2, "Too many modules, ran out of package IDs!")
# Assert that |load_native_on_get_impl| is defined iff native libraries or
# resources are defined.
if (defined(_module_desc.native_deps) || defined(_module_desc.paks)) {
assert(defined(_module_desc.load_native_on_get_impl))
} else {
assert(!defined(_module_desc.load_native_on_get_impl))
}
chrome_feature_module( chrome_feature_module(
"${_bundle_target_name}__${_module_desc.name}_bundle_module") { "${_bundle_target_name}__${_module_desc.name}_bundle_module") {
forward_variables_from(invoker, forward_variables_from(invoker,
......
...@@ -78,6 +78,9 @@ template("chrome_feature_module") { ...@@ -78,6 +78,9 @@ template("chrome_feature_module") {
if (defined(_module_desc.pak_deps)) { if (defined(_module_desc.pak_deps)) {
paks = _module_desc.paks paks = _module_desc.paks
} }
if (defined(_module_desc.load_native_on_get_impl)) {
load_native_on_get_impl = _module_desc.load_native_on_get_impl
}
} }
android_app_bundle_module(target_name) { android_app_bundle_module(target_name) {
......
...@@ -50,8 +50,9 @@ void DevUiModuleProvider::InstallModule( ...@@ -50,8 +50,9 @@ void DevUiModuleProvider::InstallModule(
listener->j_listener()); listener->j_listener());
} }
void DevUiModuleProvider::LoadModule() { void DevUiModuleProvider::EnsureLoaded() {
Java_DevUiModuleProvider_loadModule(base::android::AttachCurrentThread()); Java_DevUiModuleProvider_ensureNativeLoaded(
base::android::AttachCurrentThread());
} }
DevUiModuleProvider::DevUiModuleProvider() = default; DevUiModuleProvider::DevUiModuleProvider() = default;
......
...@@ -28,7 +28,7 @@ class DevUiModuleProvider { ...@@ -28,7 +28,7 @@ class DevUiModuleProvider {
// Assuming that the DevUI module is installed, loads DevUI resources if not // Assuming that the DevUI module is installed, loads DevUI resources if not
// already loaded. Virtual to enable testing. // already loaded. Virtual to enable testing.
virtual void LoadModule(); virtual void EnsureLoaded();
protected: protected:
DevUiModuleProvider(); DevUiModuleProvider();
......
...@@ -20,8 +20,7 @@ public class DevUiModuleProvider { ...@@ -20,8 +20,7 @@ public class DevUiModuleProvider {
} }
@CalledByNative @CalledByNative
private static void loadModule() { private static void ensureNativeLoaded() {
// Native resource are loaded as side effect of first getImpl() call. DevUiModule.ensureNativeLoaded();
DevUiModule.getImpl();
} }
} }
...@@ -9,4 +9,5 @@ stack_unwinder_module_desc = { ...@@ -9,4 +9,5 @@ stack_unwinder_module_desc = {
android_manifest = "//chrome/android/modules/stack_unwinder/internal/java/AndroidManifest.xml" android_manifest = "//chrome/android/modules/stack_unwinder/internal/java/AndroidManifest.xml"
java_deps = [ "//chrome/android/modules/stack_unwinder/internal:java" ] java_deps = [ "//chrome/android/modules/stack_unwinder/internal:java" ]
native_deps = [ "//chrome/android/modules/stack_unwinder/internal:native" ] native_deps = [ "//chrome/android/modules/stack_unwinder/internal:native" ]
load_native_on_get_impl = false
} }
...@@ -19,4 +19,5 @@ test_dummy_module_desc = { ...@@ -19,4 +19,5 @@ test_dummy_module_desc = {
paks = [ "$root_gen_dir/chrome/test_dummy_resources.pak" ] paks = [ "$root_gen_dir/chrome/test_dummy_resources.pak" ]
pak_deps = pak_deps =
[ "//chrome/android/features/test_dummy/internal:resources_native" ] [ "//chrome/android/features/test_dummy/internal:resources_native" ]
load_native_on_get_impl = true
} }
...@@ -98,10 +98,8 @@ DevUiLoaderThrottle::MaybeCreateThrottleFor(content::NavigationHandle* handle) { ...@@ -98,10 +98,8 @@ DevUiLoaderThrottle::MaybeCreateThrottleFor(content::NavigationHandle* handle) {
if (!ShouldInstallDevUiDfm(handle->GetURL())) if (!ShouldInstallDevUiDfm(handle->GetURL()))
return nullptr; return nullptr;
// If module is already installed, ensure that it is loaded.
if (dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()) { if (dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()) {
// Synchronously load module (if not already loaded). dev_ui::DevUiModuleProvider::GetInstance()->EnsureLoaded();
dev_ui::DevUiModuleProvider::GetInstance()->LoadModule();
return nullptr; return nullptr;
} }
...@@ -132,7 +130,7 @@ DevUiLoaderThrottle::WillStartRequest() { ...@@ -132,7 +130,7 @@ DevUiLoaderThrottle::WillStartRequest() {
void DevUiLoaderThrottle::OnDevUiDfmInstallWithStatus(bool success) { void DevUiLoaderThrottle::OnDevUiDfmInstallWithStatus(bool success) {
if (success) { if (success) {
dev_ui::DevUiModuleProvider::GetInstance()->LoadModule(); dev_ui::DevUiModuleProvider::GetInstance()->EnsureLoaded();
Resume(); Resume();
} else { } else {
std::string html = BuildErrorPageHtml(); std::string html = BuildErrorPageHtml();
......
...@@ -50,7 +50,7 @@ class MockDevUiModuleProvider : public DevUiModuleProvider { ...@@ -50,7 +50,7 @@ class MockDevUiModuleProvider : public DevUiModuleProvider {
} }
// DevUiModuleProvider: // DevUiModuleProvider:
void LoadModule() override { is_loaded_ = true; } void EnsureLoaded() override { is_loaded_ = true; }
void Reset() { void Reset() {
is_installed_ = false; is_installed_ = false;
......
...@@ -10,14 +10,11 @@ import org.chromium.base.BundleUtils; ...@@ -10,14 +10,11 @@ import org.chromium.base.BundleUtils;
import org.chromium.base.StrictModeContext; import org.chromium.base.StrictModeContext;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.components.module_installer.engine.InstallEngine; import org.chromium.components.module_installer.engine.InstallEngine;
import org.chromium.components.module_installer.engine.InstallListener; import org.chromium.components.module_installer.engine.InstallListener;
import org.chromium.components.module_installer.util.Timer; import org.chromium.components.module_installer.util.Timer;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
/** /**
* Represents a feature module. Can be used to install the module, access its interface, etc. See * Represents a feature module. Can be used to install the module, access its interface, etc. See
* {@link ModuleInterface} for how to conveniently create an instance of the module class for a * {@link ModuleInterface} for how to conveniently create an instance of the module class for a
...@@ -27,27 +24,12 @@ import java.util.TreeSet; ...@@ -27,27 +24,12 @@ import java.util.TreeSet;
*/ */
@JNINamespace("module_installer") @JNINamespace("module_installer")
public class Module<T> { public class Module<T> {
private static final Set<String> sInitializedModules = new HashSet<>();
private static Set<String> sPendingNativeRegistrations = new TreeSet<>();
private final String mName; private final String mName;
private final Class<T> mInterfaceClass; private final Class<T> mInterfaceClass;
private final String mImplClassName; private final String mImplClassName;
private T mImpl; private T mImpl;
private InstallEngine mInstaller; private InstallEngine mInstaller;
private boolean mIsNativeLoaded;
/**
* To be called after the main native library has been loaded. Any module instances created
* before the native library is loaded have their native component queued for loading and
* registration. Calling this methed completes that process.
*/
public static void doDeferredNativeRegistrations() {
if (sPendingNativeRegistrations == null) return;
for (String name : sPendingNativeRegistrations) {
loadNative(name);
}
sPendingNativeRegistrations = null;
}
/** /**
* Instantiates a module. * Instantiates a module.
...@@ -113,33 +95,35 @@ public class Module<T> { ...@@ -113,33 +95,35 @@ public class Module<T> {
public T getImpl() { public T getImpl() {
try (Timer timer = new Timer()) { try (Timer timer = new Timer()) {
if (mImpl != null) return mImpl; if (mImpl != null) return mImpl;
assert isInstalled(); assert isInstalled();
// Load the module's native code and/or resources if they are present, and the Chrome
// native library itself has been loaded. ModuleDescriptor moduleDescriptor = loadModuleDescriptor(mName);
if (sPendingNativeRegistrations == null) { if (moduleDescriptor.getLoadNativeOnGetImpl()) {
loadNative(mName); // Load the module's native code and/or resources if they are present, and the
} else { // Chrome native library itself has been loaded.
// We have to defer native initialization because VR is calling getImpl in early ensureNativeLoaded();
// startup. As soon as VR stops doing that we want to deprecate deferred native
// initialization.
sPendingNativeRegistrations.add(mName);
} }
mImpl = mInterfaceClass.cast(instantiateReflectively(mImplClassName)); mImpl = mInterfaceClass.cast(instantiateReflectively(mImplClassName));
return mImpl; return mImpl;
} }
} }
private static void loadNative(String name) { /**
* Loads native libraries and/or resources if and only if this is not already done, assuming
* that the module is installed, and enableNativeLoad() has been called.
*/
public void ensureNativeLoaded() {
// Can only initialize native once per lifetime of Chrome. // Can only initialize native once per lifetime of Chrome.
if (sInitializedModules.contains(name)) return; if (mIsNativeLoaded) return;
ModuleDescriptor moduleDescriptor = loadModuleDescriptor(name); assert LibraryLoader.getInstance().isInitialized();
ModuleDescriptor moduleDescriptor = loadModuleDescriptor(mName);
String[] libraries = moduleDescriptor.getLibraries(); String[] libraries = moduleDescriptor.getLibraries();
String[] paks = moduleDescriptor.getPaks(); String[] paks = moduleDescriptor.getPaks();
if (libraries.length > 0 || paks.length > 0) { if (libraries.length > 0 || paks.length > 0) {
ModuleJni.get().loadNative(name, libraries, paks); ModuleJni.get().loadNative(mName, libraries, paks);
} }
sInitializedModules.add(name); mIsNativeLoaded = true;
} }
/** /**
...@@ -164,6 +148,11 @@ public class Module<T> { ...@@ -164,6 +148,11 @@ public class Module<T> {
public String[] getPaks() { public String[] getPaks() {
return new String[0]; return new String[0];
} }
@Override
public boolean getLoadNativeOnGetImpl() {
return false;
}
}; };
} }
......
...@@ -16,4 +16,8 @@ public interface ModuleDescriptor { ...@@ -16,4 +16,8 @@ public interface ModuleDescriptor {
* Returns the list of PAK resources files this module contains. * Returns the list of PAK resources files this module contains.
*/ */
String[] getPaks(); String[] getPaks();
/**
* Returns whether to auto-load native libraries / resources on getImpl().
*/
boolean getLoadNativeOnGetImpl();
} }
...@@ -107,6 +107,12 @@ public class ModuleInterfaceProcessor extends AbstractProcessor { ...@@ -107,6 +107,12 @@ public class ModuleInterfaceProcessor extends AbstractProcessor {
.addStatement("sModule.installDeferred()") .addStatement("sModule.installDeferred()")
.build(); .build();
MethodSpec ensureNativeLoaded = MethodSpec.methodBuilder("ensureNativeLoaded")
.returns(TypeName.VOID)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addStatement("sModule.ensureNativeLoaded()")
.build();
MethodSpec getImpl = MethodSpec.methodBuilder("getImpl") MethodSpec getImpl = MethodSpec.methodBuilder("getImpl")
.returns(interfaceClassName) .returns(interfaceClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
...@@ -136,6 +142,7 @@ public class ModuleInterfaceProcessor extends AbstractProcessor { ...@@ -136,6 +142,7 @@ public class ModuleInterfaceProcessor extends AbstractProcessor {
.addMethod(isInstalled) .addMethod(isInstalled)
.addMethod(install) .addMethod(install)
.addMethod(installDeferred) .addMethod(installDeferred)
.addMethod(ensureNativeLoaded)
.addMethod(getImpl) .addMethod(getImpl)
.addMethod(getInstallEngine) .addMethod(getInstallEngine)
.addMethod(setInstallEngine) .addMethod(setInstallEngine)
......
...@@ -18,6 +18,8 @@ import("//build/config/python.gni") ...@@ -18,6 +18,8 @@ import("//build/config/python.gni")
# requires at runtime. Will consider all transitively depended on # requires at runtime. Will consider all transitively depended on
# shared_libraries. # shared_libraries.
# paks: (Optional) PAK files going into the module. # paks: (Optional) PAK files going into the module.
# load_native_on_get_impl: (Optional) Whether the module's native libraries /
# resources are automatically loaded when Module.getImpl() is called.
template("module_desc_java") { template("module_desc_java") {
_target_name = target_name _target_name = target_name
...@@ -48,6 +50,12 @@ template("module_desc_java") { ...@@ -48,6 +50,12 @@ template("module_desc_java") {
"--output", "--output",
rebase_path(_srcjar, root_out_dir), rebase_path(_srcjar, root_out_dir),
] ]
if (defined(invoker.load_native_on_get_impl) &&
invoker.load_native_on_get_impl) {
args += [ "--load-native-on-get-impl" ]
}
if (defined(invoker.paks)) { if (defined(invoker.paks)) {
_rebased_paks = [] _rebased_paks = []
foreach(_pak, invoker.paks) { foreach(_pak, invoker.paks) {
......
...@@ -38,6 +38,11 @@ public class ModuleDescriptor_{MODULE} implements ModuleDescriptor {{ ...@@ -38,6 +38,11 @@ public class ModuleDescriptor_{MODULE} implements ModuleDescriptor {{
public String[] getPaks() {{ public String[] getPaks() {{
return PAKS; return PAKS;
}} }}
@Override
public boolean getLoadNativeOnGetImpl() {{
return {LOAD_NATIVE_ON_GET_IMPL};
}}
}} }}
""" """
...@@ -50,6 +55,9 @@ def main(): ...@@ -50,6 +55,9 @@ def main():
parser.add_argument('--paks', help='GN list of PAK file paths') parser.add_argument('--paks', help='GN list of PAK file paths')
parser.add_argument( parser.add_argument(
'--output', required=True, help='Path to the generated srcjar file.') '--output', required=True, help='Path to the generated srcjar file.')
parser.add_argument('--load-native-on-get-impl', action='store_true',
default=False,
help='Load module automatically on calling Module.getImpl().')
options = parser.parse_args(build_utils.ExpandFileArgs(sys.argv[1:])) options = parser.parse_args(build_utils.ExpandFileArgs(sys.argv[1:]))
options.libraries = build_utils.ParseGnList(options.libraries) options.libraries = build_utils.ParseGnList(options.libraries)
options.paks = build_utils.ParseGnList(options.paks) options.paks = build_utils.ParseGnList(options.paks)
...@@ -68,6 +76,8 @@ def main(): ...@@ -68,6 +76,8 @@ def main():
'MODULE': options.module, 'MODULE': options.module,
'LIBRARIES': ','.join(['"%s"' % l for l in libraries]), 'LIBRARIES': ','.join(['"%s"' % l for l in libraries]),
'PAKS': ','.join(['"%s"' % os.path.basename(p) for p in paks]), 'PAKS': ','.join(['"%s"' % os.path.basename(p) for p in paks]),
'LOAD_NATIVE_ON_GET_IMPL': (
'true' if options.load_native_on_get_impl else 'false'),
} }
with build_utils.AtomicOutput(options.output) as f: with build_utils.AtomicOutput(options.output) as f:
with zipfile.ZipFile(f.name, 'w') as srcjar_file: with zipfile.ZipFile(f.name, 'w') as srcjar_file:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment