Fix regression in Java->C++->JS ViewManagers interaction.

Reviewed By: bvaughn

Differential Revision: D5953937

fbshipit-source-id: 8bc5dd8a483054ab9830ab653f2a3b41cad6c791
This commit is contained in:
Dmitry Zakharov 2017-10-03 05:24:32 -07:00 committed by Facebook Github Bot
parent 6ba9ad8ece
commit 346af557c3
3 changed files with 112 additions and 38 deletions

View File

@ -52,6 +52,61 @@ function requireNativeComponent(
componentInterface?: ?ComponentInterface, componentInterface?: ?ComponentInterface,
extraConfig?: ?{nativeOnly?: Object}, extraConfig?: ?{nativeOnly?: Object},
): React$ComponentType<any> | string { ): React$ComponentType<any> | string {
function attachBubblingEventTypes(viewConfig) {
if (UIManager.genericBubblingEventTypes) {
viewConfig.bubblingEventTypes = merge(
viewConfig.bubblingEventTypes,
UIManager.genericBubblingEventTypes,
);
// As genericBubblingEventTypes do not change over time, and there's
// merge of all the events happening in Fiber, we need to pass
// genericBubblingEventTypes to Fiber only once. Therefore, we can delete
// it and forget about it.
delete UIManager.genericBubblingEventTypes;
}
}
function attachDirectEventTypes(viewConfig) {
if (UIManager.genericDirectEventTypes) {
viewConfig.directEventTypes = merge(
viewConfig.directEventTypes,
UIManager.genericDirectEventTypes,
);
// As genericDirectEventTypes do not change over time, and there's merge
// of all the events happening in Fiber, we need to pass genericDirectEventTypes
// to Fiber only once. Therefore, we can delete it and forget about it.
delete UIManager.genericDirectEventTypes;
}
}
function merge(destination: ?Object, source: ?Object): ?Object {
if (!source) {
return destination;
}
if (!destination) {
return source;
}
for (const key in source) {
if (!source.hasOwnProperty(key)) {
continue;
}
var sourceValue = source[key];
if (destination.hasOwnProperty(key)) {
const destinationValue = destination[key];
if (
typeof sourceValue === 'object' &&
typeof destinationValue === 'object'
) {
sourceValue = merge(destinationValue, sourceValue);
}
}
destination[key] = sourceValue;
}
return destination;
}
// Don't load the ViewConfig from UIManager until it's needed for rendering. // Don't load the ViewConfig from UIManager until it's needed for rendering.
// Lazy-loading this can help avoid Prepack deopts. // Lazy-loading this can help avoid Prepack deopts.
function getViewConfig() { function getViewConfig() {
@ -129,6 +184,9 @@ function requireNativeComponent(
); );
} }
attachBubblingEventTypes(viewConfig);
attachDirectEventTypes(viewConfig);
// Register this view's event types with the ReactNative renderer. // Register this view's event types with the ReactNative renderer.
// This enables view managers to be initialized lazily, improving perf, // This enables view managers to be initialized lazily, improving perf,
// While also enabling 3rd party components to define custom event types. // While also enabling 3rd party components to define custom event types.

View File

@ -32,6 +32,7 @@ import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ReactConstants;
import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener;
@ -114,6 +115,11 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
private int mBatchId = 0; private int mBatchId = 0;
// Defines if events were already exported to JS. We do not send them more
// than once as they are stored and mixed in with Fiber for every ViewManager
// on JS side.
private boolean mEventsWereSentToJS = false;
public UIManagerModule( public UIManagerModule(
ReactApplicationContext reactContext, ReactApplicationContext reactContext,
ViewManagerResolver viewManagerResolver, ViewManagerResolver viewManagerResolver,
@ -142,10 +148,8 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
super(reactContext); super(reactContext);
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext); DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
mEventDispatcher = new EventDispatcher(reactContext); mEventDispatcher = new EventDispatcher(reactContext);
mModuleConstants = createConstants(viewManagersList); mCustomDirectEvents = MapBuilder.newHashMap();
mCustomDirectEvents = mModuleConstants = createConstants(viewManagersList, null, mCustomDirectEvents);
(Map<String, Object>) mModuleConstants.get(
UIManagerModuleConstantsHelper.CUSTOM_DIRECT_EVENTS_KEY);
mUIImplementation = mUIImplementation =
uiImplementationProvider.createUIImplementation( uiImplementationProvider.createUIImplementation(
reactContext, reactContext,
@ -214,11 +218,15 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
} }
} }
private static Map<String, Object> createConstants(List<ViewManager> viewManagers) { private static Map<String, Object> createConstants(
List<ViewManager> viewManagers,
@Nullable Map<String, Object> customBubblingEvents,
@Nullable Map<String, Object> customDirectEvents) {
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_START); ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "CreateUIManagerConstants"); Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "CreateUIManagerConstants");
try { try {
return UIManagerModuleConstantsHelper.createConstants(viewManagers); return UIManagerModuleConstantsHelper.createConstants(
viewManagers, customBubblingEvents, customDirectEvents);
} finally { } finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_END); ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_END);
@ -242,11 +250,15 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
Map<String, Object> viewManagerConstants = Map<String, Object> viewManagerConstants =
UIManagerModuleConstantsHelper.createConstantsForViewManager( UIManagerModuleConstantsHelper.createConstantsForViewManager(
targetView, targetView,
UIManagerModuleConstants.getBubblingEventTypeConstants(), mEventsWereSentToJS ? null : UIManagerModuleConstants.getBubblingEventTypeConstants(),
UIManagerModuleConstants.getDirectEventTypeConstants(), mEventsWereSentToJS ? null : UIManagerModuleConstants.getDirectEventTypeConstants(),
null, null,
mCustomDirectEvents); mCustomDirectEvents);
return viewManagerConstants != null ? Arguments.makeNativeMap(viewManagerConstants) : null; if (viewManagerConstants != null) {
mEventsWereSentToJS = true;
return Arguments.makeNativeMap(viewManagerConstants);
}
return null;
} finally { } finally {
SystraceMessage.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); SystraceMessage.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
} }

View File

@ -24,9 +24,6 @@ import javax.annotation.Nullable;
*/ */
/* package */ class UIManagerModuleConstantsHelper { /* package */ class UIManagerModuleConstantsHelper {
/* package */ static final String CUSTOM_BUBBLING_EVENTS_KEY = "customBubblingEventTypes";
/* package */ static final String CUSTOM_DIRECT_EVENTS_KEY = "customDirectEventTypes";
/** /**
* Generates a lazy discovery enabled version of {@link UIManagerModule} constants. It only * Generates a lazy discovery enabled version of {@link UIManagerModule} constants. It only
* contains a list of view manager names, so that JS side is aware of the managers there are. * contains a list of view manager names, so that JS side is aware of the managers there are.
@ -53,22 +50,27 @@ import javax.annotation.Nullable;
* {@link UIManagerModuleConstants}. * {@link UIManagerModuleConstants}.
* TODO(6845124): Create a test for this * TODO(6845124): Create a test for this
*/ */
/* package */ static Map<String, Object> createConstants(List<ViewManager> viewManagers) { /* package */ static Map<String, Object> createConstants(
List<ViewManager> viewManagers,
@Nullable Map<String, Object> allBubblingEventTypes,
@Nullable Map<String, Object> allDirectEventTypes) {
Map<String, Object> constants = UIManagerModuleConstants.getConstants(); Map<String, Object> constants = UIManagerModuleConstants.getConstants();
// Generic/default event types: // Generic/default event types:
// All view managers are capable of dispatching these events. // All view managers are capable of dispatching these events.
// They will be automatically registered for each view type. // They will be automatically registered with React Fiber.
Map genericBubblingEventTypes = UIManagerModuleConstants.getBubblingEventTypeConstants(); Map genericBubblingEventTypes = UIManagerModuleConstants.getBubblingEventTypeConstants();
Map genericDirectEventTypes = UIManagerModuleConstants.getDirectEventTypeConstants(); Map genericDirectEventTypes = UIManagerModuleConstants.getDirectEventTypeConstants();
// Cumulative event types: // Cumulative event types:
// View manager specific event types are collected as views are loaded. // View manager specific event types are collected as views are loaded.
// This information is used later when events are dispatched. // This information is used later when events are dispatched.
Map allBubblingEventTypes = MapBuilder.newHashMap(); if (allBubblingEventTypes != null) {
allBubblingEventTypes.putAll(genericBubblingEventTypes); allBubblingEventTypes.putAll(genericBubblingEventTypes);
Map allDirectEventTypes = MapBuilder.newHashMap(); }
allDirectEventTypes.putAll(genericDirectEventTypes); if (allDirectEventTypes != null) {
allDirectEventTypes.putAll(genericDirectEventTypes);
}
for (ViewManager viewManager : viewManagers) { for (ViewManager viewManager : viewManagers) {
final String viewManagerName = viewManager.getName(); final String viewManagerName = viewManager.getName();
@ -81,8 +83,8 @@ import javax.annotation.Nullable;
try { try {
Map viewManagerConstants = createConstantsForViewManager( Map viewManagerConstants = createConstantsForViewManager(
viewManager, viewManager,
genericBubblingEventTypes, null,
genericDirectEventTypes, null,
allBubblingEventTypes, allBubblingEventTypes,
allDirectEventTypes); allDirectEventTypes);
if (!viewManagerConstants.isEmpty()) { if (!viewManagerConstants.isEmpty()) {
@ -93,41 +95,39 @@ import javax.annotation.Nullable;
} }
} }
// Used by https://fburl.com/6nskr82o constants.put("genericBubblingEventTypes", genericBubblingEventTypes);
constants.put(CUSTOM_BUBBLING_EVENTS_KEY, allBubblingEventTypes); constants.put("genericDirectEventTypes", genericDirectEventTypes);
constants.put(CUSTOM_DIRECT_EVENTS_KEY, allDirectEventTypes);
return constants; return constants;
} }
/* package */ static Map<String, Object> createConstantsForViewManager( /* package */ static Map<String, Object> createConstantsForViewManager(
ViewManager viewManager, ViewManager viewManager,
Map defaultBubblingEvents, @Nullable Map defaultBubblingEvents,
Map defaultDirectEvents, @Nullable Map defaultDirectEvents,
@Nullable Map cumulativeBubblingEventTypes, @Nullable Map cumulativeBubblingEventTypes,
@Nullable Map cumulativeDirectEventTypes) { @Nullable Map cumulativeDirectEventTypes) {
final String BUBBLING_EVENTS_KEY = "bubblingEventTypes";
final String DIRECT_EVENTS_KEY = "directEventTypes";
Map<String, Object> viewManagerConstants = MapBuilder.newHashMap(); Map<String, Object> viewManagerConstants = MapBuilder.newHashMap();
Map viewManagerBubblingEvents = viewManager.getExportedCustomBubblingEventTypeConstants(); Map viewManagerBubblingEvents = viewManager.getExportedCustomBubblingEventTypeConstants();
if (viewManagerBubblingEvents != null) { if (viewManagerBubblingEvents != null) {
if (cumulativeBubblingEventTypes != null) { recursiveMerge(cumulativeBubblingEventTypes, viewManagerBubblingEvents);
recursiveMerge(cumulativeBubblingEventTypes, viewManagerBubblingEvents);
}
recursiveMerge(viewManagerBubblingEvents, defaultBubblingEvents); recursiveMerge(viewManagerBubblingEvents, defaultBubblingEvents);
} else { viewManagerConstants.put(BUBBLING_EVENTS_KEY, viewManagerBubblingEvents);
viewManagerBubblingEvents = defaultBubblingEvents; } else if (defaultBubblingEvents != null) {
viewManagerConstants.put(BUBBLING_EVENTS_KEY, defaultBubblingEvents);
} }
viewManagerConstants.put("bubblingEventTypes", viewManagerBubblingEvents);
Map viewManagerDirectEvents = viewManager.getExportedCustomDirectEventTypeConstants(); Map viewManagerDirectEvents = viewManager.getExportedCustomDirectEventTypeConstants();
if (viewManagerDirectEvents != null) { if (viewManagerDirectEvents != null) {
if (cumulativeDirectEventTypes != null) { recursiveMerge(cumulativeDirectEventTypes, viewManagerDirectEvents);
recursiveMerge(cumulativeDirectEventTypes, viewManagerBubblingEvents);
}
recursiveMerge(viewManagerDirectEvents, defaultDirectEvents); recursiveMerge(viewManagerDirectEvents, defaultDirectEvents);
} else { viewManagerConstants.put(DIRECT_EVENTS_KEY, viewManagerDirectEvents);
viewManagerDirectEvents = defaultDirectEvents; } else if (defaultDirectEvents != null) {
viewManagerConstants.put(DIRECT_EVENTS_KEY, defaultDirectEvents);
} }
viewManagerConstants.put("directEventTypes", viewManagerDirectEvents);
Map customViewConstants = viewManager.getExportedViewConstants(); Map customViewConstants = viewManager.getExportedViewConstants();
if (customViewConstants != null) { if (customViewConstants != null) {
@ -148,7 +148,11 @@ import javax.annotation.Nullable;
/** /**
* Merges {@param source} map into {@param dest} map recursively * Merges {@param source} map into {@param dest} map recursively
*/ */
private static void recursiveMerge(Map dest, Map source) { private static void recursiveMerge(@Nullable Map dest, @Nullable Map source) {
if (dest == null || source == null || source.isEmpty()) {
return;
}
for (Object key : source.keySet()) { for (Object key : source.keySet()) {
Object sourceValue = source.get(key); Object sourceValue = source.get(key);
Object destValue = dest.get(key); Object destValue = dest.get(key);