From f1b13681b425bd3384c856a23be060c8b4b5b071 Mon Sep 17 00:00:00 2001 From: yangguang Date: Sat, 4 Apr 2015 17:58:43 +0800 Subject: [PATCH 001/250] fix the clerical error --- website/src/react-native/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/react-native/index.js b/website/src/react-native/index.js index 3cc9fa6dced476..710cc930549c29 100644 --- a/website/src/react-native/index.js +++ b/website/src/react-native/index.js @@ -174,7 +174,7 @@ var GeoInfo = React.createClass({ - (void)processString:(NSString *)input callback:(RCTResponseSenderBlock)callback { RCT_EXPORT(); // available as NativeModules.MyCustomModule.processString - callback(@[[input stringByReplacingOccurrencesOfString:@"Goodbye" withString:@"Hello"];]]); + callback(@[[input stringByReplacingOccurrencesOfString:@"Goodbye" withString:@"Hello"]]); } @end`} From 7c0a332b81771f1850d2444c7cea09357f1aa63b Mon Sep 17 00:00:00 2001 From: David Rabkin Date: Wed, 8 Apr 2015 16:28:29 -0700 Subject: [PATCH 002/250] Fix grammar in EmbeddedApp.md --- docs/EmbeddedApp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/EmbeddedApp.md b/docs/EmbeddedApp.md index 71ae56d6cabada..e81768e63a0206 100644 --- a/docs/EmbeddedApp.md +++ b/docs/EmbeddedApp.md @@ -111,7 +111,7 @@ Here I disabled **AutoLayout** for simplicity. In real production world, you sho ## Add RCTRootView To Container View -Ready for the most interesting part? Now we shall create the `RCTRootView`, where your React Native app lives in. +Ready for the most interesting part? Now we shall create the `RCTRootView`, where your React Native app lives. In `ReactView.m`, we need to first initiate `RCTRootView` with the URI of your `index.ios.bundle`. `index.ios.bundle` will be created by packager and served by React Native server, which will be discussed later on. From 2975f26e805ff38af61f57136a0995b23db0e8dc Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Fri, 17 Apr 2015 09:10:45 -0700 Subject: [PATCH 003/250] [react-packager] Add asset extensions to file watch glob in the project root --- packager/react-packager/src/Server/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index 3c7be04355e558..e0bfeb2f339d63 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -71,13 +71,17 @@ function Server(options) { this._packages = Object.create(null); this._changeWatchers = []; + var assetGlobs = opts.assetExts.map(function(ext) { + return '**/*.' + ext; + }); + var watchRootConfigs = opts.projectRoots.map(function(dir) { return { dir: dir, globs: [ '**/*.js', '**/package.json', - ] + ].concat(assetGlobs), }; }); @@ -86,9 +90,7 @@ function Server(options) { opts.assetRoots.map(function(dir) { return { dir: dir, - globs: opts.assetExts.map(function(ext) { - return '**/*.' + ext; - }), + globs: assetGlobs, }; }) ); From 691297ab0d9e92189ee352a3564c592718e591fa Mon Sep 17 00:00:00 2001 From: Peter Cottle Date: Fri, 17 Apr 2015 09:10:20 -0700 Subject: [PATCH 004/250] [ReactNative|Easy] Change watchman too-long error message Summary: @wez Mentioned this in Issue #239 -- right now when watchman takes too long we recommend you run `watchman` from your terminal which actually expects some arguments, so it prints out the following: ``` [pcottle:~/Desktop/react-native:changeErrorMessage]$ watchman { "error": "invalid command (expected an array with some elements!)", "cli_validated": true, "version": "3.0.0" } ``` basically this ends up being more confusing since the command we recommend you run errors out, so lets change it to `watchman version` which at least exists cleanly. I kept the troubleshooting link as https://facebook.github.io/watchman/docs/troubleshooting.html since it sounds like we will update that with the issue people run into in #239 Closes https://github.com/facebook/react-native/pull/825 Github Author: Peter Cottle Test Plan: Imported from GitHub, without a `Test Plan:` line. --- packager/react-packager/src/FileWatcher/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/react-packager/src/FileWatcher/index.js b/packager/react-packager/src/FileWatcher/index.js index 9af96c1442ce07..b629dfbf01f3c6 100644 --- a/packager/react-packager/src/FileWatcher/index.js +++ b/packager/react-packager/src/FileWatcher/index.js @@ -73,7 +73,7 @@ function createWatcher(rootConfig) { var rejectTimeout = setTimeout(function() { reject(new Error([ 'Watcher took too long to load', - 'Try running `watchman` from your terminal', + 'Try running `watchman version` from your terminal', 'https://facebook.github.io/watchman/docs/troubleshooting.html', ].join('\n'))); }, MAX_WAIT_TIME); From 0b6dbdb8273f3dfa47208cde92fc2072f4cdf08e Mon Sep 17 00:00:00 2001 From: James Ide Date: Fri, 17 Apr 2015 09:52:57 -0700 Subject: [PATCH 005/250] [Errors] Fix Red Box by fixing providesModule parsing Summary: cc @amasad An error occurred while trying to display the Red Box since loadSourceMap was not included in the JS bundle. This is because node-haste was treating its docblock as a multiline directive which doesn't make sense for `@providesModule`. In loadSourceMap.js's case, the directive's value was parsed as "loadSourceMap -- disabled flow due to mysterious validation errors --". There are two fixes: add a newline under the `@providesModule` directive, and change the module ID code to look at only the first token of the directive. I opted for the latter so we avoid this class of bugs entirely and AFAIK it's nonsensical to have multiple `@providesModule` values anyway. Closes https://github.com/facebook/react-native/pull/866 Github Author: James Ide Test Plan: Run the packager, trigger an error in an app, see the red box now show up again. --- .../src/DependencyResolver/haste/DependencyGraph/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js index 9257d788b1baa9..dba6265a067e90 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js @@ -437,8 +437,9 @@ DependecyGraph.prototype._processModule = function(modulePath) { .then(function(content) { var moduleDocBlock = docblock.parseAsObject(content); if (moduleDocBlock.providesModule || moduleDocBlock.provides) { - moduleData.id = - moduleDocBlock.providesModule || moduleDocBlock.provides; + moduleData.id = /^(\S*)/.exec( + moduleDocBlock.providesModule || moduleDocBlock.provides + )[1]; // Incase someone wants to require this module via // packageName/path/to/module From dbe8e31c209dd1c4a8f48c5e26cbb67edb30d2a7 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Fri, 17 Apr 2015 15:56:05 -0700 Subject: [PATCH 006/250] [ReactNative][Navigator] Remove another unnecessary use of absolute screen width --- .../Navigator/NavigatorBreadcrumbNavigationBar.js | 2 +- .../NavigatorBreadcrumbNavigationBarStyles.ios.js | 9 ++++----- .../CustomComponents/Navigator/NavigatorNavigationBar.js | 2 +- .../Navigator/NavigatorNavigationBarStyles.ios.js | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js index b4bad9982ce931..9fb265f976839e 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js +++ b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js @@ -260,7 +260,7 @@ var styles = StyleSheet.create({ height: NavigatorNavigationBarStyles.General.TotalNavHeight, top: 0, left: 0, - width: NavigatorNavigationBarStyles.General.ScreenWidth, + right: 0, }, }); diff --git a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.ios.js b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.ios.js index 7bc78ee929390a..69d0d52a254870 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.ios.js +++ b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.ios.js @@ -26,12 +26,13 @@ */ 'use strict'; +var Dimensions = require('Dimensions'); var NavigatorNavigationBarStyles = require('NavigatorNavigationBarStyles'); var buildStyleInterpolator = require('buildStyleInterpolator'); var merge = require('merge'); -var SCREEN_WIDTH = NavigatorNavigationBarStyles.General.ScreenWidth; +var SCREEN_WIDTH = Dimensions.get('window').width; var STATUS_BAR_HEIGHT = NavigatorNavigationBarStyles.General.StatusBarHeight; var NAV_BAR_HEIGHT = NavigatorNavigationBarStyles.General.NavBarHeight; @@ -39,7 +40,6 @@ var SPACING = 4; var ICON_WIDTH = 40; var SEPARATOR_WIDTH = 9; var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH; -var RIGHT_BUTTON_WIDTH = 58; var OPACITY_RATIO = 100; var ICON_INACTIVE_OPACITY = 0.6; @@ -74,18 +74,17 @@ var TITLE_BASE = { // For first title styles, make sure first title is centered var FIRST_TITLE_BASE = merge(TITLE_BASE, { left: 0, + right: 0, alignItems: 'center', - width: SCREEN_WIDTH, height: NAV_BAR_HEIGHT, }); var RIGHT_BUTTON_BASE = { position: 'absolute', top: STATUS_BAR_HEIGHT, - left: SCREEN_WIDTH - SPACING - RIGHT_BUTTON_WIDTH, + right: SPACING, overflow: 'hidden', opacity: 1, - width: RIGHT_BUTTON_WIDTH, height: NAV_BAR_HEIGHT, backgroundColor: 'transparent', }; diff --git a/Libraries/CustomComponents/Navigator/NavigatorNavigationBar.js b/Libraries/CustomComponents/Navigator/NavigatorNavigationBar.js index 20c43a94a45b2d..172819de2fa280 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorNavigationBar.js +++ b/Libraries/CustomComponents/Navigator/NavigatorNavigationBar.js @@ -192,7 +192,7 @@ var styles = StyleSheet.create({ height: NavigatorNavigationBarStyles.General.TotalNavHeight, top: 0, left: 0, - width: NavigatorNavigationBarStyles.General.ScreenWidth, + right: 0, backgroundColor: 'transparent', }, }); diff --git a/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStyles.ios.js b/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStyles.ios.js index 266c5df2414d02..fff116745bed98 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStyles.ios.js +++ b/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStyles.ios.js @@ -169,7 +169,6 @@ module.exports = { NavBarHeight: NAV_BAR_HEIGHT, StatusBarHeight: STATUS_BAR_HEIGHT, TotalNavHeight: NAV_HEIGHT, - ScreenWidth: SCREEN_WIDTH, }, Interpolators, Stages, From f1174836d73f5fd248ed58630fa5e15ee2c9207c Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Fri, 17 Apr 2015 15:20:52 -0700 Subject: [PATCH 007/250] [react-packager] Add more information to deprecated asset requires --- .../__tests__/DependencyGraph-test.js | 7 ++-- .../haste/DependencyGraph/index.js | 1 + .../src/Packager/__tests__/Packager-test.js | 15 ++++++-- packager/react-packager/src/Packager/index.js | 35 ++++++++++++------- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js index 98ae7eb73f8f73..d3f6ce289eb590 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js @@ -129,7 +129,8 @@ describe('DependencyGraph', function() { { id: 'image!a', path: '/root/imgs/a.png', dependencies: [], - isAsset_DEPRECATED: true + isAsset_DEPRECATED: true, + resolution: 1, }, ]); }); @@ -288,7 +289,8 @@ describe('DependencyGraph', function() { id: 'image!a', path: '/root/imgs/a.png', dependencies: [], - isAsset_DEPRECATED: true + isAsset_DEPRECATED: true, + resolution: 1, }, ]); }); @@ -1350,6 +1352,7 @@ describe('DependencyGraph', function() { path: '/root/foo.png', dependencies: [], isAsset_DEPRECATED: true, + resolution: 1, }, ]); }); diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js index dba6265a067e90..66534597750f01 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js @@ -642,6 +642,7 @@ DependecyGraph.prototype._processAsset_DEPRECATED = function(file) { path: path.resolve(file), isAsset_DEPRECATED: true, dependencies: [], + resolution: extractAssetResolution(file).resolution, }); } }; diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index 0c9d4a84d6340f..333e0f5631e1df 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -52,6 +52,7 @@ describe('Packager', function() { path: '/root/img/img.png', isAsset_DEPRECATED: true, dependencies: [], + resolution: 2, }, { id: 'new_image.png', @@ -98,12 +99,22 @@ describe('Packager', function() { 'source /root/bar.js', '/root/bar.js' ]); + + var imgModule_DEPRECATED = { + isStatic: true, + path: '/root/img/img.png', + uri: 'img', + width: 25, + height: 50, + deprecated: true, + }; + expect(p.addModule.mock.calls[2]).toEqual([ 'lol module.exports = ' + - JSON.stringify({ uri: 'img', isStatic: true}) + + JSON.stringify(imgModule_DEPRECATED) + '; lol', 'module.exports = ' + - JSON.stringify({ uri: 'img', isStatic: true}) + + JSON.stringify(imgModule_DEPRECATED) + ';', '/root/img/img.png' ]); diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index 74e2ff4c358709..2b1eb6b1658872 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -20,6 +20,8 @@ var Activity = require('../Activity'); var declareOpts = require('../lib/declareOpts'); var imageSize = require('image-size'); +var sizeOf = Promise.promisify(imageSize); + var validateOpts = declareOpts({ projectRoots: { type: 'array', @@ -142,7 +144,7 @@ Packager.prototype._transformModule = function(module) { var transform; if (module.isAsset_DEPRECATED) { - transform = Promise.resolve(generateAssetModule_DEPRECATED(module)); + transform = generateAssetModule_DEPRECATED(module); } else if (module.isAsset) { transform = generateAssetModule( module, @@ -175,19 +177,26 @@ Packager.prototype.getGraphDebugInfo = function() { }; function generateAssetModule_DEPRECATED(module) { - var code = 'module.exports = ' + JSON.stringify({ - uri: module.id.replace(/^[^!]+!/, ''), - isStatic: true, - }) + ';'; - - return { - code: code, - sourceCode: code, - sourcePath: module.path, - }; -} + return sizeOf(module.path).then(function(dimensions) { + var img = { + isStatic: true, + path: module.path, + uri: module.id.replace(/^[^!]+!/, ''), + width: dimensions.width / module.resolution, + height: dimensions.height / module.resolution, + deprecated: true, + }; -var sizeOf = Promise.promisify(imageSize); + + var code = 'module.exports = ' + JSON.stringify(img) + ';'; + + return { + code: code, + sourceCode: code, + sourcePath: module.path, + }; + }); +} function generateAssetModule(module, relPath) { return sizeOf(module.path).then(function(dimensions) { From aaaa9a98ef61e6128a808bec2470b1a076353e8a Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 17 Apr 2015 15:33:54 -0700 Subject: [PATCH 008/250] [ReactNative] Update stacktrace-parser --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5169828ef6b04..ce743e3bf6d1d9 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "rebound": "^0.0.12", "sane": "1.0.3", "source-map": "0.1.31", - "stacktrace-parser": "0.1.1", + "stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638", "uglify-js": "~2.4.16", "underscore": "1.7.0", "worker-farm": "1.1.0", From 65b6d209d9095efba2bc1d6f2098fae8b127e1c7 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 17 Apr 2015 15:37:07 -0700 Subject: [PATCH 009/250] [ReactNative] cleanup some requireNativeComponent cruft --- Libraries/Components/ScrollView/ScrollView.js | 4 --- Libraries/Components/SliderIOS/SliderIOS.js | 21 +----------- Libraries/Image/Image.ios.js | 34 ++----------------- 3 files changed, 3 insertions(+), 56 deletions(-) diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 0308688b5768d0..e66612b22886c0 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -371,10 +371,6 @@ if (Platform.OS === 'android') { uiViewClassName: 'AndroidHorizontalScrollView', }); } else if (Platform.OS === 'ios') { - var RCTScrollView = createReactIOSNativeComponentClass({ - validAttributes: validAttributes, - uiViewClassName: 'RCTScrollView', - }); var RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView); } diff --git a/Libraries/Components/SliderIOS/SliderIOS.js b/Libraries/Components/SliderIOS/SliderIOS.js index 81815ba34c97c5..0c2d02da04f4ac 100644 --- a/Libraries/Components/SliderIOS/SliderIOS.js +++ b/Libraries/Components/SliderIOS/SliderIOS.js @@ -12,16 +12,11 @@ 'use strict'; var NativeMethodsMixin = require('NativeMethodsMixin'); -var Platform = require('Platform'); var PropTypes = require('ReactPropTypes'); var React = require('React'); -var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StyleSheet = require('StyleSheet'); var View = require('View'); -var createReactIOSNativeComponentClass = - require('createReactIOSNativeComponentClass'); -var merge = require('merge'); var requireNativeComponent = require('requireNativeComponent'); type Event = Object; @@ -98,20 +93,6 @@ var styles = StyleSheet.create({ }, }); -if (Platform.OS === 'ios') { - var RCTSlider = requireNativeComponent('RCTSlider', SliderIOS); -} else { - var validAttributes = { - ...ReactIOSViewAttributes.UIView, - value: true, - minimumValue: true, - maximumValue: true, - }; - - var RCTSlider = createReactIOSNativeComponentClass({ - validAttributes: validAttributes, - uiViewClassName: 'RCTSlider', - }); -} +var RCTSlider = requireNativeComponent('RCTSlider', SliderIOS); module.exports = SliderIOS; diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index fed358e1337ead..d519c1ac5308e1 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -14,7 +14,6 @@ var EdgeInsetsPropType = require('EdgeInsetsPropType'); var NativeMethodsMixin = require('NativeMethodsMixin'); var NativeModules = require('NativeModules'); -var Platform = require('Platform'); var PropTypes = require('ReactPropTypes'); var ImageResizeMode = require('ImageResizeMode'); var ImageStylePropTypes = require('ImageStylePropTypes'); @@ -23,9 +22,7 @@ var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StyleSheet = require('StyleSheet'); var StyleSheetPropType = require('StyleSheetPropType'); -var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); var flattenStyle = require('flattenStyle'); -var insetsDiffer = require('insetsDiffer'); var invariant = require('invariant'); var merge = require('merge'); var requireNativeComponent = require('requireNativeComponent'); @@ -156,12 +153,6 @@ var Image = React.createClass({ contentMode, tintColor: style.tintColor, }); - if (Platform.OS === 'android') { - // TODO: update android native code to not need this - nativeProps.resizeMode = contentMode; - delete nativeProps.contentMode; - } - if (isStored) { nativeProps.imageTag = source.uri; } else { @@ -180,30 +171,9 @@ var styles = StyleSheet.create({ }, }); -if (Platform.OS === 'android') { - var CommonImageViewAttributes = merge(ReactIOSViewAttributes.UIView, { - accessible: true, - accessibilityLabel: true, - capInsets: {diff: insetsDiffer}, // UIEdgeInsets=UIEdgeInsetsZero - imageTag: true, - resizeMode: true, - src: true, - testID: PropTypes.string, - }); +var RCTStaticImage = requireNativeComponent('RCTStaticImage', null); +var RCTNetworkImage = requireNativeComponent('RCTNetworkImageView', null); - var RCTStaticImage = createReactIOSNativeComponentClass({ - validAttributes: merge(CommonImageViewAttributes, { tintColor: true }), - uiViewClassName: 'RCTStaticImage', - }); - - var RCTNetworkImage = createReactIOSNativeComponentClass({ - validAttributes: merge(CommonImageViewAttributes, { defaultImageSrc: true }), - uiViewClassName: 'RCTNetworkImageView', - }); -} else { - var RCTStaticImage = requireNativeComponent('RCTStaticImage', null); - var RCTNetworkImage = requireNativeComponent('RCTNetworkImageView', null); -} var nativeOnlyProps = { src: true, defaultImageSrc: true, From ab1efbd4c12a5792f55a1021903688d5aa864b15 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 17 Apr 2015 15:47:22 -0700 Subject: [PATCH 010/250] [ReactNative] kill for...in array iteration in deepDiffer --- .../differ/__tests__/deepDiffer-test.js | 102 ++++++++++++++++++ Libraries/Utilities/differ/deepDiffer.js | 29 +++-- 2 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 Libraries/Utilities/differ/__tests__/deepDiffer-test.js diff --git a/Libraries/Utilities/differ/__tests__/deepDiffer-test.js b/Libraries/Utilities/differ/__tests__/deepDiffer-test.js new file mode 100644 index 00000000000000..745b83ab093053 --- /dev/null +++ b/Libraries/Utilities/differ/__tests__/deepDiffer-test.js @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +jest.dontMock('deepDiffer'); +var deepDiffer = require('deepDiffer'); + +describe('deepDiffer', function() { + it('should diff primitives of the same type', () => { + expect(deepDiffer(1, 2)).toBe(true); + expect(deepDiffer(42, 42)).toBe(false); + expect(deepDiffer('foo', 'bar')).toBe(true); + expect(deepDiffer('foo', 'foo')).toBe(false); + expect(deepDiffer(true, false)).toBe(true); + expect(deepDiffer(false, true)).toBe(true); + expect(deepDiffer(true, true)).toBe(false); + expect(deepDiffer(false, false)).toBe(false); + expect(deepDiffer(null, null)).toBe(false); + expect(deepDiffer(undefined, undefined)).toBe(false); + }); + it('should diff primitives of different types', () => { + expect(deepDiffer(1, '1')).toBe(true); + expect(deepDiffer(true, 'true')).toBe(true); + expect(deepDiffer(true, 1)).toBe(true); + expect(deepDiffer(false, 0)).toBe(true); + expect(deepDiffer(null, undefined)).toBe(true); + expect(deepDiffer(null, 0)).toBe(true); + expect(deepDiffer(null, false)).toBe(true); + expect(deepDiffer(null, '')).toBe(true); + expect(deepDiffer(undefined, 0)).toBe(true); + expect(deepDiffer(undefined, false)).toBe(true); + expect(deepDiffer(undefined, '')).toBe(true); + }); + it('should diff Objects', () => { + expect(deepDiffer({}, {})).toBe(false); + expect(deepDiffer({}, null)).toBe(true); + expect(deepDiffer(null, {})).toBe(true); + expect(deepDiffer({a: 1}, {a: 1})).toBe(false); + expect(deepDiffer({a: 1}, {a: 2})).toBe(true); + expect(deepDiffer({a: 1}, {a: 1, b: null})).toBe(true); + expect(deepDiffer({a: 1}, {a: 1, b: 1})).toBe(true); + expect(deepDiffer({a: 1, b: 1}, {a: 1})).toBe(true); + expect(deepDiffer({a: {A: 1}, b: 1}, {a: {A: 1}, b: 1})).toBe(false); + expect(deepDiffer({a: {A: 1}, b: 1}, {a: {A: 2}, b: 1})).toBe(true); + expect(deepDiffer( + {a: {A: {aA: 1, bB: 1}}, b: 1}, + {a: {A: {aA: 1, bB: 1}}, b: 1} + )).toBe(false); + expect(deepDiffer( + {a: {A: {aA: 1, bB: 1}}, b: 1}, + {a: {A: {aA: 1, cC: 1}}, b: 1} + )).toBe(true); + }); + it('should diff Arrays', () => { + expect(deepDiffer([], [])).toBe(false); + expect(deepDiffer([], null)).toBe(true); + expect(deepDiffer(null, [])).toBe(true); + expect(deepDiffer([42], [42])).toBe(false); + expect(deepDiffer([1], [2])).toBe(true); + expect(deepDiffer([1, 2, 3], [1, 2, 3])).toBe(false); + expect(deepDiffer([1, 2, 3], [1, 2, 4])).toBe(true); + expect(deepDiffer([1, 2, 3], [1, 4, 3])).toBe(true); + expect(deepDiffer([1, 2, 3, 4], [1, 2, 3])).toBe(true); + expect(deepDiffer([1, 2, 3], [1, 2, 3, 4])).toBe(true); + expect(deepDiffer([0, null, false, ''], [0, null, false, ''])).toBe(false); + expect(deepDiffer([0, null, false, ''], ['', false, null, 0])).toBe(true); + }); + it('should diff mixed types', () => { + expect(deepDiffer({}, [])).toBe(true); + expect(deepDiffer([], {})).toBe(true); + expect(deepDiffer( + {a: [{A: {aA: 1, bB: 1}}, 'bar'], c: [1, [false]]}, + {a: [{A: {aA: 1, bB: 1}}, 'bar'], c: [1, [false]]} + )).toBe(false); + expect(deepDiffer( + {a: [{A: {aA: 1, bB: 1}}, 'bar'], c: [1, [false]]}, + {a: [{A: {aA: 1, bB: 2}}, 'bar'], c: [1, [false]]} + )).toBe(true); + expect(deepDiffer( + {a: [{A: {aA: 1, bB: 1}}, 'bar'], c: [1, [false]]}, + {a: [{A: {aA: 1, bB: 1}}, 'bar'], c: [1, [false], null]} + )).toBe(true); + expect(deepDiffer( + {a: [{A: {aA: 1, bB: 1}}, 'bar'], c: [1, [false]]}, + {a: [{A: {aA: 1, bB: 1}}, ['bar']], c: [1, [false]]} + )).toBe(true); + }); + it('should distinguish between proper Array and Object', () => { + expect(deepDiffer(['a', 'b'], {0: 'a', 1: 'b', length: 2})).toBe(true); + expect(deepDiffer(['a', 'b'], {length: 2, 0: 'a', 1: 'b'})).toBe(true); + }); + it('should diff same object', () => { + var obj = [1,[2,3]]; + expect(deepDiffer(obj, obj)).toBe(false); + }); +}); diff --git a/Libraries/Utilities/differ/deepDiffer.js b/Libraries/Utilities/differ/deepDiffer.js index 66dc7fa95cf585..4dec6bd7bb892d 100644 --- a/Libraries/Utilities/differ/deepDiffer.js +++ b/Libraries/Utilities/differ/deepDiffer.js @@ -35,16 +35,29 @@ var deepDiffer = function(one: any, two: any): bool { if (one.constructor !== two.constructor) { return true; } - for (var key in one) { - if (deepDiffer(one[key], two[key])) { + if (Array.isArray(one)) { + // We know two is also an array because the constructors are equal + var len = one.length; + if (two.length !== len) { return true; } - } - for (var twoKey in two) { - // The only case we haven't checked yet is keys that are in two but aren't - // in one, which means they are different. - if (one[twoKey] === undefined && two[twoKey] !== undefined) { - return true; + for (var ii = 0; ii < len; ii++) { + if (deepDiffer(one[ii], two[ii])) { + return true; + } + } + } else { + for (var key in one) { + if (deepDiffer(one[key], two[key])) { + return true; + } + } + for (var twoKey in two) { + // The only case we haven't checked yet is keys that are in two but aren't + // in one, which means they are different. + if (one[twoKey] === undefined && two[twoKey] !== undefined) { + return true; + } } } return false; From f3e7511d2b830fa1b7b60b6d0a95b1a35e2b60d5 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 17 Apr 2015 17:01:18 -0700 Subject: [PATCH 011/250] [ReactNative] Dim packager output --- packager/getFlowTypeCheckMiddleware.js | 15 +++------------ .../src/Activity/__mocks__/chalk.js | 13 +++++++++++++ packager/react-packager/src/Activity/index.js | 12 +++++++----- 3 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 packager/react-packager/src/Activity/__mocks__/chalk.js diff --git a/packager/getFlowTypeCheckMiddleware.js b/packager/getFlowTypeCheckMiddleware.js index 312da45e21c1f5..cd910054f4d817 100644 --- a/packager/getFlowTypeCheckMiddleware.js +++ b/packager/getFlowTypeCheckMiddleware.js @@ -10,6 +10,7 @@ var chalk = require('chalk'); var exec = require('child_process').exec; +var Activity = require('./react-packager/src/Activity'); var hasWarned = {}; @@ -44,20 +45,10 @@ function getFlowTypeCheckMiddleware(options) { function doFlowTypecheck(res, flowroot, next) { var flowCmd = 'cd "' + flowroot + '" && flow --json --timeout 20'; - var start = Date.now(); - // Log start message if flow is slow to let user know something is happening. - var flowSlow = setTimeout( - function() { - console.log(chalk.gray('flow: Running static typechecks.')); - }, - 500 - ); + var eventId = Activity.startEvent('flow static typechecks'); exec(flowCmd, function(flowError, stdout, stderr) { - clearTimeout(flowSlow); + Activity.endEvent(eventId); if (!flowError) { - console.log(chalk.gray( - 'flow: Typechecks passed (' + (Date.now() - start) + 'ms).') - ); return next(); } else { try { diff --git a/packager/react-packager/src/Activity/__mocks__/chalk.js b/packager/react-packager/src/Activity/__mocks__/chalk.js new file mode 100644 index 00000000000000..2981f979d90dd5 --- /dev/null +++ b/packager/react-packager/src/Activity/__mocks__/chalk.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +module.exports = { + dim: function(s) { return s; }, +}; diff --git a/packager/react-packager/src/Activity/index.js b/packager/react-packager/src/Activity/index.js index 05285d0fc98c8a..8e593f9ffe7d09 100644 --- a/packager/react-packager/src/Activity/index.js +++ b/packager/react-packager/src/Activity/index.js @@ -8,6 +8,8 @@ */ 'use strict'; +var chalk = require('chalk'); + var COLLECTION_PERIOD = 1000; var _endedEvents = Object.create(null); @@ -132,22 +134,22 @@ function _writeAction(action) { switch (action.action) { case 'startEvent': - console.log( + console.log(chalk.dim( '[' + fmtTime + '] ' + ' ' + action.eventName + data - ); + )); break; case 'endEvent': var startAction = _eventStarts[action.eventId]; var startData = startAction.data ? ': ' + JSON.stringify(startAction.data) : ''; - console.log( + console.log(chalk.dim( '[' + fmtTime + '] ' + ' ' + startAction.eventName + - '(' + (action.tstamp - startAction.tstamp) + 'ms)' + + ' (' + (action.tstamp - startAction.tstamp) + 'ms)' + startData - ); + )); delete _eventStarts[action.eventId]; break; From 17be6ba82ae24800e29ceb9a9c8c7a9e64801204 Mon Sep 17 00:00:00 2001 From: Basil Hosmer Date: Fri, 17 Apr 2015 18:09:58 -0700 Subject: [PATCH 012/250] reinstate @flow --- Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js index e168daae66201d..9ecf2543b94d44 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js +++ b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js @@ -7,8 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule loadSourceMap - * - * -- disabled flow due to mysterious validation errors -- + * @flow */ 'use strict'; From bd5b12c5356c7166d8bb78521c77e710b5fcdab1 Mon Sep 17 00:00:00 2001 From: Bill Fisher Date: Fri, 17 Apr 2015 22:20:13 -0700 Subject: [PATCH 013/250] [ReactNative] implement transform styles --- .../Components/View/ViewStylePropTypes.js | 6 +- Libraries/ReactIOS/NativeMethodsMixin.js | 3 +- Libraries/ReactIOS/ReactIOSNativeComponent.js | 3 +- Libraries/StyleSheet/precomputeStyle.js | 161 ++++++++++++++++++ Libraries/Utilities/MatrixMath.js | 131 ++++++++++++++ 5 files changed, 297 insertions(+), 7 deletions(-) create mode 100644 Libraries/StyleSheet/precomputeStyle.js create mode 100755 Libraries/Utilities/MatrixMath.js diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index bb22c6b26511bf..73afe99bdb70c0 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -34,12 +34,8 @@ var ViewStylePropTypes = { ), shadowOpacity: ReactPropTypes.number, shadowRadius: ReactPropTypes.number, + transform: ReactPropTypes.arrayOf(ReactPropTypes.object), transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), - rotation: ReactPropTypes.number, - scaleX: ReactPropTypes.number, - scaleY: ReactPropTypes.number, - translateX: ReactPropTypes.number, - translateY: ReactPropTypes.number, }; module.exports = ViewStylePropTypes; diff --git a/Libraries/ReactIOS/NativeMethodsMixin.js b/Libraries/ReactIOS/NativeMethodsMixin.js index ec72a0b4f4696b..9d413e5c7ca7a1 100644 --- a/Libraries/ReactIOS/NativeMethodsMixin.js +++ b/Libraries/ReactIOS/NativeMethodsMixin.js @@ -19,6 +19,7 @@ var TextInputState = require('TextInputState'); var flattenStyle = require('flattenStyle'); var invariant = require('invariant'); var mergeFast = require('mergeFast'); +var precomputeStyle = require('precomputeStyle'); type MeasureOnSuccessCallback = ( x: number, @@ -93,7 +94,7 @@ var NativeMethodsMixin = { break; } } - var style = flattenStyle(nativeProps.style); + var style = precomputeStyle(flattenStyle(nativeProps.style)); var props = null; if (hasOnlyStyle) { diff --git a/Libraries/ReactIOS/ReactIOSNativeComponent.js b/Libraries/ReactIOS/ReactIOSNativeComponent.js index b9abd5965c5f07..7f27ae0ea7dae3 100644 --- a/Libraries/ReactIOS/ReactIOSNativeComponent.js +++ b/Libraries/ReactIOS/ReactIOSNativeComponent.js @@ -23,6 +23,7 @@ var styleDiffer = require('styleDiffer'); var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev'); var diffRawProperties = require('diffRawProperties'); var flattenStyle = require('flattenStyle'); +var precomputeStyle = require('precomputeStyle'); var warning = require('warning'); var registrationNames = ReactIOSEventEmitter.registrationNames; @@ -160,7 +161,7 @@ ReactIOSNativeComponent.Mixin = { // before actually doing the expensive flattening operation in order to // compute the diff. if (styleDiffer(nextProps.style, prevProps.style)) { - var nextFlattenedStyle = flattenStyle(nextProps.style); + var nextFlattenedStyle = precomputeStyle(flattenStyle(nextProps.style)); updatePayload = diffRawProperties( updatePayload, this.previousFlattenedStyle, diff --git a/Libraries/StyleSheet/precomputeStyle.js b/Libraries/StyleSheet/precomputeStyle.js new file mode 100644 index 00000000000000..c3a1ca8e5cb1ed --- /dev/null +++ b/Libraries/StyleSheet/precomputeStyle.js @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule precomputeStyle + * @flow + */ +'use strict'; + +var MatrixMath = require('MatrixMath'); +var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev'); +var invariant = require('invariant'); + +/** + * This method provides a hook where flattened styles may be precomputed or + * otherwise prepared to become better input data for native code. + */ +function precomputeStyle(style: ?Object): ?Object { + if (!style || !style.transform) { + return style; + } + invariant( + !style.transformMatrix, + 'transformMatrix and transform styles cannot be used on the same component' + ); + var newStyle = _precomputeTransforms({...style}); + deepFreezeAndThrowOnMutationInDev(newStyle); + return newStyle; +} + +/** + * Generate a transform matrix based on the provided transforms, and use that + * within the style object instead. + * + * This allows us to provide an API that is similar to CSS and to have a + * universal, singular interface to native code. + */ +function _precomputeTransforms(style: Object): Object { + var {transform, transformMatrix, ...style} = style; + var result = MatrixMath.createIdentityMatrix(); + + transform.forEach(transformation => { + var key = Object.keys(transformation)[0]; + var value = transformation[key]; + if (__DEV__) { + _validateTransform(key, value, transformation); + } + + switch (key) { + case 'matrix': + MatrixMath.multiplyInto(result, result, value); + break; + case 'rotate': + _multiplyTransform(result, MatrixMath.reuseRotateZCommand, [_convertToRadians(value)]); + break; + case 'scale': + _multiplyTransform(result, MatrixMath.reuseScaleCommand, [value]); + break; + case 'scaleX': + _multiplyTransform(result, MatrixMath.reuseScaleXCommand, [value]); + break; + case 'scaleY': + _multiplyTransform(result, MatrixMath.reuseScaleYCommand, [value]); + break; + case 'translate': + _multiplyTransform(result, MatrixMath.reuseTranslate3dCommand, [value[0], value[1], value[2] || 0]); + break; + case 'translateX': + _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [value, 0]); + break; + case 'translateY': + _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [0, value]); + break; + default: + throw new Error('Invalid transform name: ' + key); + } + }); + + return { + ...style, + transformMatrix: result, + }; +} + +/** + * Performs a destructive operation on a transform matrix. + */ +function _multiplyTransform( + result: Array, + matrixMathFunction: Function, + args: Array +): void { + var matrixToApply = MatrixMath.createIdentityMatrix(); + var argsWithIdentity = [matrixToApply].concat(args); + matrixMathFunction.apply(this, argsWithIdentity); + MatrixMath.multiplyInto(result, result, matrixToApply); +} + +/** + * Parses a string like '0.5rad' or '60deg' into radians expressed in a float. + * Note that validation on the string is done in `_validateTransform()`. + */ +function _convertToRadians(value: string): number { + var floatValue = parseFloat(value, 10); + return value.indexOf('rad') > -1 ? floatValue : floatValue * Math.PI / 180; +} + +function _validateTransform(key, value, transformation) { + var multivalueTransforms = [ + 'matrix', + 'translate', + ]; + if (multivalueTransforms.indexOf(key) !== -1) { + invariant( + Array.isArray(value), + 'Transform with key of %s must have an array as the value: %s', + key, + JSON.stringify(transformation) + ); + } + switch (key) { + case 'matrix': + invariant( + value.length === 9 || value.length === 16, + 'Matrix transform must have a length of 9 (2d) or 16 (3d). ' + + 'Provided matrix has a length of %s: %s', + value.length, + JSON.stringify(transformation) + ); + break; + case 'translate': + break; + case 'rotate': + invariant( + typeof value === 'string', + 'Transform with key of "%s" must be a string: %s', + key, + JSON.stringify(transformation) + ); + invariant( + value.indexOf('deg') > -1 || value.indexOf('rad') > -1, + 'Rotate transform must be expressed in degrees (deg) or radians ' + + '(rad): %s', + JSON.stringify(transformation) + ); + break; + default: + invariant( + typeof value === 'number', + 'Transform with key of "%s" must be a number: %s', + key, + JSON.stringify(transformation) + ); + } +} + +module.exports = precomputeStyle; diff --git a/Libraries/Utilities/MatrixMath.js b/Libraries/Utilities/MatrixMath.js new file mode 100755 index 00000000000000..7f3d17c461b70a --- /dev/null +++ b/Libraries/Utilities/MatrixMath.js @@ -0,0 +1,131 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule MatrixMath + */ +'use strict'; + +/** + * Memory conservative (mutative) matrix math utilities. Uses "command" + * matrices, which are reusable. + */ +var MatrixMath = { + createIdentityMatrix: function() { + return [ + 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1 + ]; + }, + + createCopy: function(m) { + return [ + m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[12], m[13], m[14], m[15], + ]; + }, + + createTranslate2d: function(x, y) { + var mat = MatrixMath.createIdentityMatrix(); + MatrixMath.reuseTranslate2dCommand(mat, x, y); + return mat; + }, + + reuseTranslate2dCommand: function(matrixCommand, x, y) { + matrixCommand[12] = x; + matrixCommand[13] = y; + }, + + reuseTranslate3dCommand: function(matrixCommand, x, y, z) { + matrixCommand[12] = x; + matrixCommand[13] = y; + matrixCommand[14] = z; + }, + + createScale: function(factor) { + var mat = MatrixMath.createIdentityMatrix(); + MatrixMath.reuseScaleCommand(mat, factor); + return mat; + }, + + reuseScaleCommand: function(matrixCommand, factor) { + matrixCommand[0] = factor; + matrixCommand[5] = factor; + }, + + reuseScale3dCommand: function(matrixCommand, x, y, z) { + matrixCommand[0] = x; + matrixCommand[5] = y; + matrixCommand[10] = z; + }, + + reuseScaleXCommand(matrixCommand, factor) { + matrixCommand[0] = factor; + }, + + reuseScaleYCommand(matrixCommand, factor) { + matrixCommand[5] = factor; + }, + + reuseScaleZCommand(matrixCommand, factor) { + matrixCommand[10] = factor; + }, + + reuseRotateYCommand: function(matrixCommand, amount) { + matrixCommand[0] = Math.cos(amount); + matrixCommand[2] = Math.sin(amount); + matrixCommand[8] = Math.sin(-amount); + matrixCommand[10] = Math.cos(amount); + }, + + createRotateZ: function(radians) { + var mat = MatrixMath.createIdentityMatrix(); + MatrixMath.reuseRotateZCommand(mat, radians); + return mat; + }, + + // http://www.w3.org/TR/css3-transforms/#recomposing-to-a-2d-matrix + reuseRotateZCommand: function(matrixCommand, radians) { + matrixCommand[0] = Math.cos(radians); + matrixCommand[1] = Math.sin(radians); + matrixCommand[4] = -Math.sin(radians); + matrixCommand[5] = Math.cos(radians); + }, + + multiplyInto: function(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + } + +}; + +module.exports = MatrixMath; From 2b9aaac2ff9868726e16f90b9de2af444ead230b Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Sat, 18 Apr 2015 06:23:24 -0700 Subject: [PATCH 014/250] [ReactNative] Guard against blocks being added to UIManager during dealloc --- React/Modules/RCTUIManager.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index b6a350dcd4964e..ae04f9a1d4c21c 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -398,13 +398,15 @@ - (void)addUIBlock:(RCTViewManagerUIBlock)block { RCTAssert(![NSThread isMainThread], @"This method should only be called on the shadow thread"); + if (!self.isValid) { + return; + } + __weak RCTUIManager *weakViewManager = self; - __weak RCTSparseArray *weakViewRegistry = _viewRegistry; dispatch_block_t outerBlock = ^{ RCTUIManager *strongViewManager = weakViewManager; - RCTSparseArray *strongViewRegistry = weakViewRegistry; - if (strongViewManager && strongViewRegistry) { - block(strongViewManager, strongViewRegistry); + if (strongViewManager && strongViewManager.isValid) { + block(strongViewManager, strongViewManager->_viewRegistry); } }; From ead0f2e020d16154a11e6d07355ee2ecc0fce6e2 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sat, 18 Apr 2015 10:43:20 -0700 Subject: [PATCH 015/250] Implemented thread control for exported methods --- .../testTabBarExampleSnapshot_1@2x.png | Bin 28887 -> 27640 bytes .../UIExplorerTests/UIExplorerTests.m | 12 +- .../ActionSheetIOS/RCTActionSheetManager.m | 120 +++++++------- .../RCTAnimationExperimentalManager.m | 5 + Libraries/Geolocation/RCTLocationObserver.m | 128 +++++++-------- Libraries/LinkingIOS/RCTLinkingManager.m | 6 +- Libraries/RCTTest/RCTTestModule.h | 22 ++- Libraries/RCTTest/RCTTestModule.m | 41 ++--- Libraries/RCTTest/RCTTestRunner.m | 37 ++--- React/Base/RCTBridge.h | 7 - React/Base/RCTBridge.m | 52 ++++-- React/Base/RCTBridgeModule.h | 17 ++ React/Base/RCTConvert.h | 4 +- React/Base/RCTConvert.m | 6 +- React/Modules/RCTAlertManager.m | 53 +++--- React/Modules/RCTAsyncLocalStorage.m | 155 ++++++++---------- React/Modules/RCTExceptionsManager.m | 6 +- React/Modules/RCTSourceCode.m | 1 - React/Modules/RCTStatusBarManager.m | 34 ++-- React/Modules/RCTTiming.m | 16 +- React/Modules/RCTUIManager.m | 34 ++-- React/Views/RCTViewManager.h | 23 +-- React/Views/RCTViewManager.m | 6 + 23 files changed, 383 insertions(+), 402 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTabBarExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTabBarExampleSnapshot_1@2x.png index d3e66652b5c01bcdc1c08114cfea10fba8d5fe05..6fa9c15557705009bd5720dee3e2e02fdcdf88fb 100644 GIT binary patch literal 27640 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 literal 28887 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 XMUQ+qbQTMMe~uoq{I=*Dx2yjLZA`-) diff --git a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m index fd321546aab27c..2c2359b4400339 100644 --- a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m +++ b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m @@ -59,11 +59,10 @@ - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test return NO; } -// Make sure this test runs first (underscores sort early) otherwise the -// other tests will tear out the rootView -- (void)test__RootViewLoadsAndRenders +// Make sure this test runs first because the other tests will tear out the rootView +- (void)testAAA_RootViewLoadsAndRenders { - UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; + UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController; RCTAssert([vc.view isKindOfClass:[RCTRootView class]], @"This test must run first."); NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; BOOL foundElement = NO; @@ -72,10 +71,8 @@ - (void)test__RootViewLoadsAndRenders while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date]; - redboxError = [[RCTRedBox sharedInstance] currentErrorMessage]; - - foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { + foundElement = [self findSubviewInView:vc.view matching:^(UIView *view) { if ([view respondsToSelector:@selector(attributedText)]) { NSString *text = [(id)view attributedText].string; if ([text isEqualToString:@""]) { @@ -120,6 +117,7 @@ - (void)testTabBarExampleSnapshot [_runner runTest:_cmd module:@"TabBarExample"]; } +// Make sure this test runs last - (void)testZZZ_NotInRecordMode { RCTAssert(_runner.recordMode == NO, @"Don't forget to turn record mode back to NO before commit."); diff --git a/Libraries/ActionSheetIOS/RCTActionSheetManager.m b/Libraries/ActionSheetIOS/RCTActionSheetManager.m index c6d6e404cb3ad8..ac672249f9f3b7 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheetManager.m +++ b/Libraries/ActionSheetIOS/RCTActionSheetManager.m @@ -34,92 +34,88 @@ - (instancetype)init failureCallback:(RCTResponseSenderBlock)failureCallback successCallback:(RCTResponseSenderBlock)successCallback) { - dispatch_async(dispatch_get_main_queue(), ^{ - UIActionSheet *actionSheet = [[UIActionSheet alloc] init]; + UIActionSheet *actionSheet = [[UIActionSheet alloc] init]; - actionSheet.title = options[@"title"]; + actionSheet.title = options[@"title"]; - for (NSString *option in options[@"options"]) { - [actionSheet addButtonWithTitle:option]; - } + for (NSString *option in options[@"options"]) { + [actionSheet addButtonWithTitle:option]; + } - if (options[@"destructiveButtonIndex"]) { - actionSheet.destructiveButtonIndex = [options[@"destructiveButtonIndex"] integerValue]; - } - if (options[@"cancelButtonIndex"]) { - actionSheet.cancelButtonIndex = [options[@"cancelButtonIndex"] integerValue]; - } + if (options[@"destructiveButtonIndex"]) { + actionSheet.destructiveButtonIndex = [options[@"destructiveButtonIndex"] integerValue]; + } + if (options[@"cancelButtonIndex"]) { + actionSheet.cancelButtonIndex = [options[@"cancelButtonIndex"] integerValue]; + } - actionSheet.delegate = self; + actionSheet.delegate = self; - _callbacks[keyForInstance(actionSheet)] = successCallback; + _callbacks[RCTKeyForInstance(actionSheet)] = successCallback; - UIWindow *appWindow = [[[UIApplication sharedApplication] delegate] window]; - if (appWindow == nil) { - RCTLogError(@"Tried to display action sheet but there is no application window. options: %@", options); - return; - } - [actionSheet showInView:appWindow]; - }); + UIWindow *appWindow = [[[UIApplication sharedApplication] delegate] window]; + if (appWindow == nil) { + RCTLogError(@"Tried to display action sheet but there is no application window. options: %@", options); + return; + } + [actionSheet showInView:appWindow]; } RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options failureCallback:(RCTResponseSenderBlock)failureCallback successCallback:(RCTResponseSenderBlock)successCallback) { - dispatch_async(dispatch_get_main_queue(), ^{ - NSMutableArray *items = [NSMutableArray array]; - id message = options[@"message"]; - id url = options[@"url"]; - if ([message isKindOfClass:[NSString class]]) { - [items addObject:message]; - } - if ([url isKindOfClass:[NSString class]]) { - [items addObject:[NSURL URLWithString:url]]; - } - if ([items count] == 0) { - failureCallback(@[@"No `url` or `message` to share"]); - return; - } - UIActivityViewController *share = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil]; - UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; - if ([share respondsToSelector:@selector(setCompletionWithItemsHandler:)]) { - share.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - if (activityError) { - failureCallback(@[[activityError localizedDescription]]); - } else { - successCallback(@[@(completed), (activityType ?: [NSNull null])]); - } - }; - } else { + NSMutableArray *items = [NSMutableArray array]; + id message = options[@"message"]; + id url = options[@"url"]; + if ([message isKindOfClass:[NSString class]]) { + [items addObject:message]; + } + if ([url isKindOfClass:[NSString class]]) { + [items addObject:[NSURL URLWithString:url]]; + } + if ([items count] == 0) { + failureCallback(@[@"No `url` or `message` to share"]); + return; + } + UIActivityViewController *share = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil]; + UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; + if ([share respondsToSelector:@selector(setCompletionWithItemsHandler:)]) { + share.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { + if (activityError) { + failureCallback(@[[activityError localizedDescription]]); + } else { + successCallback(@[@(completed), (activityType ?: [NSNull null])]); + } + }; + } else { #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 - if (![UIActivityViewController instancesRespondToSelector:@selector(completionWithItemsHandler)]) { - // Legacy iOS 7 implementation - share.completionHandler = ^(NSString *activityType, BOOL completed) { - successCallback(@[@(completed), (activityType ?: [NSNull null])]); - }; - } else + if (![UIActivityViewController instancesRespondToSelector:@selector(completionWithItemsHandler)]) { + // Legacy iOS 7 implementation + share.completionHandler = ^(NSString *activityType, BOOL completed) { + successCallback(@[@(completed), (activityType ?: [NSNull null])]); + }; + } else #endif - { - // iOS 8 version - share.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - successCallback(@[@(completed), (activityType ?: [NSNull null])]); - }; - } + { + // iOS 8 version + share.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { + successCallback(@[@(completed), (activityType ?: [NSNull null])]); + }; } - [ctrl presentViewController:share animated:YES completion:nil]; - }); + } + [ctrl presentViewController:share animated:YES completion:nil]; } #pragma mark UIActionSheetDelegate Methods - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { - NSString *key = keyForInstance(actionSheet); + NSString *key = RCTKeyForInstance(actionSheet); RCTResponseSenderBlock callback = _callbacks[key]; if (callback) { callback(@[@(buttonIndex)]); @@ -133,7 +129,7 @@ - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger #pragma mark Private -NS_INLINE NSString *keyForInstance(id instance) +static NSString *RCTKeyForInstance(id instance) { return [NSString stringWithFormat:@"%p", instance]; } diff --git a/Libraries/Animation/RCTAnimationExperimentalManager.m b/Libraries/Animation/RCTAnimationExperimentalManager.m index eb2ddd1cd81266..798c1456b6945f 100644 --- a/Libraries/Animation/RCTAnimationExperimentalManager.m +++ b/Libraries/Animation/RCTAnimationExperimentalManager.m @@ -68,6 +68,11 @@ - (instancetype)init return self; } +- (dispatch_queue_t)methodQueue +{ + return _bridge.uiManager.methodQueue; +} + - (id (^)(CGFloat))interpolateFrom:(CGFloat[])fromArray to:(CGFloat[])toArray count:(NSUInteger)count typeName:(const char *)typeName { if (count == 1) { diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index 6bb95acb0ff248..c5303df1511ac3 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -33,7 +33,9 @@ typedef NS_ENUM(NSInteger, RCTPositionErrorCode) { CLLocationAccuracy accuracy; } RCTLocationOptions; -static RCTLocationOptions RCTLocationOptionsWithJSON(id json) +@implementation RCTConvert (RCTLocationOptions) + ++ (RCTLocationOptions)RCTLocationOptions:(id)json { NSDictionary *options = [RCTConvert NSDictionary:json]; return (RCTLocationOptions){ @@ -43,6 +45,8 @@ static RCTLocationOptions RCTLocationOptionsWithJSON(id json) }; } +@end + static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /* nil for default */) { if (!msg) { @@ -121,6 +125,7 @@ - (instancetype)init - (void)dealloc { [_locationManager stopUpdatingLocation]; + _locationManager.delegate = nil; } #pragma mark - Private API @@ -153,41 +158,33 @@ - (void)timeout:(NSTimer *)timer #pragma mark - Public API -RCT_EXPORT_METHOD(startObserving:(NSDictionary *)optionsJSON) +RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options) { [self checkLocationConfig]; - dispatch_async(dispatch_get_main_queue(), ^{ - - // Select best options - _observerOptions = RCTLocationOptionsWithJSON(optionsJSON); - for (RCTLocationRequest *request in _pendingRequests) { - _observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy); - } - - _locationManager.desiredAccuracy = _observerOptions.accuracy; - [self beginLocationUpdates]; - _observingLocation = YES; + // Select best options + _observerOptions = options; + for (RCTLocationRequest *request in _pendingRequests) { + _observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy); + } - }); + _locationManager.desiredAccuracy = _observerOptions.accuracy; + [self beginLocationUpdates]; + _observingLocation = YES; } RCT_EXPORT_METHOD(stopObserving) { - dispatch_async(dispatch_get_main_queue(), ^{ - - // Stop observing - _observingLocation = NO; + // Stop observing + _observingLocation = NO; - // Stop updating if no pending requests - if (_pendingRequests.count == 0) { - [_locationManager stopUpdatingLocation]; - } - - }); + // Stop updating if no pending requests + if (_pendingRequests.count == 0) { + [_locationManager stopUpdatingLocation]; + } } -RCT_EXPORT_METHOD(getCurrentPosition:(NSDictionary *)optionsJSON +RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options withSuccessCallback:(RCTResponseSenderBlock)successBlock errorCallback:(RCTResponseSenderBlock)errorBlock) { @@ -198,56 +195,49 @@ - (void)timeout:(NSTimer *)timer return; } - dispatch_async(dispatch_get_main_queue(), ^{ - - if (![CLLocationManager locationServicesEnabled]) { - if (errorBlock) { - errorBlock(@[ - RCTPositionError(RCTPositionErrorUnavailable, @"Location services disabled.") - ]); - return; - } + if (![CLLocationManager locationServicesEnabled]) { + if (errorBlock) { + errorBlock(@[ + RCTPositionError(RCTPositionErrorUnavailable, @"Location services disabled.") + ]); + return; } + } - if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) { - if (errorBlock) { - errorBlock(@[ - RCTPositionError(RCTPositionErrorDenied, nil) - ]); - return; - } + if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) { + if (errorBlock) { + errorBlock(@[ + RCTPositionError(RCTPositionErrorDenied, nil) + ]); + return; } + } - // Get options - RCTLocationOptions options = RCTLocationOptionsWithJSON(optionsJSON); - - // Check if previous recorded location exists and is good enough - if (_lastLocationEvent && - CFAbsoluteTimeGetCurrent() - [RCTConvert NSTimeInterval:_lastLocationEvent[@"timestamp"]] < options.maximumAge && - [_lastLocationEvent[@"coords"][@"accuracy"] doubleValue] >= options.accuracy) { + // Check if previous recorded location exists and is good enough + if (_lastLocationEvent && + CFAbsoluteTimeGetCurrent() - [RCTConvert NSTimeInterval:_lastLocationEvent[@"timestamp"]] < options.maximumAge && + [_lastLocationEvent[@"coords"][@"accuracy"] doubleValue] >= options.accuracy) { - // Call success block with most recent known location - successBlock(@[_lastLocationEvent]); - return; - } + // Call success block with most recent known location + successBlock(@[_lastLocationEvent]); + return; + } - // Create request - RCTLocationRequest *request = [[RCTLocationRequest alloc] init]; - request.successBlock = successBlock; - request.errorBlock = errorBlock ?: ^(NSArray *args){}; - request.options = options; - request.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:options.timeout - target:self - selector:@selector(timeout:) - userInfo:request - repeats:NO]; - [_pendingRequests addObject:request]; - - // Configure location manager and begin updating location - _locationManager.desiredAccuracy = MIN(_locationManager.desiredAccuracy, options.accuracy); - [self beginLocationUpdates]; - - }); + // Create request + RCTLocationRequest *request = [[RCTLocationRequest alloc] init]; + request.successBlock = successBlock; + request.errorBlock = errorBlock ?: ^(NSArray *args){}; + request.options = options; + request.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:options.timeout + target:self + selector:@selector(timeout:) + userInfo:request + repeats:NO]; + [_pendingRequests addObject:request]; + + // Configure location manager and begin updating location + _locationManager.desiredAccuracy = MIN(_locationManager.desiredAccuracy, options.accuracy); + [self beginLocationUpdates]; } #pragma mark - CLLocationManagerDelegate diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m index 990c5900fee20d..4be8bfb8e45d5b 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.m +++ b/Libraries/LinkingIOS/RCTLinkingManager.m @@ -62,8 +62,10 @@ - (void)handleOpenURLNotification:(NSNotification *)notification RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL callback:(RCTResponseSenderBlock)callback) { - BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL]; - callback(@[@(canOpen)]); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL]; + callback(@[@(canOpen)]); + }); } - (NSDictionary *)constantsToExport diff --git a/Libraries/RCTTest/RCTTestModule.h b/Libraries/RCTTest/RCTTestModule.h index 21ed60c6b91f05..a8a2da16ee9aa2 100644 --- a/Libraries/RCTTest/RCTTestModule.h +++ b/Libraries/RCTTest/RCTTestModule.h @@ -15,14 +15,24 @@ @interface RCTTestModule : NSObject -// This is typically polled while running the runloop until true -@property (nonatomic, readonly, getter=isDone) BOOL done; - -// This is used to give meaningful names to snapshot image files. -@property (nonatomic, assign) SEL testSelector; +/** + * The snapshot test controller for this module. + */ +@property (nonatomic, weak) FBSnapshotTestController *controller; +/** + * This is the view to be snapshotted. + */ @property (nonatomic, weak) UIView *view; -- (instancetype)initWithSnapshotController:(FBSnapshotTestController *)controller view:(UIView *)view; +/** + * This is used to give meaningful names to snapshot image files. + */ +@property (nonatomic, assign) SEL testSelector; + +/** + * This is typically polled while running the runloop until true. + */ +@property (nonatomic, readonly, getter=isDone) BOOL done; @end diff --git a/Libraries/RCTTest/RCTTestModule.m b/Libraries/RCTTest/RCTTestModule.m index 58b6572f8bfa32..33f562515d629b 100644 --- a/Libraries/RCTTest/RCTTestModule.m +++ b/Libraries/RCTTest/RCTTestModule.m @@ -12,21 +12,25 @@ #import "FBSnapshotTestController.h" #import "RCTAssert.h" #import "RCTLog.h" +#import "RCTUIManager.h" @implementation RCTTestModule { - __weak FBSnapshotTestController *_snapshotController; - __weak UIView *_view; NSMutableDictionary *_snapshotCounter; } +@synthesize bridge = _bridge; + RCT_EXPORT_MODULE() -- (instancetype)initWithSnapshotController:(FBSnapshotTestController *)controller view:(UIView *)view +- (dispatch_queue_t)methodQueue +{ + return _bridge.uiManager.methodQueue; +} + +- (instancetype)init { if ((self = [super init])) { - _snapshotController = controller; - _view = view; _snapshotCounter = [NSMutableDictionary new]; } return self; @@ -34,30 +38,29 @@ - (instancetype)initWithSnapshotController:(FBSnapshotTestController *)controlle RCT_EXPORT_METHOD(verifySnapshot:(RCTResponseSenderBlock)callback) { - if (!_snapshotController) { - RCTLogWarn(@"No snapshot controller configured."); - callback(@[]); - return; - } + RCTAssert(_controller != nil, @"No snapshot controller configured."); + + [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { - dispatch_async(dispatch_get_main_queue(), ^{ NSString *testName = NSStringFromSelector(_testSelector); - _snapshotCounter[testName] = @([_snapshotCounter[testName] integerValue] + 1); + _snapshotCounter[testName] = [@([_snapshotCounter[testName] integerValue] + 1) stringValue]; + NSError *error = nil; - BOOL success = [_snapshotController compareSnapshotOfView:_view - selector:_testSelector - identifier:[_snapshotCounter[testName] stringValue] - error:&error]; + BOOL success = [_controller compareSnapshotOfView:_view + selector:_testSelector + identifier:_snapshotCounter[testName] + error:&error]; + RCTAssert(success, @"Snapshot comparison failed: %@", error); callback(@[]); - }); + }]; } RCT_EXPORT_METHOD(markTestCompleted) { - dispatch_async(dispatch_get_main_queue(), ^{ + [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { _done = YES; - }); + }]; } @end diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index 9b3a7d3c89881b..75a8118318ee10 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -19,7 +19,7 @@ @implementation RCTTestRunner { - FBSnapshotTestController *_snapshotController; + FBSnapshotTestController *_testController; } - (instancetype)initWithApp:(NSString *)app referenceDir:(NSString *)referenceDir @@ -27,8 +27,8 @@ - (instancetype)initWithApp:(NSString *)app referenceDir:(NSString *)referenceDi if ((self = [super init])) { NSString *sanitizedAppName = [app stringByReplacingOccurrencesOfString:@"/" withString:@"-"]; sanitizedAppName = [sanitizedAppName stringByReplacingOccurrencesOfString:@"\\" withString:@"-"]; - _snapshotController = [[FBSnapshotTestController alloc] initWithTestName:sanitizedAppName]; - _snapshotController.referenceImagesDirectory = referenceDir; + _testController = [[FBSnapshotTestController alloc] initWithTestName:sanitizedAppName]; + _testController.referenceImagesDirectory = referenceDir; _scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.includeRequire.runModule.bundle?dev=true", app]]; } return self; @@ -36,12 +36,12 @@ - (instancetype)initWithApp:(NSString *)app referenceDir:(NSString *)referenceDi - (void)setRecordMode:(BOOL)recordMode { - _snapshotController.recordMode = recordMode; + _testController.recordMode = recordMode; } - (BOOL)recordMode { - return _snapshotController.recordMode; + return _testController.recordMode; } - (void)runTest:(SEL)test module:(NSString *)moduleName @@ -59,26 +59,21 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock { - UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; - if ([vc.view isKindOfClass:[RCTRootView class]]) { - [(RCTRootView *)vc.view invalidate]; // Make sure the normal app view doesn't interfere - } - vc.view = [[UIView alloc] init]; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:_scriptURL + moduleName:moduleName + launchOptions:nil]; + rootView.initialProperties = initialProps; + rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices - RCTTestModule *testModule = [[RCTTestModule alloc] initWithSnapshotController:_snapshotController view:nil]; + NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]); + RCTTestModule *testModule = rootView.bridge.modules[testModuleName]; + testModule.controller = _testController; testModule.testSelector = test; - RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_scriptURL - moduleProvider:^(){ - return @[testModule]; - } - launchOptions:nil]; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge - moduleName:moduleName]; testModule.view = rootView; + + UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController; + vc.view = [[UIView alloc] init]; [vc.view addSubview:rootView]; // Add as subview so it doesn't get resized - rootView.initialProperties = initialProps; - rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage]; diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index ab853851ca31e6..544e5e1a2bbbca 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -101,13 +101,6 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method; */ @property (nonatomic, copy, readonly) NSDictionary *modules; -/** - * The shadow queue is used to execute callbacks from the JavaScript code. All - * native hooks (e.g. exported module methods) will be executed on the shadow - * queue. - */ -@property (nonatomic, readonly) dispatch_queue_t shadowQueue; - /** * The launch options that were used to initialize the bridge. */ diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 5c6bcf7cb35a5b..7b1f95b69e88e5 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -792,6 +792,7 @@ - (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink @implementation RCTBridge { RCTSparseArray *_modulesByID; + RCTSparseArray *_queuesByID; NSDictionary *_modulesByName; id _javaScriptExecutor; Class _executorClass; @@ -824,13 +825,13 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL return self; } + - (void)setUp { Class executorClass = _executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class]; _javaScriptExecutor = [[executorClass alloc] init]; _latestJSExecutor = _javaScriptExecutor; _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; - _shadowQueue = dispatch_queue_create("com.facebook.React.ShadowQueue", DISPATCH_QUEUE_SERIAL); _displayLink = [[RCTDisplayLink alloc] initWithBridge:self]; _frameUpdateObservers = [[NSMutableSet alloc] init]; _scheduledCalls = [[NSMutableArray alloc] init]; @@ -848,21 +849,29 @@ - (void)setUp [RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) { NSString *moduleName = RCTModuleNamesByID[moduleID]; // Check if module instance has already been registered for this name - if ((_modulesByID[moduleID] = modulesByName[moduleName])) { + id module = modulesByName[moduleName]; + if (module) { // Preregistered instances takes precedence, no questions asked if (!preregisteredModules[moduleName]) { // It's OK to have a name collision as long as the second instance is nil RCTAssert([[moduleClass alloc] init] == nil, - @"Attempted to register RCTBridgeModule class %@ for the name '%@', \ - but name was already registered by class %@", moduleClass, + @"Attempted to register RCTBridgeModule class %@ for the name " + "'%@', but name was already registered by class %@", moduleClass, moduleName, [modulesByName[moduleName] class]); } + if ([module class] != moduleClass) { + RCTLogInfo(@"RCTBridgeModule of class %@ with name '%@' was encountered " + "in the project, but name was already registered by class %@." + "That's fine if it's intentional - just letting you know.", + moduleClass, moduleName, [modulesByName[moduleName] class]); + } } else { // Module name hasn't been used before, so go ahead and instantiate - id module = [[moduleClass alloc] init]; - if (module) { - _modulesByID[moduleID] = modulesByName[moduleName] = module; - } + module = [[moduleClass alloc] init]; + } + if (module) { + // Store module instance + _modulesByID[moduleID] = modulesByName[moduleName] = module; } }]; @@ -876,6 +885,14 @@ - (void)setUp } } + // Get method queue + _queuesByID = [[RCTSparseArray alloc] init]; + [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { + if ([module respondsToSelector:@selector(methodQueue)]) { + _queuesByID[moduleID] = [module methodQueue] ?: dispatch_get_main_queue(); + } + }]; + // Inject module data into JS context NSString *configJSON = RCTJSONStringify(@{ @"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName), @@ -1027,6 +1044,7 @@ - (void)invalidate // Release modules (breaks retain cycle if module has strong bridge reference) _modulesByID = nil; + _queuesByID = nil; _modulesByName = nil; } @@ -1203,6 +1221,8 @@ - (void)_handleBuffer:(id)buffer return; } + // TODO: if we sort the requests by module, we could dispatch once per + // module instead of per request, which would reduce the call overhead. for (NSUInteger i = 0; i < numRequests; i++) { @autoreleasepool { [self _handleRequestNumber:i @@ -1212,14 +1232,15 @@ - (void)_handleBuffer:(id)buffer } } - // TODO: only used by RCTUIManager - can we eliminate this special case? - dispatch_async(self.shadowQueue, ^{ - for (id module in _modulesByID.allObjects) { - if ([module respondsToSelector:@selector(batchDidComplete)]) { + // TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case? + [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { + if ([module respondsToSelector:@selector(batchDidComplete)]) { + dispatch_queue_t queue = _queuesByID[moduleID]; + dispatch_async(queue ?: dispatch_get_main_queue(), ^{ [module batchDidComplete]; - } + }); } - }); + }]; } - (BOOL)_handleRequestNumber:(NSUInteger)i @@ -1241,7 +1262,8 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i RCTModuleMethod *method = methods[methodID]; __weak RCTBridge *weakSelf = self; - dispatch_async(self.shadowQueue, ^{ + dispatch_queue_t queue = _queuesByID[moduleID]; + dispatch_async(queue ?: dispatch_get_main_queue(), ^{ __strong RCTBridge *strongSelf = weakSelf; if (!strongSelf.isValid) { diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index 70d5c76d092138..c8fa41c4438d53 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -89,6 +89,23 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response); __attribute__((__aligned__(1))) \ static const char *__rct_export_entry__[] = { __func__, #js_name, NULL } +/** + * The queue that will be used to call all exported methods. If omitted, this + * will default the main queue, which is recommended for any methods that + * interact with UIKit. If your methods perform heavy work such as filesystem + * or network access, you should return a custom serial queue. Example: + * + * - (dispatch_queue_t)methodQueue + * { + * return dispatch_queue_create("com.mydomain.FileQueue", DISPATCH_QUEUE_SERIAL); + * } + * + * Alternatively, if only some methods on the module should be executed on a + * background queue you can leave this method unimplemented, and simply + * dispatch_async() within the method itself. + */ +- (dispatch_queue_t)methodQueue; + /** * Injects constants into JS. These constants are made accessible via * NativeModules.ModuleName.X. This method is called when the module is diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index e3f5e85980a0c5..22cc7ec816b3ee 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -138,7 +138,7 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath); /** * Underlying implementation of RCT_ENUM_CONVERTER macro. Ignore this. */ -NSNumber *RCTConverterEnumValue(const char *, NSDictionary *, NSNumber *, id); +NSNumber *RCTConvertEnumValue(const char *, NSDictionary *, NSNumber *, id); #ifdef __cplusplus } @@ -190,7 +190,7 @@ RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter]) dispatch_once(&onceToken, ^{ \ mapping = values; \ }); \ - NSNumber *converted = RCTConverterEnumValue(#type, mapping, @(default), json); \ + NSNumber *converted = RCTConvertEnumValue(#type, mapping, @(default), json); \ return ((type(*)(id, SEL))objc_msgSend)(converted, @selector(getter)); \ } diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 25de29656dc98a..2aefe89405e96e 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -58,6 +58,10 @@ + (NSData *)NSData:(id)json + (NSURL *)NSURL:(id)json { + if (!json || json == (id)kCFNull) { + return nil; + } + if (![json isKindOfClass:[NSString class]]) { RCTLogError(@"Expected NSString for NSURL, received %@: %@", [json classForCoder], json); return nil; @@ -115,7 +119,7 @@ + (NSDate *)NSDate:(id)json // JS standard for time zones is minutes. RCT_CUSTOM_CONVERTER(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0]) -NSNumber *RCTConverterEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json) +NSNumber *RCTConvertEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json) { if (!json || json == (id)kCFNull) { return defaultValue; diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m index ae11ce52be14df..b97364e3809aad 100644 --- a/React/Modules/RCTAlertManager.m +++ b/React/Modules/RCTAlertManager.m @@ -64,37 +64,34 @@ - (instancetype)init return; } - dispatch_async(dispatch_get_main_queue(), ^{ - - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title - message:message - delegate:self - cancelButtonTitle:nil - otherButtonTitles:nil]; - - NSMutableArray *buttonKeys = [[NSMutableArray alloc] initWithCapacity:buttons.count]; - - NSInteger index = 0; - for (NSDictionary *button in buttons) { - if (button.count != 1) { - RCTLogError(@"Button definitions should have exactly one key."); - } - NSString *buttonKey = [button.allKeys firstObject]; - NSString *buttonTitle = [button[buttonKey] description]; - [alertView addButtonWithTitle:buttonTitle]; - if ([buttonKey isEqualToString: @"cancel"]) { - alertView.cancelButtonIndex = index; - } - [buttonKeys addObject:buttonKey]; - index ++; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title + message:message + delegate:self + cancelButtonTitle:nil + otherButtonTitles:nil]; + + NSMutableArray *buttonKeys = [[NSMutableArray alloc] initWithCapacity:buttons.count]; + + NSInteger index = 0; + for (NSDictionary *button in buttons) { + if (button.count != 1) { + RCTLogError(@"Button definitions should have exactly one key."); } + NSString *buttonKey = [button.allKeys firstObject]; + NSString *buttonTitle = [button[buttonKey] description]; + [alertView addButtonWithTitle:buttonTitle]; + if ([buttonKey isEqualToString: @"cancel"]) { + alertView.cancelButtonIndex = index; + } + [buttonKeys addObject:buttonKey]; + index ++; + } - [_alerts addObject:alertView]; - [_alertCallbacks addObject:callback ?: ^(id unused) {}]; - [_alertButtonKeys addObject:buttonKeys]; + [_alerts addObject:alertView]; + [_alertCallbacks addObject:callback ?: ^(id unused) {}]; + [_alertButtonKeys addObject:buttonKeys]; - [alertView show]; - }); + [alertView show]; } #pragma mark - UIAlertViewDelegate diff --git a/React/Modules/RCTAsyncLocalStorage.m b/React/Modules/RCTAsyncLocalStorage.m index 8e6d414cf083ef..2c01161d48fa93 100644 --- a/React/Modules/RCTAsyncLocalStorage.m +++ b/React/Modules/RCTAsyncLocalStorage.m @@ -61,20 +61,6 @@ static id RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut return nil; } -static dispatch_queue_t RCTFileQueue(void) -{ - static dispatch_queue_t fileQueue = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // All JS is single threaded, so a serial queue is our only option. - fileQueue = dispatch_queue_create("com.facebook.rkFile", DISPATCH_QUEUE_SERIAL); - dispatch_set_target_queue(fileQueue, - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); - }); - - return fileQueue; -} - #pragma mark - RCTAsyncLocalStorage @implementation RCTAsyncLocalStorage @@ -90,6 +76,11 @@ @implementation RCTAsyncLocalStorage RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL); +} + - (NSString *)_filePathForKey:(NSString *)key { NSString *safeFileName = RCTMD5Hash(key); @@ -196,99 +187,89 @@ - (id)_writeEntry:(NSArray *)entry return; } - dispatch_async(RCTFileQueue(), ^{ - id errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[@[errorOut], [NSNull null]]); - return; - } - NSMutableArray *errors; - NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:keys.count]; - for (NSString *key in keys) { - id keyError = [self _appendItemForKey:key toArray:result]; - RCTAppendError(keyError, &errors); - } - [self _writeManifest:&errors]; - callback(@[errors ?: [NSNull null], result]); - }); + id errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[@[errorOut], [NSNull null]]); + return; + } + NSMutableArray *errors; + NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:keys.count]; + for (NSString *key in keys) { + id keyError = [self _appendItemForKey:key toArray:result]; + RCTAppendError(keyError, &errors); + } + [self _writeManifest:&errors]; + callback(@[errors ?: [NSNull null], result]); } RCT_EXPORT_METHOD(multiSet:(NSArray *)kvPairs callback:(RCTResponseSenderBlock)callback) { - dispatch_async(RCTFileQueue(), ^{ - id errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[@[errorOut]]); - return; - } - NSMutableArray *errors; - for (NSArray *entry in kvPairs) { - id keyError = [self _writeEntry:entry]; - RCTAppendError(keyError, &errors); - } - [self _writeManifest:&errors]; - if (callback) { - callback(@[errors ?: [NSNull null]]); - } - }); + id errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[@[errorOut]]); + return; + } + NSMutableArray *errors; + for (NSArray *entry in kvPairs) { + id keyError = [self _writeEntry:entry]; + RCTAppendError(keyError, &errors); + } + [self _writeManifest:&errors]; + if (callback) { + callback(@[errors ?: [NSNull null]]); + } } RCT_EXPORT_METHOD(multiRemove:(NSArray *)keys callback:(RCTResponseSenderBlock)callback) { - dispatch_async(RCTFileQueue(), ^{ - id errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[@[errorOut]]); - return; - } - NSMutableArray *errors; - for (NSString *key in keys) { - id keyError = RCTErrorForKey(key); - if (!keyError) { - NSString *filePath = [self _filePathForKey:key]; - [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; - [_manifest removeObjectForKey:key]; - } - RCTAppendError(keyError, &errors); - } - [self _writeManifest:&errors]; - if (callback) { - callback(@[errors ?: [NSNull null]]); + id errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[@[errorOut]]); + return; + } + NSMutableArray *errors; + for (NSString *key in keys) { + id keyError = RCTErrorForKey(key); + if (!keyError) { + NSString *filePath = [self _filePathForKey:key]; + [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; + [_manifest removeObjectForKey:key]; } - }); + RCTAppendError(keyError, &errors); + } + [self _writeManifest:&errors]; + if (callback) { + callback(@[errors ?: [NSNull null]]); + } } RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback) { - dispatch_async(RCTFileQueue(), ^{ - id errorOut = [self _ensureSetup]; - if (!errorOut) { - NSError *error; - for (NSString *key in _manifest) { - NSString *filePath = [self _filePathForKey:key]; - [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; - } - [_manifest removeAllObjects]; - errorOut = [self _writeManifest:nil]; - } - if (callback) { - callback(@[errorOut ?: [NSNull null]]); + id errorOut = [self _ensureSetup]; + if (!errorOut) { + NSError *error; + for (NSString *key in _manifest) { + NSString *filePath = [self _filePathForKey:key]; + [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; } - }); + [_manifest removeAllObjects]; + errorOut = [self _writeManifest:nil]; + } + if (callback) { + callback(@[errorOut ?: [NSNull null]]); + } } RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback) { - dispatch_async(RCTFileQueue(), ^{ - id errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[errorOut, [NSNull null]]); - } else { - callback(@[[NSNull null], [_manifest allKeys]]); - } - }); + id errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[errorOut, [NSNull null]]); + } else { + callback(@[[NSNull null], [_manifest allKeys]]); + } } @end diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index 5be80133bcf96e..9e919f3a364bcf 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -48,13 +48,11 @@ - (instancetype)init } #ifdef DEBUG - [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; + [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; #else if (RCTReloadRetries < _maxReloadAttempts) { RCTReloadRetries++; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil]; - }); + [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil]; } else { NSError *error; const NSUInteger MAX_SANITIZED_LENGTH = 75; diff --git a/React/Modules/RCTSourceCode.m b/React/Modules/RCTSourceCode.m index 76e9190bc279ed..e29a05637de173 100644 --- a/React/Modules/RCTSourceCode.m +++ b/React/Modules/RCTSourceCode.m @@ -24,7 +24,6 @@ @implementation RCTSourceCode } else { failureCallback(@[RCTMakeError(@"Source code is not available", nil, nil)]); } - } @end diff --git a/React/Modules/RCTStatusBarManager.m b/React/Modules/RCTStatusBarManager.m index ad8ee1df6db4f0..149ad568e133ec 100644 --- a/React/Modules/RCTStatusBarManager.m +++ b/React/Modules/RCTStatusBarManager.m @@ -29,31 +29,25 @@ static BOOL RCTViewControllerBasedStatusBarAppearance() RCT_EXPORT_METHOD(setStyle:(UIStatusBarStyle)statusBarStyle animated:(BOOL)animated) { - dispatch_async(dispatch_get_main_queue(), ^{ - - if (RCTViewControllerBasedStatusBarAppearance()) { - RCTLogError(@"RCTStatusBarManager module requires that the \ - UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); - } else { - [[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle - animated:animated]; - } - }); + if (RCTViewControllerBasedStatusBarAppearance()) { + RCTLogError(@"RCTStatusBarManager module requires that the \ + UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); + } else { + [[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle + animated:animated]; + } } RCT_EXPORT_METHOD(setHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation) { - dispatch_async(dispatch_get_main_queue(), ^{ - - if (RCTViewControllerBasedStatusBarAppearance()) { - RCTLogError(@"RCTStatusBarManager module requires that the \ - UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); - } else { - [[UIApplication sharedApplication] setStatusBarHidden:hidden - withAnimation:animation]; - } - }); + if (RCTViewControllerBasedStatusBarAppearance()) { + RCTLogError(@"RCTStatusBarManager module requires that the \ + UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); + } else { + [[UIApplication sharedApplication] setStatusBarHidden:hidden + withAnimation:animation]; + } } - (NSDictionary *)constantsToExport diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index aaab5fae006c61..1f6e84d6afe98f 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -187,21 +187,17 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update interval:jsDuration targetTime:targetTime repeats:repeats]; - dispatch_async(dispatch_get_main_queue(), ^{ - _timers[callbackID] = timer; - [self startTimers]; - }); + _timers[callbackID] = timer; + [self startTimers]; } RCT_EXPORT_METHOD(deleteTimer:(NSNumber *)timerID) { if (timerID) { - dispatch_async(dispatch_get_main_queue(), ^{ - _timers[timerID] = nil; - if (_timers.count == 0) { - [self stopTimers]; - } - }); + _timers[timerID] = nil; + if (_timers.count == 0) { + [self stopTimers]; + } } else { RCTLogWarn(@"Called deleteTimer: with a nil timerID"); } diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index ae04f9a1d4c21c..961b5a0b599436 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -178,7 +178,7 @@ @interface RCTUIManager () @implementation RCTUIManager { - __weak dispatch_queue_t _shadowQueue; + dispatch_queue_t _shadowQueue; // Root views are only mutated on the shadow queue NSMutableSet *_rootViewTags; @@ -242,24 +242,12 @@ @implementation RCTUIManager }; } -/** - * This private constructor should only be called when creating - * isolated UIImanager instances for testing. Normal initialization - * is via -init:, which is called automatically by the bridge. - */ -- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue -{ - if ((self = [self init])) { - _shadowQueue = shadowQueue; - _viewManagers = [[NSMutableDictionary alloc] init]; - } - return self; -} - - (instancetype)init { if ((self = [super init])) { + _shadowQueue = dispatch_queue_create("com.facebook.React.ShadowQueue", DISPATCH_QUEUE_SERIAL); + _pendingUIBlocksLock = [[NSLock alloc] init]; _defaultShadowViews = [[NSMutableDictionary alloc] init]; @@ -310,7 +298,6 @@ - (void)setBridge:(RCTBridge *)bridge RCTAssert(_bridge == nil, @"Should not re-use same UIIManager instance"); _bridge = bridge; - _shadowQueue = _bridge.shadowQueue; _shadowViewRegistry = [[RCTSparseArray alloc] init]; // Get view managers from bridge @@ -328,6 +315,11 @@ - (void)setBridge:(RCTBridge *)bridge _viewConfigs = [viewConfigs copy]; } +- (dispatch_queue_t)methodQueue +{ + return _shadowQueue; +} + - (void)registerRootView:(UIView *)rootView; { RCTAssertMainThread(); @@ -366,7 +358,7 @@ - (void)setFrame:(CGRect)frame forRootView:(UIView *)rootView NSNumber *reactTag = rootView.reactTag; RCTAssert(RCTIsReactRootView(reactTag), @"Specified view %@ is not a root view", reactTag); - dispatch_async(_bridge.shadowQueue, ^{ + dispatch_async(_shadowQueue, ^{ RCTShadowView *rootShadowView = _shadowViewRegistry[reactTag]; RCTAssert(rootShadowView != nil, @"Could not locate root view with tag #%@", reactTag); rootShadowView.frame = frame; @@ -396,8 +388,6 @@ - (void)_purgeChildren:(NSArray *)children fromRegistry:(RCTSparseArray *)regist - (void)addUIBlock:(RCTViewManagerUIBlock)block { - RCTAssert(![NSThread isMainThread], @"This method should only be called on the shadow thread"); - if (!self.isValid) { return; } @@ -417,7 +407,7 @@ - (void)addUIBlock:(RCTViewManagerUIBlock)block - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)rootShadowView { - RCTAssert(![NSThread isMainThread], @"This should never be executed on main thread."); + RCTAssert(![NSThread isMainThread], @"Should be called on shadow thread"); NSMutableSet *viewsWithNewFrames = [NSMutableSet setWithCapacity:1]; @@ -679,6 +669,8 @@ - (void)_manageChildren:(NSNumber *)containerReactTag [self _purgeChildren:permanentlyRemovedChildren fromRegistry:registry]; + // TODO (#5906496): optimize all these loops - constantly calling array.count is not efficient + // Figure out what to insert - merge temporary inserts and adds NSMutableDictionary *destinationsToChildrenToAdd = [NSMutableDictionary dictionary]; for (NSInteger index = 0, length = temporarilyRemovedChildren.count; index < length; index++) { @@ -886,8 +878,6 @@ - (void)batchDidComplete - (void)flushUIBlocks { - RCTAssert(![NSThread isMainThread], @"Should be called on shadow thread"); - // First copy the previous blocks into a temporary variable, then reset the // pending blocks to a new array. This guards against mutation while // processing the pending blocks in another thread. diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index 74c7bbd8ce7a17..ed97cf3b2bf3f0 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -109,8 +109,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v * within the view or shadowView. */ #define RCT_REMAP_VIEW_PROPERTY(name, keyPath, type) \ -RCT_EXPORT_VIEW_PROP_CONFIG(name, type) \ -- (void)set_##name:(id)json forView:(id)view withDefaultView:(id)defaultView { \ +RCT_CUSTOM_VIEW_PROPERTY(name, type, UIView) { \ if ((json && !RCTSetProperty(view, @#keyPath, @selector(type:), json)) || \ (!json && !RCTCopyProperty(view, defaultView, @#keyPath))) { \ RCTLogError(@"%@ does not have setter for `%s` property", [view class], #name); \ @@ -118,8 +117,7 @@ RCT_EXPORT_VIEW_PROP_CONFIG(name, type) \ } #define RCT_REMAP_SHADOW_PROPERTY(name, keyPath, type) \ -RCT_EXPORT_SHADOW_PROP_CONFIG(name, type) \ -- (void)set_##name:(id)json forShadowView:(id)view withDefaultView:(id)defaultView { \ +RCT_CUSTOM_SHADOW_PROPERTY(name, type, RCTShadowView) { \ if ((json && !RCTSetProperty(view, @#keyPath, @selector(type:), json)) || \ (!json && !RCTCopyProperty(view, defaultView, @#keyPath))) { \ RCTLogError(@"%@ does not have setter for `%s` property", [view class], #name); \ @@ -132,11 +130,11 @@ RCT_EXPORT_SHADOW_PROP_CONFIG(name, type) \ * refer to "json", "view" and "defaultView" to implement the required logic. */ #define RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass) \ -RCT_EXPORT_VIEW_PROP_CONFIG(name, type) \ ++ (NSDictionary *)getPropConfigView_##name { return @{@"name": @#name, @"type": @#type}; } \ - (void)set_##name:(id)json forView:(viewClass *)view withDefaultView:(viewClass *)defaultView #define RCT_CUSTOM_SHADOW_PROPERTY(name, type, viewClass) \ -RCT_EXPORT_SHADOW_PROP_CONFIG(name, type) \ ++ (NSDictionary *)getPropConfigShadow_##name { return @{@"name": @#name, @"type": @#type}; } \ - (void)set_##name:(id)json forShadowView:(viewClass *)view withDefaultView:(viewClass *)defaultView /** @@ -164,17 +162,4 @@ RCT_EXPORT_SHADOW_PROP_CONFIG(name, type) \ [self set_##newName:json forView:view withDefaultView:defaultView]; \ } -/** - * PROP_CONFIG macros should only be paired with property setters. - */ -#define RCT_EXPORT_VIEW_PROP_CONFIG(name, type) \ -+ (NSDictionary *)getPropConfigView_##name { \ - return @{@"name": @#name, @"type": @#type}; \ -} - -#define RCT_EXPORT_SHADOW_PROP_CONFIG(name, type) \ -+ (NSDictionary *)getPropConfigShadow_##name { \ - return @{@"name": @#name, @"type": @#type}; \ -} - @end diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 3c848537454872..8388b83cfb489c 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -14,6 +14,7 @@ #import "RCTEventDispatcher.h" #import "RCTLog.h" #import "RCTShadowView.h" +#import "RCTUIManager.h" #import "RCTUtils.h" #import "RCTView.h" @@ -23,6 +24,11 @@ @implementation RCTViewManager RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return [_bridge.uiManager methodQueue]; +} + - (UIView *)view { return [[RCTView alloc] init]; From 2186691812d5a0b79f768ec5214a87f3a7f28862 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Sat, 18 Apr 2015 18:50:29 -0100 Subject: [PATCH 016/250] Revert "[ReactNative] implement transform styles" --- .../Components/View/ViewStylePropTypes.js | 6 +- Libraries/ReactIOS/NativeMethodsMixin.js | 3 +- Libraries/ReactIOS/ReactIOSNativeComponent.js | 3 +- Libraries/StyleSheet/precomputeStyle.js | 161 ------------------ Libraries/Utilities/MatrixMath.js | 131 -------------- 5 files changed, 7 insertions(+), 297 deletions(-) delete mode 100644 Libraries/StyleSheet/precomputeStyle.js delete mode 100755 Libraries/Utilities/MatrixMath.js diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index 73afe99bdb70c0..bb22c6b26511bf 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -34,8 +34,12 @@ var ViewStylePropTypes = { ), shadowOpacity: ReactPropTypes.number, shadowRadius: ReactPropTypes.number, - transform: ReactPropTypes.arrayOf(ReactPropTypes.object), transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), + rotation: ReactPropTypes.number, + scaleX: ReactPropTypes.number, + scaleY: ReactPropTypes.number, + translateX: ReactPropTypes.number, + translateY: ReactPropTypes.number, }; module.exports = ViewStylePropTypes; diff --git a/Libraries/ReactIOS/NativeMethodsMixin.js b/Libraries/ReactIOS/NativeMethodsMixin.js index 9d413e5c7ca7a1..ec72a0b4f4696b 100644 --- a/Libraries/ReactIOS/NativeMethodsMixin.js +++ b/Libraries/ReactIOS/NativeMethodsMixin.js @@ -19,7 +19,6 @@ var TextInputState = require('TextInputState'); var flattenStyle = require('flattenStyle'); var invariant = require('invariant'); var mergeFast = require('mergeFast'); -var precomputeStyle = require('precomputeStyle'); type MeasureOnSuccessCallback = ( x: number, @@ -94,7 +93,7 @@ var NativeMethodsMixin = { break; } } - var style = precomputeStyle(flattenStyle(nativeProps.style)); + var style = flattenStyle(nativeProps.style); var props = null; if (hasOnlyStyle) { diff --git a/Libraries/ReactIOS/ReactIOSNativeComponent.js b/Libraries/ReactIOS/ReactIOSNativeComponent.js index 7f27ae0ea7dae3..b9abd5965c5f07 100644 --- a/Libraries/ReactIOS/ReactIOSNativeComponent.js +++ b/Libraries/ReactIOS/ReactIOSNativeComponent.js @@ -23,7 +23,6 @@ var styleDiffer = require('styleDiffer'); var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev'); var diffRawProperties = require('diffRawProperties'); var flattenStyle = require('flattenStyle'); -var precomputeStyle = require('precomputeStyle'); var warning = require('warning'); var registrationNames = ReactIOSEventEmitter.registrationNames; @@ -161,7 +160,7 @@ ReactIOSNativeComponent.Mixin = { // before actually doing the expensive flattening operation in order to // compute the diff. if (styleDiffer(nextProps.style, prevProps.style)) { - var nextFlattenedStyle = precomputeStyle(flattenStyle(nextProps.style)); + var nextFlattenedStyle = flattenStyle(nextProps.style); updatePayload = diffRawProperties( updatePayload, this.previousFlattenedStyle, diff --git a/Libraries/StyleSheet/precomputeStyle.js b/Libraries/StyleSheet/precomputeStyle.js deleted file mode 100644 index c3a1ca8e5cb1ed..00000000000000 --- a/Libraries/StyleSheet/precomputeStyle.js +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule precomputeStyle - * @flow - */ -'use strict'; - -var MatrixMath = require('MatrixMath'); -var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev'); -var invariant = require('invariant'); - -/** - * This method provides a hook where flattened styles may be precomputed or - * otherwise prepared to become better input data for native code. - */ -function precomputeStyle(style: ?Object): ?Object { - if (!style || !style.transform) { - return style; - } - invariant( - !style.transformMatrix, - 'transformMatrix and transform styles cannot be used on the same component' - ); - var newStyle = _precomputeTransforms({...style}); - deepFreezeAndThrowOnMutationInDev(newStyle); - return newStyle; -} - -/** - * Generate a transform matrix based on the provided transforms, and use that - * within the style object instead. - * - * This allows us to provide an API that is similar to CSS and to have a - * universal, singular interface to native code. - */ -function _precomputeTransforms(style: Object): Object { - var {transform, transformMatrix, ...style} = style; - var result = MatrixMath.createIdentityMatrix(); - - transform.forEach(transformation => { - var key = Object.keys(transformation)[0]; - var value = transformation[key]; - if (__DEV__) { - _validateTransform(key, value, transformation); - } - - switch (key) { - case 'matrix': - MatrixMath.multiplyInto(result, result, value); - break; - case 'rotate': - _multiplyTransform(result, MatrixMath.reuseRotateZCommand, [_convertToRadians(value)]); - break; - case 'scale': - _multiplyTransform(result, MatrixMath.reuseScaleCommand, [value]); - break; - case 'scaleX': - _multiplyTransform(result, MatrixMath.reuseScaleXCommand, [value]); - break; - case 'scaleY': - _multiplyTransform(result, MatrixMath.reuseScaleYCommand, [value]); - break; - case 'translate': - _multiplyTransform(result, MatrixMath.reuseTranslate3dCommand, [value[0], value[1], value[2] || 0]); - break; - case 'translateX': - _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [value, 0]); - break; - case 'translateY': - _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [0, value]); - break; - default: - throw new Error('Invalid transform name: ' + key); - } - }); - - return { - ...style, - transformMatrix: result, - }; -} - -/** - * Performs a destructive operation on a transform matrix. - */ -function _multiplyTransform( - result: Array, - matrixMathFunction: Function, - args: Array -): void { - var matrixToApply = MatrixMath.createIdentityMatrix(); - var argsWithIdentity = [matrixToApply].concat(args); - matrixMathFunction.apply(this, argsWithIdentity); - MatrixMath.multiplyInto(result, result, matrixToApply); -} - -/** - * Parses a string like '0.5rad' or '60deg' into radians expressed in a float. - * Note that validation on the string is done in `_validateTransform()`. - */ -function _convertToRadians(value: string): number { - var floatValue = parseFloat(value, 10); - return value.indexOf('rad') > -1 ? floatValue : floatValue * Math.PI / 180; -} - -function _validateTransform(key, value, transformation) { - var multivalueTransforms = [ - 'matrix', - 'translate', - ]; - if (multivalueTransforms.indexOf(key) !== -1) { - invariant( - Array.isArray(value), - 'Transform with key of %s must have an array as the value: %s', - key, - JSON.stringify(transformation) - ); - } - switch (key) { - case 'matrix': - invariant( - value.length === 9 || value.length === 16, - 'Matrix transform must have a length of 9 (2d) or 16 (3d). ' + - 'Provided matrix has a length of %s: %s', - value.length, - JSON.stringify(transformation) - ); - break; - case 'translate': - break; - case 'rotate': - invariant( - typeof value === 'string', - 'Transform with key of "%s" must be a string: %s', - key, - JSON.stringify(transformation) - ); - invariant( - value.indexOf('deg') > -1 || value.indexOf('rad') > -1, - 'Rotate transform must be expressed in degrees (deg) or radians ' + - '(rad): %s', - JSON.stringify(transformation) - ); - break; - default: - invariant( - typeof value === 'number', - 'Transform with key of "%s" must be a number: %s', - key, - JSON.stringify(transformation) - ); - } -} - -module.exports = precomputeStyle; diff --git a/Libraries/Utilities/MatrixMath.js b/Libraries/Utilities/MatrixMath.js deleted file mode 100755 index 7f3d17c461b70a..00000000000000 --- a/Libraries/Utilities/MatrixMath.js +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2004-present Facebook. All Rights Reserved. - * - * @providesModule MatrixMath - */ -'use strict'; - -/** - * Memory conservative (mutative) matrix math utilities. Uses "command" - * matrices, which are reusable. - */ -var MatrixMath = { - createIdentityMatrix: function() { - return [ - 1,0,0,0, - 0,1,0,0, - 0,0,1,0, - 0,0,0,1 - ]; - }, - - createCopy: function(m) { - return [ - m[0], m[1], m[2], m[3], - m[4], m[5], m[6], m[7], - m[8], m[9], m[10], m[11], - m[12], m[13], m[14], m[15], - ]; - }, - - createTranslate2d: function(x, y) { - var mat = MatrixMath.createIdentityMatrix(); - MatrixMath.reuseTranslate2dCommand(mat, x, y); - return mat; - }, - - reuseTranslate2dCommand: function(matrixCommand, x, y) { - matrixCommand[12] = x; - matrixCommand[13] = y; - }, - - reuseTranslate3dCommand: function(matrixCommand, x, y, z) { - matrixCommand[12] = x; - matrixCommand[13] = y; - matrixCommand[14] = z; - }, - - createScale: function(factor) { - var mat = MatrixMath.createIdentityMatrix(); - MatrixMath.reuseScaleCommand(mat, factor); - return mat; - }, - - reuseScaleCommand: function(matrixCommand, factor) { - matrixCommand[0] = factor; - matrixCommand[5] = factor; - }, - - reuseScale3dCommand: function(matrixCommand, x, y, z) { - matrixCommand[0] = x; - matrixCommand[5] = y; - matrixCommand[10] = z; - }, - - reuseScaleXCommand(matrixCommand, factor) { - matrixCommand[0] = factor; - }, - - reuseScaleYCommand(matrixCommand, factor) { - matrixCommand[5] = factor; - }, - - reuseScaleZCommand(matrixCommand, factor) { - matrixCommand[10] = factor; - }, - - reuseRotateYCommand: function(matrixCommand, amount) { - matrixCommand[0] = Math.cos(amount); - matrixCommand[2] = Math.sin(amount); - matrixCommand[8] = Math.sin(-amount); - matrixCommand[10] = Math.cos(amount); - }, - - createRotateZ: function(radians) { - var mat = MatrixMath.createIdentityMatrix(); - MatrixMath.reuseRotateZCommand(mat, radians); - return mat; - }, - - // http://www.w3.org/TR/css3-transforms/#recomposing-to-a-2d-matrix - reuseRotateZCommand: function(matrixCommand, radians) { - matrixCommand[0] = Math.cos(radians); - matrixCommand[1] = Math.sin(radians); - matrixCommand[4] = -Math.sin(radians); - matrixCommand[5] = Math.cos(radians); - }, - - multiplyInto: function(out, a, b) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; - - var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; - out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; - out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; - out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; - out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - } - -}; - -module.exports = MatrixMath; From 0b21df4a34eaa0d2df88f7183bcb1dd1c07efee0 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sun, 19 Apr 2015 12:55:46 -0700 Subject: [PATCH 017/250] Improved logging and dev menu --- .../RCTAnimationExperimentalManager.m | 4 +- React/Base/RCTDevMenu.h | 45 +++- React/Base/RCTDevMenu.m | 228 +++++++++++++----- React/Base/RCTJavaScriptLoader.h | 9 +- React/Base/RCTJavaScriptLoader.m | 9 +- React/Base/RCTLog.m | 4 +- React/Base/RCTRootView.h | 6 - React/Base/RCTRootView.m | 20 -- React/Executors/RCTContextExecutor.m | 48 ++-- React/Modules/RCTExceptionsManager.m | 47 ++-- React/Modules/RCTUIManager.m | 13 +- 11 files changed, 300 insertions(+), 133 deletions(-) diff --git a/Libraries/Animation/RCTAnimationExperimentalManager.m b/Libraries/Animation/RCTAnimationExperimentalManager.m index 798c1456b6945f..64ee577fef30b6 100644 --- a/Libraries/Animation/RCTAnimationExperimentalManager.m +++ b/Libraries/Animation/RCTAnimationExperimentalManager.m @@ -264,7 +264,9 @@ static void RCTInvalidAnimationProp(RCTSparseArray *callbacks, NSNumber *tag, NS RCTAnimationExperimentalManager *strongSelf = weakSelf; NSNumber *reactTag = strongSelf->_animationRegistry[animationTag]; - if (!reactTag) return; + if (!reactTag) { + return; + } UIView *view = viewRegistry[reactTag]; for (NSString *animationKey in view.layer.animationKeys) { diff --git a/React/Base/RCTDevMenu.h b/React/Base/RCTDevMenu.h index a49e076e621743..8057e570869383 100644 --- a/React/Base/RCTDevMenu.h +++ b/React/Base/RCTDevMenu.h @@ -9,11 +9,50 @@ #import -@class RCTBridge; +#import "RCTBridge.h" +#import "RCTBridgeModule.h" +#import "RCTInvalidating.h" -@interface RCTDevMenu : NSObject +/** + * Developer menu, useful for exposing extra functionality when debugging. + */ +@interface RCTDevMenu : NSObject + +/** + * Is the menu enabled. The menu is enabled by default in debug mode, but + * you may wish to disable it so that you can provide your own shake handler. + */ +@property (nonatomic, assign) BOOL shakeToShow; + +/** + * Enables performance profiling. + */ +@property (nonatomic, assign) BOOL profilingEnabled; + +/** + * Enables automatic polling for JS code changes. Only applicable when + * running the app from a server. + */ +@property (nonatomic, assign) BOOL liveReloadEnabled; -- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; +/** + * The time between checks for code changes. Defaults to 1 second. + */ +@property (nonatomic, assign) NSTimeInterval liveReloadPeriod; + +/** + * Manually show the menu. This will. + */ - (void)show; @end + +/** + * This category makes the developer menu instance available via the + * RCTBridge, which is useful for any class that needs to access the menu. + */ +@interface RCTBridge (RCTDevMenu) + +@property (nonatomic, readonly) RCTDevMenu *devMenu; + +@end diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 7621b1955f3db8..f29201f9af3260 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -9,12 +9,13 @@ #import "RCTDevMenu.h" -#import "RCTRedBox.h" +#import "RCTBridge.h" +#import "RCTLog.h" #import "RCTRootView.h" #import "RCTSourceCode.h" -#import "RCTWebViewExecutor.h" +#import "RCTUtils.h" -@interface RCTBridge (RCTDevMenu) +@interface RCTBridge (Profiling) @property (nonatomic, copy, readonly) NSArray *profile; @@ -23,87 +24,206 @@ - (void)stopProfiling; @end +static NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification"; + +@implementation UIWindow (RCTDevMenu) + +- (void)RCT_motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event +{ + if (event.subtype == UIEventSubtypeMotionShake) { + [[NSNotificationCenter defaultCenter] postNotificationName:RCTShowDevMenuNotification object:nil]; + } +} + +@end + @interface RCTDevMenu () @end @implementation RCTDevMenu { - BOOL _liveReload; - __weak RCTBridge *_bridge; + NSTimer *_updateTimer; + UIActionSheet *_actionSheet; } -- (instancetype)initWithBridge:(RCTBridge *)bridge +@synthesize bridge = _bridge; + +RCT_EXPORT_MODULE() + ++ (void)initialize +{ + // We're swizzling here because it's poor form to override methods in a category, + // however UIWindow doesn't actually implement motionEnded:withEvent:, so there's + // no need to call the original implementation. + RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:)); +} + +- (instancetype)init { - if (self = [super init]) { - _bridge = bridge; + if ((self = [super init])) { + + _shakeToShow = YES; + _liveReloadPeriod = 1.0; // 1 second + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(showOnShake) + name:RCTShowDevMenuNotification + object:nil]; } return self; } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)showOnShake +{ + if (_shakeToShow) { + [self show]; + } +} + - (void)show { - NSString *debugTitleChrome = _bridge.executorClass != Nil && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging"; - NSString *debugTitleSafari = _bridge.executorClass == [RCTWebViewExecutor class] ? @"Disable Safari Debugging" : @"Enable Safari Debugging"; - NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload"; + if (_actionSheet) { + return; + } + + NSString *debugTitleChrome = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging"; + NSString *debugTitleSafari = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Enable Safari Debugging"; + NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload"; NSString *profilingTitle = _bridge.profile ? @"Stop Profiling" : @"Start Profiling"; - UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development" - delegate:self - cancelButtonTitle:@"Cancel" - destructiveButtonTitle:nil - otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, liveReloadTitle, profilingTitle, nil]; + + UIActionSheet *actionSheet = + [[UIActionSheet alloc] initWithTitle:@"React Native: Development" + delegate:self + cancelButtonTitle:@"Cancel" + destructiveButtonTitle:nil + otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, liveReloadTitle, profilingTitle, nil]; + actionSheet.actionSheetStyle = UIBarStyleBlack; - [actionSheet showInView:[[[[UIApplication sharedApplication] keyWindow] rootViewController] view]]; + [actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view]; } - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { - if (buttonIndex == 0) { - [_bridge reload]; - } else if (buttonIndex == 1) { - Class cls = NSClassFromString(@"RCTWebSocketExecutor"); - _bridge.executorClass = (_bridge.executorClass != cls) ? cls : nil; - [_bridge reload]; - } else if (buttonIndex == 2) { - Class cls = [RCTWebViewExecutor class]; - _bridge.executorClass = (_bridge.executorClass != cls) ? cls : Nil; - [_bridge reload]; - } else if (buttonIndex == 3) { - _liveReload = !_liveReload; - [self _pollAndReload]; - } else if (buttonIndex == 4) { - if (_bridge.profile) { - [_bridge stopProfiling]; - } else { - [_bridge startProfiling]; + _actionSheet = nil; + + switch (buttonIndex) { + case 0: { + [_bridge reload]; + break; + } + case 1: { + Class cls = NSClassFromString(@"RCTWebSocketExecutor"); + _bridge.executorClass = (_bridge.executorClass != cls) ? cls : nil; + [_bridge reload]; + break; + } + case 2: { + Class cls = NSClassFromString(@"RCTWebViewExecutor"); + _bridge.executorClass = (_bridge.executorClass != cls) ? cls : Nil; + [_bridge reload]; + break; + } + case 3: { + self.liveReloadEnabled = !_liveReloadEnabled; + break; + } + case 4: { + self.profilingEnabled = !_profilingEnabled; + break; } + default: + break; } } -- (void)_pollAndReload +- (void)setProfilingEnabled:(BOOL)enabled { - if (_liveReload) { - RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; - NSURL *url = sourceCodeModule.scriptURL; - NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url]; - [self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL]; + if (_profilingEnabled == enabled) { + return; + } + + _profilingEnabled = enabled; + if (_bridge.profile) { + [_bridge stopProfiling]; + } else { + [_bridge startProfiling]; } } -- (void)_checkForUpdates:(NSURL *)URL +- (void)setLiveReloadEnabled:(BOOL)enabled { - NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL]; - longPollRequest.timeoutInterval = 30; - NSHTTPURLResponse *response; - [NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil]; - - dispatch_async(dispatch_get_main_queue(), ^{ - if (_liveReload && response.statusCode == 205) { - [[RCTRedBox sharedInstance] dismiss]; - [_bridge reload]; - } - [self _pollAndReload]; - }); + if (_liveReloadEnabled == enabled) { + return; + } + + _liveReloadEnabled = enabled; + if (_liveReloadEnabled) { + + _updateTimer = [NSTimer scheduledTimerWithTimeInterval:_liveReloadPeriod + target:self + selector:@selector(pollForUpdates) + userInfo:nil + repeats:YES]; + } else { + + [_updateTimer invalidate]; + _updateTimer = nil; + } +} + +- (void)setLiveReloadPeriod:(NSTimeInterval)liveReloadPeriod +{ + _liveReloadPeriod = liveReloadPeriod; + if (_liveReloadEnabled) { + self.liveReloadEnabled = NO; + self.liveReloadEnabled = YES; + } +} + +- (void)pollForUpdates +{ + RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; + if (!sourceCodeModule) { + RCTLogError(@"RCTSourceCode module not found"); + self.liveReloadEnabled = NO; + } + + NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL]; + [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:longPollURL] + queue:[[NSOperationQueue alloc] init] + completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { + + NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response; + if (_liveReloadEnabled && HTTPResponse.statusCode == 205) { + [_bridge reload]; + } + }]; +} + +- (BOOL)isValid +{ + return !_liveReloadEnabled || _updateTimer != nil; +} + +- (void)invalidate +{ + [_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES]; + [_updateTimer invalidate]; + _updateTimer = nil; +} + +@end + +@implementation RCTBridge (RCTDevMenu) + +- (RCTDevMenu *)devMenu +{ + return self.modules[RCTBridgeModuleNameForClass([RCTDevMenu class])]; } @end diff --git a/React/Base/RCTJavaScriptLoader.h b/React/Base/RCTJavaScriptLoader.h index bdc551b4d22194..8d52529e67acfb 100755 --- a/React/Base/RCTJavaScriptLoader.h +++ b/React/Base/RCTJavaScriptLoader.h @@ -1,4 +1,11 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #import diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index baf2ca34455476..02785fb416e861 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -1,4 +1,11 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #import "RCTJavaScriptLoader.h" diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index 1770a20a2a6fb0..4b9653650041a3 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -31,8 +31,8 @@ + (void)logMessage:(NSString *)message level:(NSString *)level; static RCTLogFunction RCTCurrentLogFunction; static RCTLogLevel RCTCurrentLogThreshold; -void RCTLogSetup(void) __attribute__((constructor)); -void RCTLogSetup() +__attribute__((constructor)) +static void RCTLogSetup() { RCTCurrentLogFunction = RCTDefaultLogFunction; diff --git a/React/Base/RCTRootView.h b/React/Base/RCTRootView.h index 1227eba9473ba5..ee5a35d7f0be96 100644 --- a/React/Base/RCTRootView.h +++ b/React/Base/RCTRootView.h @@ -57,12 +57,6 @@ */ @property (nonatomic, strong) Class executorClass; -/** - * If YES will watch for shake gestures and show development menu - * with options like "Reload", "Enable Debugging", etc. - */ -@property (nonatomic, assign) BOOL enableDevMenu; - /** * The backing view controller of the root view. */ diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index c9a97dfd134e6a..24cfe8417101bd 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -13,7 +13,6 @@ #import "RCTBridge.h" #import "RCTContextExecutor.h" -#import "RCTDevMenu.h" #import "RCTEventDispatcher.h" #import "RCTKeyCommands.h" #import "RCTLog.h" @@ -42,7 +41,6 @@ - (NSNumber *)allocateRootTag; @implementation RCTRootView { - RCTDevMenu *_devMenu; RCTBridge *_bridge; RCTTouchHandler *_touchHandler; NSString *_moduleName; @@ -60,12 +58,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge self.backgroundColor = [UIColor whiteColor]; -#ifdef DEBUG - - _enableDevMenu = YES; - -#endif - _bridge = bridge; _moduleName = moduleName; @@ -120,18 +112,6 @@ - (BOOL)canBecomeFirstResponder return YES; } -- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event -{ - if (motion == UIEventSubtypeMotionShake && self.enableDevMenu) { - if (!_devMenu) { - _devMenu = [[RCTDevMenu alloc] initWithBridge:_bridge]; - } - [_devMenu show]; - } else { - [super motionEnded:motion withEvent:event]; - } -} - RCT_IMPORT_METHOD(AppRegistry, runApplication) RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer) diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 8475e2afa311f2..39616bf3506db1 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -85,7 +85,14 @@ static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, range:(NSRange){0, message.length} withTemplate:@"[$4$5] \t$2"]; - _RCTLogFormat(RCTLogLevelInfo, NULL, -1, @"%@", message); + // TODO: it would be good if log level was sent as a param, instead of this hack + RCTLogLevel level = RCTLogLevelInfo; + if ([message rangeOfString:@"error" options:NSCaseInsensitiveSearch].length) { + level = RCTLogLevelError; + } else if ([message rangeOfString:@"warning" options:NSCaseInsensitiveSearch].length) { + level = RCTLogLevelWarning; + } + _RCTLogFormat(level, NULL, -1, @"%@", message); } return JSValueMakeUndefined(context); @@ -126,8 +133,6 @@ static JSValueRef RCTNoop(JSContextRef context, JSObjectRef object, JSObjectRef + (void)runRunLoopThread { - // TODO (#5906496): Investigate exactly what this does and why - @autoreleasepool { // copy thread name to pthread name pthread_setname_np([[[NSThread currentThread] name] UTF8String]); @@ -273,11 +278,11 @@ - (void)executeJSCall:(NSString *)name } - (void)executeApplicationScript:(NSString *)script - sourceURL:(NSURL *)url + sourceURL:(NSURL *)sourceURL onComplete:(RCTJavaScriptCompleteBlock)onComplete { - RCTAssert(url != nil, @"url should not be nil"); - RCTAssert(onComplete != nil, @"onComplete block should not be nil"); + RCTAssert(sourceURL != nil, @"url should not be nil"); + __weak RCTContextExecutor *weakSelf = self; [self executeBlockOnJavaScriptQueue:^{ RCTContextExecutor *strongSelf = weakSelf; @@ -286,17 +291,18 @@ - (void)executeApplicationScript:(NSString *)script } JSValueRef jsError = NULL; JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script); - JSStringRef sourceURL = JSStringCreateWithCFString((__bridge CFStringRef)url.absoluteString); - JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, sourceURL, 0, &jsError); - JSStringRelease(sourceURL); + JSStringRef jsURL = JSStringCreateWithCFString((__bridge CFStringRef)sourceURL.absoluteString); + JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, &jsError); + JSStringRelease(jsURL); JSStringRelease(execJSString); - NSError *error; - if (!result) { - error = RCTNSErrorFromJSError(strongSelf->_context.ctx, jsError); + if (onComplete) { + NSError *error; + if (!result) { + error = RCTNSErrorFromJSError(strongSelf->_context.ctx, jsError); + } + onComplete(error); } - - onComplete(error); }]; } @@ -314,7 +320,7 @@ - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete { - RCTAssert(onComplete != nil, @"onComplete block should not be nil"); + #if DEBUG RCTAssert(RCTJSONParse(script, NULL) != nil, @"%@ wasn't valid JSON!", script); #endif @@ -333,19 +339,21 @@ - (void)injectJSONText:(NSString *)script NSString *errorDesc = [NSString stringWithFormat:@"Can't make JSON value from script '%@'", script]; RCTLogError(@"%@", errorDesc); - NSError *error = [NSError errorWithDomain:@"JS" code:2 userInfo:@{NSLocalizedDescriptionKey: errorDesc}]; - onComplete(error); + if (onComplete) { + NSError *error = [NSError errorWithDomain:@"JS" code:2 userInfo:@{NSLocalizedDescriptionKey: errorDesc}]; + onComplete(error); + } return; } JSObjectRef globalObject = JSContextGetGlobalObject(strongSelf->_context.ctx); - JSStringRef JSName = JSStringCreateWithCFString((__bridge CFStringRef)objectName); JSObjectSetProperty(strongSelf->_context.ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL); JSStringRelease(JSName); - onComplete(nil); + if (onComplete) { + onComplete(nil); + } }]; - } @end diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index 9e919f3a364bcf..ed30d874185465 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -19,10 +19,6 @@ @implementation RCTExceptionsManager NSUInteger _reloadRetries; } -#ifndef DEBUG -static NSUInteger RCTReloadRetries = 0; -#endif - RCT_EXPORT_MODULE() - (instancetype)initWithDelegate:(id)delegate @@ -47,27 +43,32 @@ - (instancetype)init return; } -#ifdef DEBUG +#if DEBUG + [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; + #else - if (RCTReloadRetries < _maxReloadAttempts) { - RCTReloadRetries++; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil]; + + static NSUInteger reloadRetries = 0; + const NSUInteger maxMessageLength = 75; + + if (reloadRetries < _maxReloadAttempts) { + + reloadRetries++; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification + object:nil]; + } else { - NSError *error; - const NSUInteger MAX_SANITIZED_LENGTH = 75; + // Filter out numbers so the same base errors are mapped to the same categories independent of incorrect values. NSString *pattern = @"[+-]?\\d+[,.]?\\d*"; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error]; - RCTAssert(error == nil, @"Bad regex pattern: %@", pattern); - NSString *sanitizedMessage = [regex stringByReplacingMatchesInString:message - options:0 - range:NSMakeRange(0, message.length) - withTemplate:@""]; - if (sanitizedMessage.length > MAX_SANITIZED_LENGTH) { - sanitizedMessage = [[sanitizedMessage substringToIndex:MAX_SANITIZED_LENGTH] stringByAppendingString:@"..."]; + NSString *sanitizedMessage = [message stringByReplacingOccurrencesOfString:pattern withString:@"" options:NSRegularExpressionSearch range:(NSRange){0, message.length}]; + + if (sanitizedMessage.length > maxMessageLength) { + sanitizedMessage = [[sanitizedMessage substringToIndex:maxMessageLength] stringByAppendingString:@"..."]; } - NSMutableString *prettyStack = [@"\n" mutableCopy]; + + NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"]; for (NSDictionary *frame in stack) { [prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]]; } @@ -75,13 +76,21 @@ - (instancetype)init NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage]; [NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack]; } + #endif + } RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message stack:(NSArray *)stack) { + +#if DEBUG + [[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack]; + +#endif + } @end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 961b5a0b599436..2830ce6b029582 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -999,7 +999,7 @@ static void RCTMeasureLayout(RCTShadowView *view, * Only layouts for views that are within the rect passed in are returned. Invokes the error callback if the * passed in parent view does not exist. Invokes the supplied callback with the array of computed layouts. */ -RCT_EXPORT_METHOD(measureViewsInRect:(NSDictionary *)rect +RCT_EXPORT_METHOD(measureViewsInRect:(CGRect)rect parentView:(NSNumber *)reactTag errorCallback:(RCTResponseSenderBlock)errorCallback callback:(RCTResponseSenderBlock)callback) @@ -1011,7 +1011,7 @@ static void RCTMeasureLayout(RCTShadowView *view, } NSArray *childShadowViews = [shadowView reactSubviews]; NSMutableArray *results = [[NSMutableArray alloc] initWithCapacity:[childShadowViews count]]; - CGRect layoutRect = [RCTConvert CGRect:rect]; + [childShadowViews enumerateObjectsUsingBlock:^(RCTShadowView *childShadowView, NSUInteger idx, BOOL *stop) { CGRect childLayout = [childShadowView measureLayoutRelativeToAncestor:shadowView]; @@ -1026,10 +1026,11 @@ static void RCTMeasureLayout(RCTShadowView *view, CGFloat width = childLayout.size.width; CGFloat height = childLayout.size.height; - if (leftOffset <= layoutRect.origin.x + layoutRect.size.width && - leftOffset + width >= layoutRect.origin.x && - topOffset <= layoutRect.origin.y + layoutRect.size.height && - topOffset + height >= layoutRect.origin.y) { + if (leftOffset <= rect.origin.x + rect.size.width && + leftOffset + width >= rect.origin.x && + topOffset <= rect.origin.y + rect.size.height && + topOffset + height >= rect.origin.y) { + // This view is within the layout rect NSDictionary *result = @{@"index": @(idx), @"left": @(leftOffset), From 0e67e33534e8f2e7652ad83f4395a64d955517d4 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 20 Apr 2015 02:09:11 -0700 Subject: [PATCH 018/250] [ReactNative] Ensure JS calls scheduled by a deallocated context don't fire --- .../RCTWebSocketExecutor.m | 12 ++-- React/Base/RCTBridge.m | 70 ++++++++++++------- React/Base/RCTJavaScriptExecutor.h | 17 +++++ React/Base/RCTJavaScriptLoader.m | 4 +- React/Executors/RCTContextExecutor.m | 3 +- React/Executors/RCTWebViewExecutor.m | 5 ++ 6 files changed, 76 insertions(+), 35 deletions(-) diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m index 784c91e12768fa..7fd817d53cfda1 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m @@ -82,7 +82,7 @@ - (BOOL)prepareJSRuntime { __block NSError *initError; dispatch_semaphore_t s = dispatch_semaphore_create(0); - [self sendMessage:@{@"method": @"prepareJSRuntime"} waitForReply:^(NSError *error, NSDictionary *reply) { + [self sendMessage:@{@"method": @"prepareJSRuntime"} context:nil waitForReply:^(NSError *error, NSDictionary *reply) { initError = error; dispatch_semaphore_signal(s); }]; @@ -111,7 +111,7 @@ - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error RCTLogError(@"WebSocket connection failed with error %@", error); } -- (void)sendMessage:(NSDictionary *)message waitForReply:(WSMessageCallback)callback +- (void)sendMessage:(NSDictionary *)message context:(NSNumber *)executorID waitForReply:(WSMessageCallback)callback { static NSUInteger lastID = 10000; @@ -122,6 +122,8 @@ - (void)sendMessage:(NSDictionary *)message waitForReply:(WSMessageCallback)call }]; callback(error, nil); return; + } else if (executorID && ![RCTGetExecutorID(self) isEqualToNumber:executorID]) { + return; } NSNumber *expectedID = @(lastID++); @@ -135,12 +137,12 @@ - (void)sendMessage:(NSDictionary *)message waitForReply:(WSMessageCallback)call - (void)executeApplicationScript:(NSString *)script sourceURL:(NSURL *)URL onComplete:(RCTJavaScriptCompleteBlock)onComplete { NSDictionary *message = @{@"method": NSStringFromSelector(_cmd), @"url": [URL absoluteString], @"inject": _injectedObjects}; - [self sendMessage:message waitForReply:^(NSError *error, NSDictionary *reply) { + [self sendMessage:message context:nil waitForReply:^(NSError *error, NSDictionary *reply) { onComplete(error); }]; } -- (void)executeJSCall:(NSString *)name method:(NSString *)method arguments:(NSArray *)arguments callback:(RCTJavaScriptCallback)onComplete +- (void)executeJSCall:(NSString *)name method:(NSString *)method arguments:(NSArray *)arguments context:(NSNumber *)executorID callback:(RCTJavaScriptCallback)onComplete { RCTAssert(onComplete != nil, @"callback was missing for exec JS call"); NSDictionary *message = @{ @@ -149,7 +151,7 @@ - (void)executeJSCall:(NSString *)name method:(NSString *)method arguments:(NSAr @"moduleMethod": method, @"arguments": arguments }; - [self sendMessage:message waitForReply:^(NSError *socketError, NSDictionary *reply) { + [self sendMessage:message context:executorID waitForReply:^(NSError *socketError, NSDictionary *reply) { if (socketError) { onComplete(nil, socketError); return; diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 7b1f95b69e88e5..a2ff0146bb30ce 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -234,12 +234,13 @@ @interface RCTBridge () - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method - arguments:(NSArray *)args; + arguments:(NSArray *)args + context:(NSNumber *)context; - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method - arguments:(NSArray *)args; - + arguments:(NSArray *)args + context:(NSNumber *)context; @end /** @@ -338,7 +339,7 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2]; #define RCT_ARG_BLOCK(_logic) \ - [argumentBlocks addObject:^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { \ + [argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) { \ _logic \ [invocation setArgument:&value atIndex:index]; \ }]; \ @@ -355,7 +356,8 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName __autoreleasing id value = (json ? ^(NSArray *args) { [bridge _invokeAndProcessModule:@"BatchedBridge" method:@"invokeCallbackAndReturnFlushedQueue" - arguments:@[json, args]]; + arguments:@[json, args] + context:context]; } : ^(NSArray *unused) {}); ) }; @@ -477,6 +479,7 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName - (void)invokeWithBridge:(RCTBridge *)bridge module:(id)module arguments:(NSArray *)arguments + context:(NSNumber *)context { #if DEBUG @@ -503,8 +506,8 @@ - (void)invokeWithBridge:(RCTBridge *)bridge NSUInteger index = 0; for (id json in arguments) { id arg = (json == [NSNull null]) ? nil : json; - void (^block)(RCTBridge *, NSInvocation *, NSUInteger, id) = _argumentBlocks[index]; - block(bridge, invocation, index + 2, arg); + void (^block)(RCTBridge *, NSNumber *, NSInvocation *, NSUInteger, id) = _argumentBlocks[index]; + block(bridge, context, invocation, index + 2, arg); index++; } @@ -653,7 +656,6 @@ - (NSString *)description return moduleConfig; } - /** * As above, but for local modules/methods, which represent JS classes * and methods that will be called by the native code via the bridge. @@ -801,7 +803,7 @@ @implementation RCTBridge RCTDisplayLink *_displayLink; NSMutableSet *_frameUpdateObservers; NSMutableArray *_scheduledCalls; - NSMutableArray *_scheduledCallbacks; + RCTSparseArray *_scheduledCallbacks; BOOL _loading; NSUInteger _startingTime; @@ -829,13 +831,13 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL - (void)setUp { Class executorClass = _executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class]; - _javaScriptExecutor = [[executorClass alloc] init]; + _javaScriptExecutor = RCTCreateExecutor(executorClass); _latestJSExecutor = _javaScriptExecutor; _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; _displayLink = [[RCTDisplayLink alloc] initWithBridge:self]; _frameUpdateObservers = [[NSMutableSet alloc] init]; _scheduledCalls = [[NSMutableArray alloc] init]; - _scheduledCallbacks = [[NSMutableArray alloc] init]; + _scheduledCallbacks = [[RCTSparseArray alloc] init]; // Register passed-in module instances NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init]; @@ -991,7 +993,6 @@ - (void)bindKeys } - - (NSDictionary *)modules { RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. " @@ -1072,7 +1073,8 @@ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args if (!_loading) { [self _invokeAndProcessModule:@"BatchedBridge" method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, args ?: @[]]]; + arguments:@[moduleID, methodID, args ?: @[]] + context:RCTGetExecutorID(_javaScriptExecutor)]; } } @@ -1093,13 +1095,15 @@ - (void)_immediatelyCallTimer:(NSNumber *)timer #if BATCHED_BRIDGE [self _actuallyInvokeAndProcessModule:@"BatchedBridge" method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, @[@[timer]]]]; + arguments:@[moduleID, methodID, @[@[timer]]] + context:RCTGetExecutorID(_javaScriptExecutor)]; #else [self _invokeAndProcessModule:@"BatchedBridge" method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, @[@[timer]]]]; + arguments:@[moduleID, methodID, @[@[timer]]] + context:RCTGetExecutorID(_javaScriptExecutor)]; #endif } } @@ -1108,6 +1112,7 @@ - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete: { RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil"); RCT_PROFILE_START(); + NSNumber *context = RCTGetExecutorID(_javaScriptExecutor); [_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) { RCT_PROFILE_END(js_call, scriptLoadError, @"initial_script"); if (scriptLoadError) { @@ -1119,10 +1124,11 @@ - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete: [_javaScriptExecutor executeJSCall:@"BatchedBridge" method:@"flushedQueue" arguments:@[] + context:context callback:^(id json, NSError *error) { RCT_PROFILE_END(js_call, error, @"initial_call", @"BatchedBridge.flushedQueue"); RCT_PROFILE_START(); - [self _handleBuffer:json]; + [self _handleBuffer:json context:context]; RCT_PROFILE_END(objc_call, json, @"batched_js_calls"); onComplete(error); }]; @@ -1131,7 +1137,7 @@ - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete: #pragma mark - Payload Generation -- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args +- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context { #if BATCHED_BRIDGE RCT_PROFILE_START(); @@ -1148,10 +1154,11 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg @"module": module, @"method": method, @"args": args, + @"context": context ?: @0, }; if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) { - [_scheduledCallbacks addObject:call]; + _scheduledCallbacks[args[0]] = call; } else { [_scheduledCalls addObject:call]; } @@ -1159,7 +1166,7 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg RCT_PROFILE_END(js_call, args, @"schedule", module, method); } -- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args +- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context { #endif [[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil]; @@ -1171,19 +1178,20 @@ - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)me RCT_PROFILE_END(js_call, args, moduleDotMethod); RCT_PROFILE_START(); - [self _handleBuffer:json]; + [self _handleBuffer:json context:context]; RCT_PROFILE_END(objc_call, json, @"batched_js_calls"); }; [_javaScriptExecutor executeJSCall:module method:method arguments:args + context:context callback:processResponse]; } #pragma mark - Payload Processing -- (void)_handleBuffer:(id)buffer +- (void)_handleBuffer:(id)buffer context:(NSNumber *)context { if (buffer == nil || buffer == (id)kCFNull) { return; @@ -1228,7 +1236,8 @@ - (void)_handleBuffer:(id)buffer [self _handleRequestNumber:i moduleID:[moduleIDs[i] integerValue] methodID:[methodIDs[i] integerValue] - params:paramsArrays[i]]; + params:paramsArrays[i] + context:context]; } } @@ -1247,6 +1256,7 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i moduleID:(NSUInteger)moduleID methodID:(NSUInteger)methodID params:(NSArray *)params + context:(NSNumber *)context { if (![params isKindOfClass:[NSArray class]]) { RCTLogError(@"Invalid module/method/params tuple for request #%zd", i); @@ -1280,7 +1290,7 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i } @try { - [method invokeWithBridge:strongSelf module:module arguments:params]; + [method invokeWithBridge:strongSelf module:module arguments:params context:context]; } @catch (NSException *exception) { RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception); @@ -1313,13 +1323,18 @@ - (void)_runScheduledCalls { #if BATCHED_BRIDGE - NSArray *calls = [_scheduledCallbacks arrayByAddingObjectsFromArray:_scheduledCalls]; + NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls]; + NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor); + calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) { + return [call[@"context"] isEqualToNumber:currentExecutorID]; + }]]; if (calls.count > 0) { _scheduledCalls = [[NSMutableArray alloc] init]; - _scheduledCallbacks = [[NSMutableArray alloc] init]; + _scheduledCallbacks = [[RCTSparseArray alloc] init]; [self _actuallyInvokeAndProcessModule:@"BatchedBridge" - method:@"processBatch" - arguments:@[calls]]; + method:@"processBatch" + arguments:@[calls] + context:RCTGetExecutorID(_javaScriptExecutor)]; } #endif @@ -1357,6 +1372,7 @@ + (void)logMessage:(NSString *)message level:(NSString *)level [_latestJSExecutor executeJSCall:@"RCTLog" method:@"logIfNoNativeHook" arguments:@[level, message] + context:RCTGetExecutorID(_latestJSExecutor) callback:^(id json, NSError *error) {}]; } diff --git a/React/Base/RCTJavaScriptExecutor.h b/React/Base/RCTJavaScriptExecutor.h index 57dff78e756700..2816c7a7a81f3d 100644 --- a/React/Base/RCTJavaScriptExecutor.h +++ b/React/Base/RCTJavaScriptExecutor.h @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import + #import #import "RCTInvalidating.h" @@ -27,6 +29,7 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error); - (void)executeJSCall:(NSString *)name method:(NSString *)method arguments:(NSArray *)arguments + context:(NSNumber *)executorID callback:(RCTJavaScriptCallback)onComplete; /** @@ -40,3 +43,17 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error); asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete; @end + +static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID"; +__used static id RCTCreateExecutor(Class executorClass) +{ + static NSUInteger executorID = 0; + id executor = [[executorClass alloc] init]; + objc_setAssociatedObject(executor, RCTJavaScriptExecutorID, @(++executorID), OBJC_ASSOCIATION_RETAIN); + return executor; +} + +__used static NSNumber *RCTGetExecutorID(id executor) +{ + return objc_getAssociatedObject(executor, RCTJavaScriptExecutorID); +} diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index 02785fb416e861..4eaaf278dd480e 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -140,9 +140,9 @@ - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onCom sourceCodeModule.scriptURL = scriptURL; sourceCodeModule.scriptText = rawText; - [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *_error) { + [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(_error); + onComplete(scriptError); }); }]; }]; diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 39616bf3506db1..71e4f45c30f3cc 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -229,13 +229,14 @@ - (void)dealloc - (void)executeJSCall:(NSString *)name method:(NSString *)method arguments:(NSArray *)arguments + context:(NSNumber *)executorID callback:(RCTJavaScriptCallback)onComplete { RCTAssert(onComplete != nil, @"onComplete block should not be nil"); __weak RCTContextExecutor *weakSelf = self; [self executeBlockOnJavaScriptQueue:^{ RCTContextExecutor *strongSelf = weakSelf; - if (!strongSelf || !strongSelf.isValid) { + if (!strongSelf || !strongSelf.isValid || ![RCTGetExecutorID(strongSelf) isEqualToNumber:executorID]) { return; } NSError *error; diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m index 55de44ab969479..0bc6fdfc81f6d4 100644 --- a/React/Executors/RCTWebViewExecutor.m +++ b/React/Executors/RCTWebViewExecutor.m @@ -76,10 +76,15 @@ - (UIWebView *)invalidateAndReclaimWebView - (void)executeJSCall:(NSString *)name method:(NSString *)method arguments:(NSArray *)arguments + context:(NSNumber *)executorID callback:(RCTJavaScriptCallback)onComplete { RCTAssert(onComplete != nil, @""); [self executeBlockOnJavaScriptQueue:^{ + if (!self.isValid || ![RCTGetExecutorID(self) isEqualToNumber:executorID]) { + return; + } + NSError *error; NSString *argsString = RCTJSONStringify(arguments, &error); if (!argsString) { From fde476f4e56bb9d07312743ef9a6529e398830f9 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Mon, 20 Apr 2015 02:47:20 -0700 Subject: [PATCH 019/250] [react_native] JS files from D2001617: [react_native] Add support for rendering to hardware textures on Android --- Libraries/Components/View/View.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index c981e41294b486..0fdfa1fc8b76c4 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -136,6 +136,20 @@ var View = React.createClass({ * (or one of its superviews). */ removeClippedSubviews: PropTypes.bool, + + /** + * Whether this view should render itself (and all of its children) into a + * single hardware texture on the GPU. + * + * On Android, this is useful for animations and interactions that only + * modify opacity, rotation, translation, and/or scale: in those cases, the + * view doesn't have to be redrawn and display lists don't need to be + * re-executed. The texture can just be re-used and re-composited with + * different parameters. The downside is that this can use up limited video + * memory, so this prop should be set back to false at the end of the + * interaction/animation. + */ + renderToHardwareTextureAndroid: PropTypes.bool, }, render: function() { From fb1fa12e89912ee5c19cc10ca95fa24577673639 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 20 Apr 2015 04:55:05 -0700 Subject: [PATCH 020/250] [ReactNative] Better profiling API + Fix overlaping events --- React/Base/RCTBridge.m | 109 ++++++------------- React/Base/RCTDevMenu.m | 7 +- React/Base/RCTLog.h | 5 + React/Base/RCTLog.m | 29 ++--- React/Base/RCTProfile.h | 103 ++++++++++++++++++ React/Base/RCTProfile.m | 149 ++++++++++++++++++++++++++ React/Executors/RCTContextExecutor.m | 13 +-- React/Modules/RCTUIManager.m | 5 + React/React.xcodeproj/project.pbxproj | 6 ++ 9 files changed, 328 insertions(+), 98 deletions(-) create mode 100644 React/Base/RCTProfile.h create mode 100644 React/Base/RCTProfile.m diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index a2ff0146bb30ce..46dcc1dfe321c1 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -22,6 +22,7 @@ #import "RCTJavaScriptLoader.h" #import "RCTKeyCommands.h" #import "RCTLog.h" +#import "RCTProfile.h" #import "RCTRedBox.h" #import "RCTRootView.h" #import "RCTSparseArray.h" @@ -48,39 +49,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { */ #define BATCHED_BRIDGE 1 -#ifdef DEBUG - -#define RCT_PROFILE_START() \ -_Pragma("clang diagnostic push") \ -_Pragma("clang diagnostic ignored \"-Wshadow\"") \ -NSTimeInterval __rct_profile_start = CACurrentMediaTime() \ -_Pragma("clang diagnostic pop") - -#define RCT_PROFILE_END(cat, args, profileName...) \ -do { \ -if (_profile) { \ - [_profileLock lock]; \ - [_profile addObject:@{ \ - @"name": [@[profileName] componentsJoinedByString: @"_"], \ - @"cat": @ #cat, \ - @"ts": @((NSUInteger)((__rct_profile_start - _startingTime) * 1e6)), \ - @"dur": @((NSUInteger)((CACurrentMediaTime() - __rct_profile_start) * 1e6)), \ - @"ph": @"X", \ - @"pid": @([[NSProcessInfo processInfo] processIdentifier]), \ - @"tid": [[NSThread currentThread] description], \ - @"args": args ?: [NSNull null], \ - }]; \ - [_profileLock unlock]; \ -} \ -} while(0) - -#else - -#define RCT_PROFILE_START(...) -#define RCT_PROFILE_END(...) - -#endif - #ifdef __LP64__ typedef uint64_t RCTHeaderValue; typedef struct section_64 RCTHeaderSection; @@ -230,8 +198,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { @interface RCTBridge () -@property (nonatomic, copy, readonly) NSArray *profile; - - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args @@ -250,6 +216,7 @@ @interface RCTModuleMethod : NSObject @property (nonatomic, copy, readonly) NSString *moduleClassName; @property (nonatomic, copy, readonly) NSString *JSMethodName; +@property (nonatomic, assign, readonly) SEL selector; @end @@ -805,10 +772,6 @@ @implementation RCTBridge NSMutableArray *_scheduledCalls; RCTSparseArray *_scheduledCallbacks; BOOL _loading; - - NSUInteger _startingTime; - NSMutableArray *_profile; - NSLock *_profileLock; } static id _latestJSExecutor; @@ -1111,25 +1074,28 @@ - (void)_immediatelyCallTimer:(NSNumber *)timer - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete { RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil"); - RCT_PROFILE_START(); - NSNumber *context = RCTGetExecutorID(_javaScriptExecutor); + RCTProfileBeginEvent(); [_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) { - RCT_PROFILE_END(js_call, scriptLoadError, @"initial_script"); + RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError); if (scriptLoadError) { onComplete(scriptLoadError); return; } - RCT_PROFILE_START(); + RCTProfileBeginEvent(); + NSNumber *context = RCTGetExecutorID(_javaScriptExecutor); [_javaScriptExecutor executeJSCall:@"BatchedBridge" method:@"flushedQueue" arguments:@[] context:context callback:^(id json, NSError *error) { - RCT_PROFILE_END(js_call, error, @"initial_call", @"BatchedBridge.flushedQueue"); - RCT_PROFILE_START(); + RCTProfileEndEvent(@"FetchApplicationScriptCallbacks", @"js_call,init", @{ + @"json": json ?: [NSNull null], + @"error": error ?: [NSNull null], + }); + [self _handleBuffer:json context:context]; - RCT_PROFILE_END(objc_call, json, @"batched_js_calls"); + onComplete(error); }]; }]; @@ -1140,7 +1106,7 @@ - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete: - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context { #if BATCHED_BRIDGE - RCT_PROFILE_START(); + RCTProfileBeginEvent(); if ([module isEqualToString:@"RCTEventEmitter"]) { for (NSDictionary *call in _scheduledCalls) { @@ -1163,7 +1129,7 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg [_scheduledCalls addObject:call]; } - RCT_PROFILE_END(js_call, args, @"schedule", module, method); + RCTProfileEndEvent(@"enqueue_call", @"objc_call", call); } - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context @@ -1171,15 +1137,9 @@ - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)me #endif [[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil]; - NSString *moduleDotMethod = [NSString stringWithFormat:@"%@.%@", module, method]; - RCT_PROFILE_START(); RCTJavaScriptCallback processResponse = ^(id json, NSError *error) { [[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil]; - RCT_PROFILE_END(js_call, args, moduleDotMethod); - - RCT_PROFILE_START(); [self _handleBuffer:json context:context]; - RCT_PROFILE_END(objc_call, json, @"batched_js_calls"); }; [_javaScriptExecutor executeJSCall:module @@ -1271,9 +1231,17 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i } RCTModuleMethod *method = methods[methodID]; + // Look up module + id module = self->_modulesByID[moduleID]; + if (!module) { + RCTLogError(@"No module found for name '%@'", RCTModuleNamesByID[moduleID]); + return NO; + } + __weak RCTBridge *weakSelf = self; dispatch_queue_t queue = _queuesByID[moduleID]; dispatch_async(queue ?: dispatch_get_main_queue(), ^{ + RCTProfileBeginEvent(); __strong RCTBridge *strongSelf = weakSelf; if (!strongSelf.isValid) { @@ -1282,13 +1250,6 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i return; } - // Look up module - id module = strongSelf->_modulesByID[moduleID]; - if (!module) { - RCTLogError(@"No module found for name '%@'", RCTModuleNamesByID[moduleID]); - return; - } - @try { [method invokeWithBridge:strongSelf module:module arguments:params context:context]; } @@ -1298,6 +1259,12 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i @throw; } } + + RCTProfileEndEvent(@"Invoke callback", @"objc_call", @{ + @"module": method.moduleClassName, + @"method": method.JSMethodName, + @"selector": NSStringFromSelector(method.selector), + }); }); return YES; @@ -1305,7 +1272,8 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i - (void)_update:(CADisplayLink *)displayLink { - RCT_PROFILE_START(); + RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); + RCTProfileBeginEvent(); RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink]; for (id observer in _frameUpdateObservers) { @@ -1316,7 +1284,7 @@ - (void)_update:(CADisplayLink *)displayLink [self _runScheduledCalls]; - RCT_PROFILE_END(display_link, nil, @"main_thread"); + RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil); } - (void)_runScheduledCalls @@ -1382,23 +1350,12 @@ - (void)startProfiling RCTLogError(@"To run the profiler you must be running from the dev server"); return; } - _profileLock = [[NSLock alloc] init]; - _startingTime = CACurrentMediaTime(); - - [_profileLock lock]; - _profile = [[NSMutableArray alloc] init]; - [_profileLock unlock]; + RCTProfileInit(); } - (void)stopProfiling { - [_profileLock lock]; - NSArray *profile = _profile; - _profile = nil; - [_profileLock unlock]; - _profileLock = nil; - - NSString *log = RCTJSONStringify(profile, NULL); + NSString *log = RCTProfileEnd(); NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", _bundleURL.scheme, _bundleURL.host, _bundleURL.port]; NSURL *URL = [NSURL URLWithString:URLString]; NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL]; diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index f29201f9af3260..4af9d4e62c118a 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -11,14 +11,13 @@ #import "RCTBridge.h" #import "RCTLog.h" +#import "RCTProfile.h" #import "RCTRootView.h" #import "RCTSourceCode.h" #import "RCTUtils.h" @interface RCTBridge (Profiling) -@property (nonatomic, copy, readonly) NSArray *profile; - - (void)startProfiling; - (void)stopProfiling; @@ -94,7 +93,7 @@ - (void)show NSString *debugTitleChrome = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging"; NSString *debugTitleSafari = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Enable Safari Debugging"; NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload"; - NSString *profilingTitle = _bridge.profile ? @"Stop Profiling" : @"Start Profiling"; + NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling"; UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development" @@ -148,7 +147,7 @@ - (void)setProfilingEnabled:(BOOL)enabled } _profilingEnabled = enabled; - if (_bridge.profile) { + if (RCTProfileIsProfiling()) { [_bridge stopProfiling]; } else { [_bridge startProfiling]; diff --git a/React/Base/RCTLog.h b/React/Base/RCTLog.h index 7ffd860068d74e..3a8a70d5b6af34 100644 --- a/React/Base/RCTLog.h +++ b/React/Base/RCTLog.h @@ -45,6 +45,11 @@ typedef void (^RCTLogFunction)( NSString *message ); +/** + * Get a given thread's name (or the current queue, iff in debug mode) + */ +NSString *RCTThreadName(NSThread *); + /** * A method to generate a string from a collection of log data. To omit any * particular data from the log, just pass nil or zero for the argument. diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index 4b9653650041a3..449980fc3b9620 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -98,6 +98,22 @@ void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix) [prefixStack removeLastObject]; } +NSString *RCTThreadName(NSThread *thread) +{ + NSString *threadName = [thread isMainThread] ? @"main" : thread.name; + if (threadName.length == 0) { +#if DEBUG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + threadName = @(dispatch_queue_get_label(dispatch_get_current_queue())); +#pragma clang diagnostic pop +#else + threadName = [NSString stringWithFormat:@"%p", thread]; +#endif + } + return threadName; +} + NSString *RCTFormatLog( NSDate *timestamp, NSThread *thread, @@ -121,18 +137,7 @@ void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix) [log appendFormat:@"[%s]", RCTLogLevels[level - 1]]; } if (thread) { - NSString *threadName = [thread isMainThread] ? @"main" : thread.name; - if (threadName.length == 0) { -#if DEBUG -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - threadName = @(dispatch_queue_get_label(dispatch_get_current_queue())); -#pragma clang diagnostic pop -#else - threadName = [NSString stringWithFormat:@"%p", thread]; -#endif - } - [log appendFormat:@"[tid:%@]", threadName]; + [log appendFormat:@"[tid:%@]", RCTThreadName(thread)]; } if (fileName) { fileName = [fileName lastPathComponent]; diff --git a/React/Base/RCTProfile.h b/React/Base/RCTProfile.h new file mode 100644 index 00000000000000..b3a1683d1d30cc --- /dev/null +++ b/React/Base/RCTProfile.h @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +/** + * RCTProfile + * + * This file provides a set of functions and macros for performance profiling + * + * NOTE: This API is a work in a work in progress, please consider it before + * before using. + */ + +#if DEBUG + +/** + * Returns YES if the profiling information is currently being collected + */ +BOOL RCTProfileIsProfiling(void); + +/** + * Start collecting profiling information + */ +void RCTProfileInit(void); + +/** + * Stop profiling and return a JSON string of the collected data - The data + * returned is compliant with google's trace event format - the format used + * as input to trace-viewer + */ +NSString *RCTProfileEnd(void); + +/** + * Collects the initial event information for the event and returns a reference ID + */ +NSNumber *_RCTProfileBeginEvent(void); + +/** + * The ID returned by BeginEvent should then be passed into EndEvent, with the + * rest of the event information. Just at this point the event will actually be + * registered + */ +void _RCTProfileEndEvent(NSNumber *, NSString *, NSString *, id); + +/** + * This pair of macros implicitly handle the event ID when beginning and ending + * an event, for both simplicity and performance reasons, this method is preferred + * + * NOTE: The EndEvent call has to be either, in the same scope of BeginEvent, + * or in a sub-scope, otherwise the ID stored by BeginEvent won't be accessible + * for EndEvent, in this case you may want to use the actual C functions. + */ +#define RCTProfileBeginEvent() \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Wshadow\"") \ +NSNumber *__rct_profile_id = _RCTProfileBeginEvent(); \ +_Pragma("clang diagnostic pop") + +#define RCTProfileEndEvent(name, category, args...) \ +_RCTProfileEndEvent(__rct_profile_id, name, category, args) + +/** + * An event that doesn't have a duration (i.e. Notification, VSync, etc) + */ +void RCTProfileImmediateEvent(NSString *, NSTimeInterval , NSString *); + +/** + * Helper to profile the duration of the execution of a block. This method uses + * self and _cmd to name this event for simplicity sake. + * + * NOTE: The block can't expect any argument + */ +#define RCTProfileBlock(block, category, arguments) \ +^{ \ + RCTProfileBeginEvent(); \ + block(); \ + RCTProfileEndEvent([NSString stringWithFormat:@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)], category, arguments); \ +} + +#else + +#define RCTProfileIsProfiling(...) NO +#define RCTProfileInit(...) +#define RCTProfileEnd(...) @"" + +#define _RCTProfileBeginEvent(...) @0 +#define RCTProfileBeginEvent(...) + +#define _RCTProfileEndEvent(...) +#define RCTProfileEndEvent(...) + +#define RCTProfileImmediateEvent(...) + +#define RCTProfileBlock(block, ...) block + +#endif diff --git a/React/Base/RCTProfile.m b/React/Base/RCTProfile.m new file mode 100644 index 00000000000000..4ba99725b176b1 --- /dev/null +++ b/React/Base/RCTProfile.m @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTProfile.h" + +#import + +#import + +#import "RCTLog.h" +#import "RCTUtils.h" + +#pragma mark - Prototypes + +NSNumber *RCTProfileTimestamp(NSTimeInterval); +NSString *RCTProfileMemory(vm_size_t); +NSDictionary *RCTProfileGetMemoryUsage(void); + +#pragma mark - Constants + +NSString const *RCTProfileTraceEvents = @"traceEvents"; +NSString const *RCTProfileSamples = @"samples"; + +#pragma mark - Variables + +NSDictionary *RCTProfileInfo; +NSUInteger RCTProfileEventID = 0; +NSMutableDictionary *RCTProfileOngoingEvents; +NSTimeInterval RCTProfileStartTime; + +#pragma mark - Macros + +#define RCTProfileAddEvent(type, props...) \ +[RCTProfileInfo[type] addObject:@{ \ + @"pid": @([[NSProcessInfo processInfo] processIdentifier]), \ + @"tid": RCTThreadName([NSThread currentThread]), \ + props \ +}]; + +#define CHECK(...) \ +if (!RCTProfileIsProfiling()) { \ + return __VA_ARGS__; \ +} + +#pragma mark - Private Helpers + +NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp) +{ + return @((timestamp - RCTProfileStartTime) * 1e6); +} + +NSString *RCTProfileMemory(vm_size_t memory) +{ + double mem = ((double)memory) / 1024 / 1024; + return [NSString stringWithFormat:@"%.2lfmb", mem]; +} + +NSDictionary *RCTProfileGetMemoryUsage(void) +{ + CHECK(@{}); + struct task_basic_info info; + mach_msg_type_number_t size = sizeof(info); + kern_return_t kerr = task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&info, + &size); + if( kerr == KERN_SUCCESS ) { + return @{ + @"suspend_count": @(info.suspend_count), + @"virtual_size": RCTProfileMemory(info.virtual_size), + @"resident_size": RCTProfileMemory(info.resident_size), + }; + } else { + return @{}; + } +} + +#pragma mark - Public Functions + +BOOL RCTProfileIsProfiling(void) +{ + return RCTProfileInfo != nil; +} + +void RCTProfileInit(void) +{ + RCTProfileStartTime = CACurrentMediaTime(); + RCTProfileOngoingEvents = [[NSMutableDictionary alloc] init]; + RCTProfileInfo = @{ + RCTProfileTraceEvents: [[NSMutableArray alloc] init], + RCTProfileSamples: [[NSMutableArray alloc] init], + }; +} + +NSString *RCTProfileEnd(void) +{ + NSString *log = RCTJSONStringify(RCTProfileInfo, NULL); + RCTProfileEventID = 0; + RCTProfileInfo = nil; + RCTProfileOngoingEvents = nil; + return log; +} + +NSNumber *_RCTProfileBeginEvent(void) +{ + CHECK(@0); + NSNumber *eventID = @(++RCTProfileEventID); + RCTProfileOngoingEvents[eventID] = RCTProfileTimestamp(CACurrentMediaTime()); + return eventID; +} + +void _RCTProfileEndEvent(NSNumber *eventID, NSString *name, NSString *categories, id args) +{ + CHECK(); + NSNumber *startTimestamp = RCTProfileOngoingEvents[eventID]; + if (!startTimestamp) { + return; + } + + NSNumber *endTimestamp = RCTProfileTimestamp(CACurrentMediaTime()); + + RCTProfileAddEvent(RCTProfileTraceEvents, + @"name": name, + @"cat": categories, + @"ph": @"X", + @"ts": startTimestamp, + @"dur": @(endTimestamp.doubleValue - startTimestamp.doubleValue), + @"args": args ?: @[], + ); + [RCTProfileOngoingEvents removeObjectForKey:eventID]; +} + +void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString *scope) +{ + CHECK(); + RCTProfileAddEvent(RCTProfileTraceEvents, + @"name": name, + @"ts": RCTProfileTimestamp(timestamp), + @"scope": scope, + @"ph": @"i", + @"args": RCTProfileGetMemoryUsage(), + ); +} diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 71e4f45c30f3cc..3de6182d144c30 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -15,6 +15,7 @@ #import "RCTAssert.h" #import "RCTLog.h" +#import "RCTProfile.h" #import "RCTUtils.h" @interface RCTJavaScriptContext : NSObject @@ -234,7 +235,7 @@ - (void)executeJSCall:(NSString *)name { RCTAssert(onComplete != nil, @"onComplete block should not be nil"); __weak RCTContextExecutor *weakSelf = self; - [self executeBlockOnJavaScriptQueue:^{ + [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ RCTContextExecutor *strongSelf = weakSelf; if (!strongSelf || !strongSelf.isValid || ![RCTGetExecutorID(strongSelf) isEqualToNumber:executorID]) { return; @@ -275,7 +276,7 @@ - (void)executeJSCall:(NSString *)name } onComplete(objcValue, nil); - }]; + }), @"js_call", (@{@"module":name, @"method": method, @"args": arguments}))]; } - (void)executeApplicationScript:(NSString *)script @@ -285,7 +286,7 @@ - (void)executeApplicationScript:(NSString *)script RCTAssert(sourceURL != nil, @"url should not be nil"); __weak RCTContextExecutor *weakSelf = self; - [self executeBlockOnJavaScriptQueue:^{ + [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ RCTContextExecutor *strongSelf = weakSelf; if (!strongSelf || !strongSelf.isValid) { return; @@ -304,7 +305,7 @@ - (void)executeApplicationScript:(NSString *)script } onComplete(error); } - }]; + }), @"js_call", (@{ @"url": sourceURL }))]; } - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block @@ -327,7 +328,7 @@ - (void)injectJSONText:(NSString *)script #endif __weak RCTContextExecutor *weakSelf = self; - [self executeBlockOnJavaScriptQueue:^{ + [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ RCTContextExecutor *strongSelf = weakSelf; if (!strongSelf || !strongSelf.isValid) { return; @@ -354,7 +355,7 @@ - (void)injectJSONText:(NSString *)script if (onComplete) { onComplete(nil); } - }]; + }), @"js_call,json_call", (@{@"objectName": objectName}))]; } @end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 2830ce6b029582..e2dc8d560d089b 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -19,6 +19,7 @@ #import "RCTBridge.h" #import "RCTConvert.h" #import "RCTLog.h" +#import "RCTProfile.h" #import "RCTRootView.h" #import "RCTScrollableProtocol.h" #import "RCTShadowView.h" @@ -888,9 +889,13 @@ - (void)flushUIBlocks // Execute the previously queued UI blocks dispatch_async(dispatch_get_main_queue(), ^{ + RCTProfileBeginEvent(); for (dispatch_block_t block in previousPendingUIBlocks) { block(); } + RCTProfileEndEvent(@"UIManager flushUIBlocks", @"objc_call", @{ + @"count": @(previousPendingUIBlocks.count), + }); }); } diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 294bf414595641..48d7aee0459b40 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ 14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F362081AABD06A001CE568 /* RCTSwitch.m */; }; 14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */; }; 14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; }; + 14F4D38B1AE1B7E40049C042 /* RCTProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F4D38A1AE1B7E40049C042 /* RCTProfile.m */; }; 58114A161AAE854800E7D092 /* RCTPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A131AAE854800E7D092 /* RCTPicker.m */; }; 58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A151AAE854800E7D092 /* RCTPickerManager.m */; }; 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */; }; @@ -165,6 +166,8 @@ 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitchManager.m; sourceTree = ""; }; 14F484541AABFCE100FDF6B9 /* RCTSliderManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSliderManager.h; sourceTree = ""; }; 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSliderManager.m; sourceTree = ""; }; + 14F4D3891AE1B7E40049C042 /* RCTProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTProfile.h; sourceTree = ""; }; + 14F4D38A1AE1B7E40049C042 /* RCTProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTProfile.m; sourceTree = ""; }; 58114A121AAE854800E7D092 /* RCTPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPicker.h; sourceTree = ""; }; 58114A131AAE854800E7D092 /* RCTPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPicker.m; sourceTree = ""; }; 58114A141AAE854800E7D092 /* RCTPickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPickerManager.h; sourceTree = ""; }; @@ -393,6 +396,8 @@ 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */, 83CBBA501A601E3B00E9B192 /* RCTUtils.m */, 1436DD071ADE7AA000A5ED7D /* RCTFrameUpdate.h */, + 14F4D3891AE1B7E40049C042 /* RCTProfile.h */, + 14F4D38A1AE1B7E40049C042 /* RCTProfile.m */, ); path = Base; sourceTree = ""; @@ -483,6 +488,7 @@ 83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */, 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */, 832348161A77A5AA00B55238 /* Layout.c in Sources */, + 14F4D38B1AE1B7E40049C042 /* RCTProfile.m in Sources */, 14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */, 14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */, 13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */, From 88b6df99000b001717541fcf59390829a291de07 Mon Sep 17 00:00:00 2001 From: Marek Cirkos Date: Mon, 20 Apr 2015 05:09:54 -0700 Subject: [PATCH 021/250] Fixed way that ScrollView handles removeClippedSubviews flag --- React/Views/RCTScrollView.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m index 0376d2a9c0e960..1b31700649b31d 100644 --- a/React/Views/RCTScrollView.m +++ b/React/Views/RCTScrollView.m @@ -175,7 +175,7 @@ - (void)dockClosestSectionHeader scrollBounds.origin.y += self.contentInset.top; NSInteger i = 0; - for (UIView *subview in contentView.subviews) { + for (UIView *subview in contentView.reactSubviews) { CGRect rowFrame = [RCTCustomScrollView _calculateUntransformedFrame:subview]; if (CGRectIntersectsRect(scrollBounds, rowFrame)) { firstIndexInView = i; @@ -198,8 +198,8 @@ - (void)dockClosestSectionHeader NSInteger nextDockedIndex = (stickyHeaderii < _stickyHeaderIndices.count - 1) ? [_stickyHeaderIndices[stickyHeaderii + 1] integerValue] : -1; - UIView *currentHeader = contentView.subviews[currentlyDockedIndex]; - UIView *previousHeader = previouslyDockedIndex >= 0 ? contentView.subviews[previouslyDockedIndex] : nil; + UIView *currentHeader = contentView.reactSubviews[currentlyDockedIndex]; + UIView *previousHeader = previouslyDockedIndex >= 0 ? contentView.reactSubviews[previouslyDockedIndex] : nil; CGRect curFrame = [RCTCustomScrollView _calculateUntransformedFrame:currentHeader]; if (previousHeader) { @@ -210,7 +210,7 @@ - (void)dockClosestSectionHeader previousHeader.transform = CGAffineTransformMakeTranslation(0, yOffset); } - UIView *nextHeader = nextDockedIndex >= 0 ? contentView.subviews[nextDockedIndex] : nil; + UIView *nextHeader = nextDockedIndex >= 0 ? contentView.reactSubviews[nextDockedIndex] : nil; CGRect nextFrame = [RCTCustomScrollView _calculateUntransformedFrame:nextHeader]; if (curFrame.origin.y < scrollBounds.origin.y) { From 915925db9d823aabda2c833f8c35ae83e07642a9 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 20 Apr 2015 05:40:42 -0700 Subject: [PATCH 022/250] [ReactNative] Add tests on root view, bridge, modules and js context deallocation --- React/Executors/RCTContextExecutor.m | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 3de6182d144c30..d2aac3fbe6df7d 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -47,9 +47,11 @@ - (BOOL)isValid - (void)invalidate { - JSGlobalContextRelease(_ctx); - _ctx = NULL; - _self = nil; + if (self.isValid) { + JSGlobalContextRelease(_ctx); + _ctx = NULL; + _self = nil; + } } @end @@ -216,10 +218,7 @@ - (BOOL)isValid - (void)invalidate { - if (self.isValid) { - [_context performSelector:@selector(invalidate) onThread:_javaScriptThread withObject:nil waitUntilDone:NO]; - _context = nil; - } + [_context performSelector:@selector(invalidate) onThread:_javaScriptThread withObject:nil waitUntilDone:NO]; } - (void)dealloc From 2d5d55d17e2068dab2281570f8c9fa1e65c95a8b Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 20 Apr 2015 12:33:52 -0100 Subject: [PATCH 023/250] [ReactNative] Add if DEBUG to profile functions declarations --- React/Base/RCTProfile.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/React/Base/RCTProfile.m b/React/Base/RCTProfile.m index 4ba99725b176b1..71d34551e9c37a 100644 --- a/React/Base/RCTProfile.m +++ b/React/Base/RCTProfile.m @@ -16,6 +16,8 @@ #import "RCTLog.h" #import "RCTUtils.h" +#if DEBUG + #pragma mark - Prototypes NSNumber *RCTProfileTimestamp(NSTimeInterval); @@ -147,3 +149,5 @@ void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString @"args": RCTProfileGetMemoryUsage(), ); } + +#endif From bbd52595864a3ac6469b46b3789b882b51f33e86 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Mon, 20 Apr 2015 08:31:15 -0700 Subject: [PATCH 024/250] Fixed reachability --- Examples/2048/2048/AppDelegate.m | 41 ++++++++++++-------- Examples/Movies/Movies/AppDelegate.m | 39 ++++++++++++------- Examples/SampleApp/iOS/AppDelegate.m | 41 ++++++++++++-------- Examples/TicTacToe/TicTacToe/AppDelegate.m | 41 ++++++++++++-------- Examples/UIExplorer/UIExplorer/AppDelegate.m | 41 ++++++++++++-------- Libraries/Network/RCTReachability.m | 6 +-- 6 files changed, 127 insertions(+), 82 deletions(-) diff --git a/Examples/2048/2048/AppDelegate.m b/Examples/2048/2048/AppDelegate.m index bc089038c21d34..004e854a7558ae 100644 --- a/Examples/2048/2048/AppDelegate.m +++ b/Examples/2048/2048/AppDelegate.m @@ -22,24 +22,33 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( { NSURL *jsCodeLocation; - // Loading JavaScript code - uncomment the one you want. - - // OPTION 1 - // Load from development server. Start the server from the repository root: - // - // $ npm start - // - // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and - // iOS device are on the same Wi-Fi network. + /** + * Loading JavaScript code - uncomment the one you want. + * + * OPTION 1 + * Load from development server. Start the server from the repository root: + * + * $ npm start + * + * To run on device, change `localhost` to the IP address of your computer + * (you can get this by typing `ifconfig` into the terminal and selecting the + * `inet` value under `en0:`) and make sure your computer and iOS device are + * on the same Wi-Fi network. + */ + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/2048/Game2048.includeRequire.runModule.bundle"]; - // OPTION 2 - // Load from pre-bundled file on disk. To re-generate the static bundle, run - // - // $ curl http://localhost:8081/Examples/2048/Game2048.includeRequire.runModule.bundle -o main.jsbundle - // - // and uncomment the next following line - // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + /** + * OPTION 2 + * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` + * to your Xcode project folder in the terminal, and run + * + * $ curl 'http://localhost:8081/Examples/2048/Game2048.includeRequire.runModule.bundle' -o main.jsbundle + * + * then add the `main.jsbundle` file to your project and uncomment this line: + */ + +// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"Game2048" diff --git a/Examples/Movies/Movies/AppDelegate.m b/Examples/Movies/Movies/AppDelegate.m index b5672271252c87..74aed2cc4724d9 100644 --- a/Examples/Movies/Movies/AppDelegate.m +++ b/Examples/Movies/Movies/AppDelegate.m @@ -23,24 +23,33 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( { NSURL *jsCodeLocation; - // Loading JavaScript code - uncomment the one you want. + /** + * Loading JavaScript code - uncomment the one you want. + * + * OPTION 1 + * Load from development server. Start the server from the repository root: + * + * $ npm start + * + * To run on device, change `localhost` to the IP address of your computer + * (you can get this by typing `ifconfig` into the terminal and selecting the + * `inet` value under `en0:`) and make sure your computer and iOS device are + * on the same Wi-Fi network. + */ - // OPTION 1 - // Load from development server. Start the server from the repository root: - // - // $ npm start - // - // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and - // iOS device are on the same Wi-Fi network. jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle"]; - // OPTION 2 - // Load from pre-bundled file on disk. To re-generate the static bundle, run - // - // $ curl http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle -o main.jsbundle - // - // and uncomment the next following line - // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + /** + * OPTION 2 + * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` + * to your Xcode project folder in the terminal, and run + * + * $ curl 'http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle' -o main.jsbundle + * + * then add the `main.jsbundle` file to your project and uncomment this line: + */ + +// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"MoviesApp" diff --git a/Examples/SampleApp/iOS/AppDelegate.m b/Examples/SampleApp/iOS/AppDelegate.m index 5b6cc2d58e30b6..777072c6c56964 100644 --- a/Examples/SampleApp/iOS/AppDelegate.m +++ b/Examples/SampleApp/iOS/AppDelegate.m @@ -17,24 +17,33 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( { NSURL *jsCodeLocation; - // Loading JavaScript code - uncomment the one you want. - - // OPTION 1 - // Load from development server. Start the server from the repository root: - // - // $ npm start - // - // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and - // iOS device are on the same Wi-Fi network. + /** + * Loading JavaScript code - uncomment the one you want. + * + * OPTION 1 + * Load from development server. Start the server from the repository root: + * + * $ npm start + * + * To run on device, change `localhost` to the IP address of your computer + * (you can get this by typing `ifconfig` into the terminal and selecting the + * `inet` value under `en0:`) and make sure your computer and iOS device are + * on the same Wi-Fi network. + */ + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/SampleApp/index.ios.bundle"]; - // OPTION 2 - // Load from pre-bundled file on disk. To re-generate the static bundle, run - // - // $ curl 'http://localhost:8081/Examples/SampleApp/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle - // - // and uncomment the next following line - // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + /** + * OPTION 2 + * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` + * to your Xcode project folder in the terminal, and run + * + * $ curl 'http://localhost:8081/Examples/SampleApp/index.ios.bundle?dev=false&minify=true' -o main.jsbundle + * + * then add the `main.jsbundle` file to your project and uncomment this line: + */ + +// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"SampleApp" diff --git a/Examples/TicTacToe/TicTacToe/AppDelegate.m b/Examples/TicTacToe/TicTacToe/AppDelegate.m index a118b94dc47051..9c328a3a83a57e 100644 --- a/Examples/TicTacToe/TicTacToe/AppDelegate.m +++ b/Examples/TicTacToe/TicTacToe/AppDelegate.m @@ -22,24 +22,33 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( { NSURL *jsCodeLocation; - // Loading JavaScript code - uncomment the one you want. - - // OPTION 1 - // Load from development server. Start the server from the repository root: - // - // $ npm start - // - // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and - // iOS device are on the same Wi-Fi network. + /** + * Loading JavaScript code - uncomment the one you want. + * + * OPTION 1 + * Load from development server. Start the server from the repository root: + * + * $ npm start + * + * To run on device, change `localhost` to the IP address of your computer + * (you can get this by typing `ifconfig` into the terminal and selecting the + * `inet` value under `en0:`) and make sure your computer and iOS device are + * on the same Wi-Fi network. + */ + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.includeRequire.runModule.bundle"]; - // OPTION 2 - // Load from pre-bundled file on disk. To re-generate the static bundle, run - // - // $ curl http://localhost:8081/Examples/TicTacToe/TicTacToeApp.includeRequire.runModule.bundle -o main.jsbundle - // - // and uncomment the next following line - // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + /** + * OPTION 2 + * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` + * to your Xcode project folder in the terminal, and run + * + * $ curl 'http://localhost:8081/Examples/TicTacToe/TicTacToeApp.includeRequire.runModule.bundle' -o main.jsbundle + * + * then add the `main.jsbundle` file to your project and uncomment this line: + */ + +// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"TicTacToeApp" diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.m b/Examples/UIExplorer/UIExplorer/AppDelegate.m index 84734f12f9d642..d72262e78f0cd5 100644 --- a/Examples/UIExplorer/UIExplorer/AppDelegate.m +++ b/Examples/UIExplorer/UIExplorer/AppDelegate.m @@ -22,24 +22,33 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( { NSURL *jsCodeLocation; - // Loading JavaScript code - uncomment the one you want. - - // OPTION 1 - // Load from development server. Start the server from the repository root: - // - // $ npm start - // - // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and - // iOS device are on the same Wi-Fi network. + /** + * Loading JavaScript code - uncomment the one you want. + * + * OPTION 1 + * Load from development server. Start the server from the repository root: + * + * $ npm start + * + * To run on device, change `localhost` to the IP address of your computer + * (you can get this by typing `ifconfig` into the terminal and selecting the + * `inet` value under `en0:`) and make sure your computer and iOS device are + * on the same Wi-Fi network. + */ + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.includeRequire.runModule.bundle?dev=true"]; - // OPTION 2 - // Load from pre-bundled file on disk. To re-generate the static bundle, run - // - // $ curl http://localhost:8081/Examples/UIExplorer/UIExplorerApp.includeRequire.runModule.bundle -o main.jsbundle - // - // and uncomment the next following line - // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + /** + * OPTION 2 + * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` + * to your Xcode project folder and run + * + * $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.includeRequire.runModule.bundle' -o main.jsbundle + * + * then add the `main.jsbundle` file to your project and uncomment this line: + */ + +// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"UIExplorerApp" diff --git a/Libraries/Network/RCTReachability.m b/Libraries/Network/RCTReachability.m index b5f30de30e7865..e0711571b9a3c3 100644 --- a/Libraries/Network/RCTReachability.m +++ b/Libraries/Network/RCTReachability.m @@ -25,6 +25,8 @@ @implementation RCTReachability @synthesize bridge = _bridge; +RCT_EXPORT_MODULE() + static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) { RCTReachability *self = (__bridge id)info; @@ -53,8 +55,6 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC } } -RCT_EXPORT_MODULE() - #pragma mark - Lifecycle - (instancetype)initWithHost:(NSString *)host @@ -71,7 +71,7 @@ - (instancetype)initWithHost:(NSString *)host - (instancetype)init { - return [self initWithHost:@"http://apple.com"]; + return [self initWithHost:@"apple.com"]; } - (void)dealloc From d6afe1b1249741512d78fbef04949c38e10c7f5d Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 20 Apr 2015 11:03:56 -0700 Subject: [PATCH 025/250] [ReactNative] Don't break when can't create executor --- React/Base/RCTJavaScriptExecutor.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/React/Base/RCTJavaScriptExecutor.h b/React/Base/RCTJavaScriptExecutor.h index 2816c7a7a81f3d..8ff5a16585d8bc 100644 --- a/React/Base/RCTJavaScriptExecutor.h +++ b/React/Base/RCTJavaScriptExecutor.h @@ -49,11 +49,13 @@ __used static id RCTCreateExecutor(Class executorClass) { static NSUInteger executorID = 0; id executor = [[executorClass alloc] init]; - objc_setAssociatedObject(executor, RCTJavaScriptExecutorID, @(++executorID), OBJC_ASSOCIATION_RETAIN); + if (executor) { + objc_setAssociatedObject(executor, RCTJavaScriptExecutorID, @(++executorID), OBJC_ASSOCIATION_RETAIN); + } return executor; } __used static NSNumber *RCTGetExecutorID(id executor) { - return objc_getAssociatedObject(executor, RCTJavaScriptExecutorID); + return executor ? objc_getAssociatedObject(executor, RCTJavaScriptExecutorID) : @0; } From 5e2f90a73e710e6ad94cf33218113fa090d359e5 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Mon, 20 Apr 2015 12:41:29 -0700 Subject: [PATCH 026/250] [ReactNative] Skip flow checks for URLs that are not bundles --- packager/getFlowTypeCheckMiddleware.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packager/getFlowTypeCheckMiddleware.js b/packager/getFlowTypeCheckMiddleware.js index cd910054f4d817..059f0e319b6b75 100644 --- a/packager/getFlowTypeCheckMiddleware.js +++ b/packager/getFlowTypeCheckMiddleware.js @@ -16,7 +16,8 @@ var hasWarned = {}; function getFlowTypeCheckMiddleware(options) { return function(req, res, next) { - if (options.skipflow) { + var isBundle = req.url.indexOf('.bundle') !== -1; + if (options.skipflow || !isBundle) { return next(); } if (options.flowroot || options.projectRoots.length === 1) { From 5ce9fa4dda79bd925e075afcb552893acdc83628 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Mon, 20 Apr 2015 12:06:02 -0700 Subject: [PATCH 027/250] Changed default method queue to a background queue. --- .../ActionSheetIOS/RCTActionSheetManager.m | 5 +++ Libraries/Geolocation/RCTLocationObserver.m | 5 +++ Libraries/LinkingIOS/RCTLinkingManager.m | 13 ++++--- React/Base/RCTBridge.h | 10 +++--- React/Base/RCTBridge.m | 36 ++++++++++--------- React/Base/RCTBridgeModule.h | 26 ++++++++++---- React/Modules/RCTAlertManager.m | 5 +++ React/Modules/RCTStatusBarManager.m | 5 +++ React/Modules/RCTTiming.m | 5 +++ 9 files changed, 78 insertions(+), 32 deletions(-) diff --git a/Libraries/ActionSheetIOS/RCTActionSheetManager.m b/Libraries/ActionSheetIOS/RCTActionSheetManager.m index ac672249f9f3b7..75798efaf37ada 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheetManager.m +++ b/Libraries/ActionSheetIOS/RCTActionSheetManager.m @@ -30,6 +30,11 @@ - (instancetype)init return self; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options failureCallback:(RCTResponseSenderBlock)failureCallback successCallback:(RCTResponseSenderBlock)successCallback) diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index c5303df1511ac3..3e864657b82c5d 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -128,6 +128,11 @@ - (void)dealloc _locationManager.delegate = nil; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + #pragma mark - Private API - (void)beginLocationUpdates diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m index 4be8bfb8e45d5b..eec17a012f9eeb 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.m +++ b/Libraries/LinkingIOS/RCTLinkingManager.m @@ -36,6 +36,11 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_queue_create("com.facebook.React.LinkingManager", DISPATCH_QUEUE_SERIAL); +} + + (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL sourceApplication:(NSString *)sourceApplication @@ -56,16 +61,16 @@ - (void)handleOpenURLNotification:(NSNotification *)notification RCT_EXPORT_METHOD(openURL:(NSURL *)URL) { + // Doesn't really matter what thread we call this on since it exits the app [[UIApplication sharedApplication] openURL:URL]; } RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL callback:(RCTResponseSenderBlock)callback) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL]; - callback(@[@(canOpen)]); - }); + // This can be expensive, so we deliberately don't call on main thread + BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL]; + callback(@[@(canOpen)]); } - (NSDictionary *)constantsToExport diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 544e5e1a2bbbca..c0f3a1a916b0dc 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -62,9 +62,9 @@ extern NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); /** * This method is used to call functions in the JavaScript application context. * It is primarily intended for use by modules that require two-way communication - * with the JavaScript code. Method should be regsitered using the + * with the JavaScript code. Method should be registered using the * RCT_IMPORT_METHOD macro below. Attempting to call a method that has not been - * registered will result in an error. + * registered will result in an error. Safe to call from any thread. */ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args; @@ -112,17 +112,17 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method; @property (nonatomic, readonly, getter=isLoading) BOOL loading; /** - * Reload the bundle and reset executor and modules. + * Reload the bundle and reset executor & modules. Safe to call from any thread. */ - (void)reload; /** - * Add a new observer that will be called on every screen refresh + * Add a new observer that will be called on every screen refresh. */ - (void)addFrameUpdateObserver:(id)observer; /** - * Stop receiving screen refresh updates for the given observer + * Stop receiving screen refresh updates for the given observer. */ - (void)removeFrameUpdateObserver:(id)observer; diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 46dcc1dfe321c1..31a8bea7736921 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -228,6 +228,7 @@ @implementation RCTModuleMethod NSMethodSignature *_methodSignature; NSArray *_argumentBlocks; NSString *_methodName; + dispatch_block_t _methodQueue; } static Class _globalExecutorClass; @@ -762,6 +763,7 @@ @implementation RCTBridge { RCTSparseArray *_modulesByID; RCTSparseArray *_queuesByID; + dispatch_queue_t _methodQueue; NSDictionary *_modulesByName; id _javaScriptExecutor; Class _executorClass; @@ -787,7 +789,6 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL [self setUp]; [self bindKeys]; } - return self; } @@ -797,6 +798,7 @@ - (void)setUp _javaScriptExecutor = RCTCreateExecutor(executorClass); _latestJSExecutor = _javaScriptExecutor; _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; + _methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL); _displayLink = [[RCTDisplayLink alloc] initWithBridge:self]; _frameUpdateObservers = [[NSMutableSet alloc] init]; _scheduledCalls = [[NSMutableArray alloc] init]; @@ -850,11 +852,14 @@ - (void)setUp } } - // Get method queue + // Get method queues _queuesByID = [[RCTSparseArray alloc] init]; [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { if ([module respondsToSelector:@selector(methodQueue)]) { - _queuesByID[moduleID] = [module methodQueue] ?: dispatch_get_main_queue(); + dispatch_queue_t queue = [module methodQueue]; + if (queue) { + _queuesByID[moduleID] = queue; + } } }]; @@ -895,11 +900,6 @@ - (void)setUp } else { [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification object:self]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(reload) - name:RCTReloadNotification - object:nil]; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reload) @@ -1205,7 +1205,7 @@ - (void)_handleBuffer:(id)buffer context:(NSNumber *)context [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { if ([module respondsToSelector:@selector(batchDidComplete)]) { dispatch_queue_t queue = _queuesByID[moduleID]; - dispatch_async(queue ?: dispatch_get_main_queue(), ^{ + dispatch_async(queue ?: _methodQueue, ^{ [module batchDidComplete]; }); } @@ -1240,7 +1240,7 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i __weak RCTBridge *weakSelf = self; dispatch_queue_t queue = _queuesByID[moduleID]; - dispatch_async(queue ?: dispatch_get_main_queue(), ^{ + dispatch_async(queue ?: _methodQueue, ^{ RCTProfileBeginEvent(); __strong RCTBridge *strongSelf = weakSelf; @@ -1320,13 +1320,15 @@ - (void)removeFrameUpdateObserver:(id)observer - (void)reload { - if (!_loading) { - // If the bridge has not loaded yet, the context will be already invalid at - // the time the javascript gets executed. - // It will crash the javascript, and even the next `load` won't render. - [self invalidate]; - [self setUp]; - } + dispatch_async(dispatch_get_main_queue(), ^{ + if (!_loading) { + // If the bridge has not loaded yet, the context will be already invalid at + // the time the javascript gets executed. + // It will crash the javascript, and even the next `load` won't render. + [self invalidate]; + [self setUp]; + } + }); } + (void)logMessage:(NSString *)message level:(NSString *)level diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index c8fa41c4438d53..12f7803ea73108 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -91,18 +91,32 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response); /** * The queue that will be used to call all exported methods. If omitted, this - * will default the main queue, which is recommended for any methods that - * interact with UIKit. If your methods perform heavy work such as filesystem - * or network access, you should return a custom serial queue. Example: + * will call on the default background queue, which is avoids blocking the main + * thread. + * + * If the methods in your module need to interact with UIKit methods, they will + * probably need to call those on the main thread, as most of UIKit is main- + * thread-only. You can tell React Native to call your module methods on the + * main thread by returning a reference to the main queue, like this: + * + * - (dispatch_queue_t)methodQueue + * { + * return dispatch_get_main_queue(); + * } + * + * If your methods perform heavy work such as synchronous filesystem or network + * access, you probably don't want to block the default background queue, as + * this will stall other methods. Instead, you should return a custom serial + * queue, like this: * * - (dispatch_queue_t)methodQueue * { * return dispatch_queue_create("com.mydomain.FileQueue", DISPATCH_QUEUE_SERIAL); * } * - * Alternatively, if only some methods on the module should be executed on a - * background queue you can leave this method unimplemented, and simply - * dispatch_async() within the method itself. + * Alternatively, if only some methods of the module should be executed on a + * particular queue you can leave this method unimplemented, and simply + * dispatch_async() to the required queue within the method itself. */ - (dispatch_queue_t)methodQueue; diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m index b97364e3809aad..2690de1dfea65e 100644 --- a/React/Modules/RCTAlertManager.m +++ b/React/Modules/RCTAlertManager.m @@ -35,6 +35,11 @@ - (instancetype)init return self; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + /** * @param {NSDictionary} args Dictionary of the form * diff --git a/React/Modules/RCTStatusBarManager.m b/React/Modules/RCTStatusBarManager.m index 149ad568e133ec..04bb390387c0fb 100644 --- a/React/Modules/RCTStatusBarManager.m +++ b/React/Modules/RCTStatusBarManager.m @@ -26,6 +26,11 @@ static BOOL RCTViewControllerBasedStatusBarAppearance() RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + RCT_EXPORT_METHOD(setStyle:(UIStatusBarStyle)statusBarStyle animated:(BOOL)animated) { diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index 1f6e84d6afe98f..62d42a7bbf532d 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -108,6 +108,11 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + - (BOOL)isValid { return _bridge != nil; From 0e8bc08d3fcaa20543a4c77ec90615ae18a7a6ab Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 20 Apr 2015 12:03:58 -0700 Subject: [PATCH 028/250] [ReactNative] Update method name on chrome debugger --- packager/debugger.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/debugger.html b/packager/debugger.html index d0d4aba54bfaf9..6e002a2255a7cf 100644 --- a/packager/debugger.html +++ b/packager/debugger.html @@ -47,7 +47,7 @@ } loadScript(message.url, sendReply.bind(null, null)); }, - 'executeJSCall:method:arguments:callback:': function(message, sendReply) { + 'executeJSCall:method:arguments:context:callback:': function(message, sendReply) { var returnValue = [[], [], [], [], []]; try { if (window && window.require) { From 2434512847516e149b80b6a32bef7f4f7794782c Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Mon, 20 Apr 2015 14:04:53 -0700 Subject: [PATCH 029/250] [ReactNative] Allow JS know its URL --- React/Base/RCTBridge.h | 5 +++++ React/Modules/RCTSourceCode.m | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index c0f3a1a916b0dc..0a82b05e2c440e 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -88,6 +88,11 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method; url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete; +/** + * URL of the script that was loaded into the bridge. + */ +@property (nonatomic, copy, readonly) NSURL *bundleURL; + @property (nonatomic, strong) Class executorClass; /** diff --git a/React/Modules/RCTSourceCode.m b/React/Modules/RCTSourceCode.m index e29a05637de173..1b6eb842ece49d 100644 --- a/React/Modules/RCTSourceCode.m +++ b/React/Modules/RCTSourceCode.m @@ -10,12 +10,15 @@ #import "RCTSourceCode.h" #import "RCTAssert.h" +#import "RCTBridge.h" #import "RCTUtils.h" @implementation RCTSourceCode RCT_EXPORT_MODULE() +@synthesize bridge = _bridge; + RCT_EXPORT_METHOD(getScriptText:(RCTResponseSenderBlock)successCallback failureCallback:(RCTResponseSenderBlock)failureCallback) { @@ -26,4 +29,10 @@ @implementation RCTSourceCode } } +- (NSDictionary *)constantsToExport +{ + NSString *URL = [self.bridge.bundleURL absoluteString] ?: @""; + return @{@"scriptURL": URL}; +} + @end From a8a179844924d689aef16e9ed9742c236627689b Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Mon, 20 Apr 2015 14:35:45 -0700 Subject: [PATCH 030/250] [ReactNative] Fix Chrome debugger --- Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m | 4 ++-- packager/debugger.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m index 7fd817d53cfda1..4bdfab1bd53e04 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m @@ -136,7 +136,7 @@ - (void)sendMessage:(NSDictionary *)message context:(NSNumber *)executorID waitF - (void)executeApplicationScript:(NSString *)script sourceURL:(NSURL *)URL onComplete:(RCTJavaScriptCompleteBlock)onComplete { - NSDictionary *message = @{@"method": NSStringFromSelector(_cmd), @"url": [URL absoluteString], @"inject": _injectedObjects}; + NSDictionary *message = @{@"method": @"executeApplicationScript", @"url": [URL absoluteString], @"inject": _injectedObjects}; [self sendMessage:message context:nil waitForReply:^(NSError *error, NSDictionary *reply) { onComplete(error); }]; @@ -146,7 +146,7 @@ - (void)executeJSCall:(NSString *)name method:(NSString *)method arguments:(NSAr { RCTAssert(onComplete != nil, @"callback was missing for exec JS call"); NSDictionary *message = @{ - @"method": NSStringFromSelector(_cmd), + @"method": @"executeJSCall", @"moduleName": name, @"moduleMethod": method, @"arguments": arguments diff --git a/packager/debugger.html b/packager/debugger.html index 6e002a2255a7cf..d72e40ead23ba4 100644 --- a/packager/debugger.html +++ b/packager/debugger.html @@ -41,13 +41,13 @@ window.localStorage.setItem('sessionID', message.id); window.location.reload(); }, - 'executeApplicationScript:sourceURL:onComplete:': function(message, sendReply) { + 'executeApplicationScript': function(message, sendReply) { for (var key in message.inject) { window[key] = JSON.parse(message.inject[key]); } loadScript(message.url, sendReply.bind(null, null)); }, - 'executeJSCall:method:arguments:context:callback:': function(message, sendReply) { + 'executeJSCall': function(message, sendReply) { var returnValue = [[], [], [], [], []]; try { if (window && window.require) { From 82704adeadd15fcbe595f502541929641e87e100 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Mon, 20 Apr 2015 15:54:24 -0700 Subject: [PATCH 031/250] [react-packager] Implement Packager::getAssets --- .../react-packager/src/Packager/Package.js | 11 +++++++ .../src/Packager/__tests__/Package-test.js | 12 +++++++ .../src/Packager/__tests__/Packager-test.js | 8 +++++ packager/react-packager/src/Packager/index.js | 33 ++++++++++--------- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/packager/react-packager/src/Packager/Package.js b/packager/react-packager/src/Packager/Package.js index 0f55c8edcc9ebf..67e31e47efef45 100644 --- a/packager/react-packager/src/Packager/Package.js +++ b/packager/react-packager/src/Packager/Package.js @@ -17,6 +17,7 @@ module.exports = Package; function Package(sourceMapUrl) { this._finalized = false; this._modules = []; + this._assets = []; this._sourceMapUrl = sourceMapUrl; } @@ -36,6 +37,10 @@ Package.prototype.addModule = function( }); }; +Package.prototype.addAsset = function(asset) { + this._assets.push(asset); +}; + Package.prototype.finalize = function(options) { options = options || {}; if (options.runMainModule) { @@ -49,6 +54,8 @@ Package.prototype.finalize = function(options) { Object.freeze(this._modules); Object.seal(this._modules); + Object.freeze(this._assets); + Object.seal(this._assets); this._finalized = true; }; @@ -146,6 +153,10 @@ Package.prototype.getSourceMap = function(options) { return map; }; +Package.prototype.getAssets = function() { + return this._assets; +}; + Package.prototype._getMappings = function() { var modules = this._modules; diff --git a/packager/react-packager/src/Packager/__tests__/Package-test.js b/packager/react-packager/src/Packager/__tests__/Package-test.js index 5a7438d27f4293..db596a7bc4beb4 100644 --- a/packager/react-packager/src/Packager/__tests__/Package-test.js +++ b/packager/react-packager/src/Packager/__tests__/Package-test.js @@ -76,6 +76,18 @@ describe('Package', function() { expect(s).toEqual(genSourceMap(p._modules)); }); }); + + describe('getAssets()', function() { + it('should save and return asset objects', function() { + var p = new Package('test_url'); + var asset1 = {}; + var asset2 = {}; + p.addAsset(asset1); + p.addAsset(asset2); + p.finalize(); + expect(p.getAssets()).toEqual([asset1, asset2]); + }); + }); }); function genSourceMap(modules) { diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index 333e0f5631e1df..c177321651bc67 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -140,6 +140,14 @@ describe('Packager', function() { expect(p.finalize.mock.calls[0]).toEqual([ {runMainModule: true} ]); + + expect(p.addAsset.mock.calls[0]).toEqual([ + imgModule_DEPRECATED + ]); + + expect(p.addAsset.mock.calls[1]).toEqual([ + imgModule + ]); }); }); diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index 2b1eb6b1658872..aab55c0842a062 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -100,9 +100,9 @@ Packager.prototype.kill = function() { }; Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) { - var transformModule = this._transformModule.bind(this); var ppackage = new Package(sourceMapUrl); + var transformModule = this._transformModule.bind(this, ppackage); var findEventId = Activity.startEvent('find dependencies'); var transformEventId; @@ -140,16 +140,13 @@ Packager.prototype.getDependencies = function(main, isDev) { return this._resolver.getDependencies(main, { dev: isDev }); }; -Packager.prototype._transformModule = function(module) { +Packager.prototype._transformModule = function(ppackage, module) { var transform; if (module.isAsset_DEPRECATED) { - transform = generateAssetModule_DEPRECATED(module); + transform = this.generateAssetModule_DEPRECATED(ppackage, module); } else if (module.isAsset) { - transform = generateAssetModule( - module, - getPathRelativeToRoot(this._projectRoots, module.path) - ); + transform = this.generateAssetModule(ppackage, module); } else { transform = this._transformer.loadFileAndTransform( path.resolve(module.path) @@ -166,17 +163,11 @@ Packager.prototype._transformModule = function(module) { }); }; - -function verifyRootExists(root) { - // Verify that the root exists. - assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory'); -} - Packager.prototype.getGraphDebugInfo = function() { return this._resolver.getDebugInfo(); }; -function generateAssetModule_DEPRECATED(module) { +Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) { return sizeOf(module.path).then(function(dimensions) { var img = { isStatic: true, @@ -187,6 +178,7 @@ function generateAssetModule_DEPRECATED(module) { deprecated: true, }; + ppackage.addAsset(img); var code = 'module.exports = ' + JSON.stringify(img) + ';'; @@ -196,9 +188,11 @@ function generateAssetModule_DEPRECATED(module) { sourcePath: module.path, }; }); -} +}; + +Packager.prototype.generateAssetModule = function(ppackage, module) { + var relPath = getPathRelativeToRoot(this._projectRoots, module.path); -function generateAssetModule(module, relPath) { return sizeOf(module.path).then(function(dimensions) { var img = { isStatic: true, @@ -208,6 +202,8 @@ function generateAssetModule(module, relPath) { height: dimensions.height / module.resolution, }; + ppackage.addAsset(img); + var code = 'module.exports = ' + JSON.stringify(img) + ';'; return { @@ -231,4 +227,9 @@ function getPathRelativeToRoot(roots, absPath) { ); } +function verifyRootExists(root) { + // Verify that the root exists. + assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory'); +} + module.exports = Packager; From 765779a4bd16144924e0d03aa726401e2ea4a63e Mon Sep 17 00:00:00 2001 From: Lochlan Wansbrough Date: Mon, 20 Apr 2015 18:01:46 -0700 Subject: [PATCH 032/250] Adds `opaque` and `underlayColor` to WebView. Summary: Enables overwriting of underlying colors for WebViews. Especially useful if you want to give your WebView a transparent background. Closes https://github.com/facebook/react-native/pull/767 Github Author: Lochlan Wansbrough Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Components/WebView/WebView.ios.js | 11 ++++++++++- Libraries/RCTWebSocketDebugger/SRWebSocket.m | 2 +- React/Views/RCTWebView.m | 13 +++++++++++++ React/Views/RCTWebViewManager.m | 2 ++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 83c90a1fdbe915..6257c12b71a8c2 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -87,6 +87,8 @@ var WebView = React.createClass({ html: PropTypes.string, renderError: PropTypes.func, // view to show if there's an error renderLoading: PropTypes.func, // loading indicator to show + bounces: PropTypes.bool, + scrollEnabled: PropTypes.bool, automaticallyAdjustContentInsets: PropTypes.bool, shouldInjectAJAXHandler: PropTypes.bool, contentInset: EdgeInsetsPropType, @@ -131,7 +133,7 @@ var WebView = React.createClass({ ); } - var webViewStyles = [styles.container, this.props.style]; + var webViewStyles = [styles.container, styles.webView, this.props.style]; if (this.state.viewState === WebViewState.LOADING || this.state.viewState === WebViewState.ERROR) { // if we're in either LOADING or ERROR states, don't show the webView @@ -145,6 +147,8 @@ var WebView = React.createClass({ style={webViewStyles} url={this.props.url} html={this.props.html} + bounces={this.props.bounces} + scrollEnabled={this.props.scrollEnabled} shouldInjectAJAXHandler={this.props.shouldInjectAJAXHandler} contentInset={this.props.contentInset} automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets} @@ -213,6 +217,8 @@ var RCTWebView = createReactIOSNativeComponentClass({ validAttributes: merge(ReactIOSViewAttributes.UIView, { url: true, html: true, + bounces: true, + scrollEnabled: true, contentInset: {diff: insetsDiffer}, automaticallyAdjustContentInsets: true, shouldInjectAJAXHandler: true @@ -250,6 +256,9 @@ var styles = StyleSheet.create({ justifyContent: 'center', alignItems: 'center', }, + webView: { + backgroundColor: '#ffffff', + } }); module.exports = WebView; diff --git a/Libraries/RCTWebSocketDebugger/SRWebSocket.m b/Libraries/RCTWebSocketDebugger/SRWebSocket.m index 3fd675103367ce..589ab75ddb3af9 100644 --- a/Libraries/RCTWebSocketDebugger/SRWebSocket.m +++ b/Libraries/RCTWebSocketDebugger/SRWebSocket.m @@ -1702,7 +1702,7 @@ static inline int32_t validate_dispatch_data_partial_string(NSData *data) { for (int i = 0; i < maxCodepointSize; i++) { NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; if (str) { - return data.length - i; + return (int32_t)(data.length - i); } } diff --git a/React/Views/RCTWebView.m b/React/Views/RCTWebView.m index bb9bb2acfb6b44..56cda0c88b6971 100644 --- a/React/Views/RCTWebView.m +++ b/React/Views/RCTWebView.m @@ -31,6 +31,7 @@ @implementation RCTWebView - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher { if ((self = [super initWithFrame:CGRectZero])) { + super.backgroundColor = [UIColor clearColor]; _automaticallyAdjustContentInsets = YES; _contentInset = UIEdgeInsetsZero; _eventDispatcher = eventDispatcher; @@ -95,6 +96,18 @@ - (void)setContentInset:(UIEdgeInsets)contentInset updateOffset:NO]; } +- (void)setBackgroundColor:(UIColor *)backgroundColor +{ + CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor); + self.opaque = _webView.opaque = (alpha == 1.0); + _webView.backgroundColor = backgroundColor; +} + +- (UIColor *)backgroundColor +{ + return _webView.backgroundColor; +} + - (NSMutableDictionary *)baseEvent { NSURL *url = _webView.request.URL; diff --git a/React/Views/RCTWebViewManager.m b/React/Views/RCTWebViewManager.m index e25a7da68b98b2..015285871ed4e0 100644 --- a/React/Views/RCTWebViewManager.m +++ b/React/Views/RCTWebViewManager.m @@ -25,6 +25,8 @@ - (UIView *)view RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL); RCT_REMAP_VIEW_PROPERTY(html, HTML, NSString); +RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL); +RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL); RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets); RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL); RCT_EXPORT_VIEW_PROPERTY(shouldInjectAJAXHandler, BOOL); From b0348edcaee00241e5a624bfc8d49ae67abc1923 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Tue, 21 Apr 2015 04:14:17 -0700 Subject: [PATCH 033/250] [react_native] JS files from D2001635: [react_native] Use hardware layers during adsmanager Navigator navigation --- .../CustomComponents/Navigator/Navigator.js | 31 +++++++++++++++++++ .../NavigatorBreadcrumbNavigationBar.js | 31 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index f783c34eaf147d..7a030b45580ea4 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -494,6 +494,7 @@ var Navigator = React.createClass({ _completeTransition: function() { if (this.spring.getCurrentValue() === 1) { + this._onAnimationEnd(); var presentedIndex = this.state.toIndex; this.state.presentedIndex = presentedIndex; this.state.fromIndex = presentedIndex; @@ -515,6 +516,7 @@ var Navigator = React.createClass({ // For visual consistency, the from index is always used to configure the spring this.state.sceneConfigStack[this.state.fromIndex] ); + this._onAnimationStart(); this.state.isAnimating = true; this.spring.setVelocity(v); this.spring.setEndValue(1); @@ -573,6 +575,34 @@ var Navigator = React.createClass({ } }, + _onAnimationStart: function() { + this._setRenderSceneToHarwareTextureAndroid(this.state.fromIndex, true); + this._setRenderSceneToHarwareTextureAndroid(this.state.toIndex, true); + + var navBar = this._navBar; + if (navBar && navBar.onAnimationStart) { + navBar.onAnimationStart(this.state.fromIndex, this.state.toIndex); + } + }, + + _onAnimationEnd: function() { + this._setRenderSceneToHarwareTextureAndroid(this.state.fromIndex, false); + this._setRenderSceneToHarwareTextureAndroid(this.state.toIndex, false); + + var navBar = this._navBar; + if (navBar && navBar.onAnimationEnd) { + navBar.onAnimationEnd(this.state.fromIndex, this.state.toIndex); + } + }, + + _setRenderSceneToHarwareTextureAndroid: function(sceneIndex, shouldRenderToHardwareTexture) { + var viewAtIndex = this.refs['scene_' + sceneIndex]; + if (viewAtIndex === null || viewAtIndex === undefined) { + return; + } + viewAtIndex.setNativeProps({renderToHardwareTextureAndroid: shouldRenderToHardwareTexture}); + }, + /** * Becomes the responder on touch start (capture) while animating so that it * blocks all touch interactions inside of it. However, this responder lock @@ -610,6 +640,7 @@ var Navigator = React.createClass({ this.state.fromIndex = this.state.presentedIndex; var gestureSceneDelta = this._deltaForGestureAction(this._activeGestureAction); this.state.toIndex = this.state.presentedIndex + gestureSceneDelta; + this.onAnimationStart(); } }, diff --git a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js index 9fb265f976839e..fa62b3f455baf6 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js +++ b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js @@ -138,6 +138,37 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({ } }, + onAnimationStart: function(fromIndex, toIndex) { + var max = Math.max(fromIndex, toIndex); + var min = Math.min(fromIndex, toIndex); + for (var index = min; index <= max; index++) { + this._setRenderViewsToHardwareTextureAndroid(index, true); + } + }, + + onAnimationEnd: function(fromIndex, toIndex) { + var max = Math.max(fromIndex, toIndex); + var min = Math.min(fromIndex, toIndex); + for (var index = min; index <= max; index++) { + this._setRenderViewsToHardwareTextureAndroid(index, false); + } + }, + + _setRenderViewsToHardwareTextureAndroid: function(index, renderToHardwareTexture) { + var props = { + renderToHardwareTextureAndroid: renderToHardwareTexture, + }; + + this.refs['crumb_' + index].setNativeProps(props); + this.refs['icon_' + index].setNativeProps(props); + this.refs['separator_' + index].setNativeProps(props); + this.refs['title_' + index].setNativeProps(props); + var right = this.refs['right_' + index]; + if (right) { + right.setNativeProps(props); + } + }, + render: function() { var navState = this.props.navState; var icons = navState && navState.routeStack.map(this._renderOrReturnBreadcrumb); From 8e15a0d5e716ca63d0610e4b947ff2ab784e3ca2 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 21 Apr 2015 05:26:51 -0700 Subject: [PATCH 034/250] Added RCT_DEBUG --- React/Base/RCTAssert.h | 31 +++--------- React/Base/RCTBridge.h | 3 +- React/Base/RCTBridge.m | 75 ++++++++++++++-------------- React/Base/RCTConvert.h | 15 ++---- React/Base/RCTDefines.h | 55 ++++++++++++++++++++ React/Base/RCTLog.h | 27 ++++------ React/Base/RCTLog.m | 30 +++++------ React/Base/RCTProfile.h | 20 ++++---- React/Base/RCTProfile.m | 3 +- React/Base/RCTRedBox.m | 23 +++------ React/Base/RCTUtils.h | 39 ++++++--------- React/Executors/RCTContextExecutor.m | 8 +-- React/Modules/RCTExceptionsManager.m | 23 +++------ React/Modules/RCTUIManager.m | 23 ++++----- 14 files changed, 185 insertions(+), 190 deletions(-) create mode 100644 React/Base/RCTDefines.h diff --git a/React/Base/RCTAssert.h b/React/Base/RCTAssert.h index 7e73aed7d6ab33..b0a3c5c52f553f 100644 --- a/React/Base/RCTAssert.h +++ b/React/Base/RCTAssert.h @@ -9,21 +9,7 @@ #import -#ifdef __cplusplus -extern "C" { -#endif - -/** - * By default, only raise an NSAssertion in debug mode - * (custom assert functions will still be called). - */ -#ifndef RCT_ASSERT -#if DEBUG -#define RCT_ASSERT 1 -#else -#define RCT_ASSERT 0 -#endif -#endif +#import "RCTDefines.h" /** * The default error domain to be used for React errors. @@ -44,13 +30,14 @@ typedef void (^RCTAssertFunction)( /** * Private logging function - ignore this. */ -void _RCTAssertFormat(BOOL, const char *, int, const char *, NSString *, ...) NS_FORMAT_FUNCTION(5,6); +RCT_EXTERN void _RCTAssertFormat( + BOOL, const char *, int, const char *, NSString *, ...) NS_FORMAT_FUNCTION(5,6); /** * This is the main assert macro that you should use. */ #define RCTAssert(condition, ...) do { BOOL pass = ((condition) != 0); \ -if (RCT_ASSERT && !pass) { [[NSAssertionHandler currentHandler] handleFailureInFunction:@(__func__) \ +if (RCT_NSASSERT && !pass) { [[NSAssertionHandler currentHandler] handleFailureInFunction:@(__func__) \ file:@(__FILE__) lineNumber:__LINE__ description:__VA_ARGS__]; } \ _RCTAssertFormat(pass, __FILE__, __LINE__, __func__, __VA_ARGS__); \ } while (false) @@ -66,16 +53,12 @@ _RCTAssertFormat(pass, __FILE__, __LINE__, __func__, __VA_ARGS__); \ * macros. You can use these to replace the standard behavior with custom log * functionality. */ -void RCTSetAssertFunction(RCTAssertFunction assertFunction); -RCTAssertFunction RCTGetAssertFunction(void); +RCT_EXTERN void RCTSetAssertFunction(RCTAssertFunction assertFunction); +RCT_EXTERN RCTAssertFunction RCTGetAssertFunction(void); /** * This appends additional code to the existing assert function, without * replacing the existing functionality. Useful if you just want to forward * assert info to an extra service without changing the default behavior. */ -void RCTAddAssertFunction(RCTAssertFunction assertFunction); - -#ifdef __cplusplus -} -#endif +RCT_EXTERN void RCTAddAssertFunction(RCTAssertFunction assertFunction); diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 0a82b05e2c440e..a37b9ac7db946f 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -10,6 +10,7 @@ #import #import "RCTBridgeModule.h" +#import "RCTDefines.h" #import "RCTFrameUpdate.h" #import "RCTInvalidating.h" #import "RCTJavaScriptExecutor.h" @@ -40,7 +41,7 @@ typedef NSArray *(^RCTBridgeModuleProviderBlock)(void); /** * This function returns the module name for a given class. */ -extern NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); +RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); /** * Async batched bridge used to communicate with the JavaScript application. diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 31a8bea7736921..40dfceec8f6ea4 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -143,7 +143,8 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { // Get data entry NSString *entry = @(*(const char **)(mach_header + addr)); - NSArray *parts = [[entry substringWithRange:(NSRange){2, entry.length - 3}] componentsSeparatedByString:@" "]; + NSArray *parts = [[entry substringWithRange:(NSRange){2, entry.length - 3}] + componentsSeparatedByString:@" "]; // Parse class name NSString *moduleClassName = parts[0]; @@ -164,33 +165,32 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { } } -#if DEBUG + if (RCT_DEBUG) { - // We may be able to get rid of this check in future, once people - // get used to the new registration system. That would potentially - // allow you to create modules that are not automatically registered + // We may be able to get rid of this check in future, once people + // get used to the new registration system. That would potentially + // allow you to create modules that are not automatically registered - static unsigned int classCount; - Class *classes = objc_copyClassList(&classCount); - for (unsigned int i = 0; i < classCount; i++) - { - Class cls = classes[i]; - Class superclass = cls; - while (superclass) + static unsigned int classCount; + Class *classes = objc_copyClassList(&classCount); + for (unsigned int i = 0; i < classCount; i++) { - if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule))) + Class cls = classes[i]; + Class superclass = cls; + while (superclass) { - if (![RCTModuleClassesByID containsObject:cls]) { - RCTLogError(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", NSStringFromClass(cls)); + if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule))) + { + if (![RCTModuleClassesByID containsObject:cls]) { + RCTLogError(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", NSStringFromClass(cls)); + } + break; } - break; + superclass = class_getSuperclass(superclass); } - superclass = class_getSuperclass(superclass); } } -#endif - }); return RCTModuleClassesByID; @@ -289,13 +289,13 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName _isClassMethod = [reactMethodName characterAtIndex:0] == '+'; _moduleClass = NSClassFromString(_moduleClassName); -#if DEBUG + if (RCT_DEBUG) { - // Sanity check - RCTAssert([_moduleClass conformsToProtocol:@protocol(RCTBridgeModule)], - @"You are attempting to export the method %@, but %@ does not \ - conform to the RCTBridgeModule Protocol", objCMethodName, _moduleClassName); -#endif + // Sanity check + RCTAssert([_moduleClass conformsToProtocol:@protocol(RCTBridgeModule)], + @"You are attempting to export the method %@, but %@ does not \ + conform to the RCTBridgeModule Protocol", objCMethodName, _moduleClassName); + } // Get method signature _methodSignature = _isClassMethod ? @@ -449,20 +449,19 @@ - (void)invokeWithBridge:(RCTBridge *)bridge arguments:(NSArray *)arguments context:(NSNumber *)context { + if (RCT_DEBUG) { -#if DEBUG - - // Sanity check - RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \ - %@ on a module of class %@", _methodName, [module class]); -#endif - - // Safety check - if (arguments.count != _argumentBlocks.count) { - RCTLogError(@"%@.%@ was called with %zd arguments, but expects %zd", - RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, - arguments.count, _argumentBlocks.count); - return; + // Sanity check + RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \ + %@ on a module of class %@", _methodName, [module class]); + + // Safety check + if (arguments.count != _argumentBlocks.count) { + RCTLogError(@"%@.%@ was called with %zd arguments, but expects %zd", + RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, + arguments.count, _argumentBlocks.count); + return; + } } // Create invocation (we can't re-use this as it wouldn't be thread-safe) diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 22cc7ec816b3ee..e664f06e0863e1 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -16,6 +16,7 @@ #import "../Views/RCTAnimationType.h" #import "../Views/RCTPointerEvents.h" +#import "RCTDefines.h" #import "RCTLog.h" /** @@ -116,33 +117,25 @@ typedef BOOL css_overflow; @end -#ifdef __cplusplus -extern "C" { -#endif - /** * This function will attempt to set a property using a json value by first * inferring the correct type from all available information, and then * applying an appropriate conversion method. If the property does not * exist, or the type cannot be inferred, the function will return NO. */ -BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json); +RCT_EXTERN BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json); /** * This function attempts to copy a property from the source object to the * destination object using KVC. If the property does not exist, or cannot * be set, it will do nothing and return NO. */ -BOOL RCTCopyProperty(id target, id source, NSString *keyPath); +RCT_EXTERN BOOL RCTCopyProperty(id target, id source, NSString *keyPath); /** * Underlying implementation of RCT_ENUM_CONVERTER macro. Ignore this. */ -NSNumber *RCTConvertEnumValue(const char *, NSDictionary *, NSNumber *, id); - -#ifdef __cplusplus -} -#endif +RCT_EXTERN NSNumber *RCTConvertEnumValue(const char *, NSDictionary *, NSNumber *, id); /** * This macro is used for creating simple converter functions that just call diff --git a/React/Base/RCTDefines.h b/React/Base/RCTDefines.h new file mode 100644 index 00000000000000..71550a30d1c6c4 --- /dev/null +++ b/React/Base/RCTDefines.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +/** + * Make global functions usable in C++ + */ +#if defined(__cplusplus) +#define RCT_EXTERN extern "C" __attribute__((visibility("default"))) +#else +#define RCT_EXTERN extern __attribute__((visibility("default"))) +#endif + +/** + * The RCT_DEBUG macro can be used to exclude error checking and logging code + * from release builds to improve performance and reduce binary size. + */ +#ifndef RCT_DEBUG +#if DEBUG +#define RCT_DEBUG 1 +#else +#define RCT_DEBUG 0 +#endif +#endif + +/** + * The RCT_DEV macro can be used to enable or disable development tools + * such as the debug executors, dev menu, red box, etc. + */ +#ifndef RCT_DEV +#if DEBUG +#define RCT_DEV 1 +#else +#define RCT_DEV 0 +#endif +#endif + +/** + * By default, only raise an NSAssertion in debug mode + * (custom assert functions will still be called). + */ +#ifndef RCT_NSASSERT +#if RCT_DEBUG +#define RCT_NSASSERT 1 +#else +#define RCT_NSASSERT 0 +#endif +#endif diff --git a/React/Base/RCTLog.h b/React/Base/RCTLog.h index 3a8a70d5b6af34..7fa25e6a88ce42 100644 --- a/React/Base/RCTLog.h +++ b/React/Base/RCTLog.h @@ -10,10 +10,7 @@ #import #import "RCTAssert.h" - -#ifdef __cplusplus -extern "C" { -#endif +#import "RCTDefines.h" /** * Thresholds for logs to raise an assertion, or display redbox, respectively. @@ -46,9 +43,9 @@ typedef void (^RCTLogFunction)( ); /** - * Get a given thread's name (or the current queue, iff in debug mode) + * Get a given thread's name (or the current queue, if in debug mode) */ -NSString *RCTThreadName(NSThread *); +RCT_EXTERN NSString *RCTThreadName(NSThread *); /** * A method to generate a string from a collection of log data. To omit any @@ -73,35 +70,35 @@ extern RCTLogFunction RCTDefaultLogFunction; * below which logs will be ignored. Default is RCTLogLevelInfo for debug and * RCTLogLevelError for production. */ -void RCTSetLogThreshold(RCTLogLevel threshold); -RCTLogLevel RCTGetLogThreshold(void); +RCT_EXTERN void RCTSetLogThreshold(RCTLogLevel threshold); +RCT_EXTERN RCTLogLevel RCTGetLogThreshold(void); /** * These methods get and set the current logging function called by the RCTLogXX * macros. You can use these to replace the standard behavior with custom log * functionality. */ -void RCTSetLogFunction(RCTLogFunction logFunction); -RCTLogFunction RCTGetLogFunction(void); +RCT_EXTERN void RCTSetLogFunction(RCTLogFunction logFunction); +RCT_EXTERN RCTLogFunction RCTGetLogFunction(void); /** * This appends additional code to the existing log function, without replacing * the existing functionality. Useful if you just want to forward logs to an * extra service without changing the default behavior. */ -void RCTAddLogFunction(RCTLogFunction logFunction); +RCT_EXTERN void RCTAddLogFunction(RCTLogFunction logFunction); /** * This method adds a conditional prefix to any messages logged within the scope * of the passed block. This is useful for adding additional context to log * messages. The block will be performed synchronously on the current thread. */ -void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix); +RCT_EXTERN void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix); /** * Private logging functions - ignore these. */ -void _RCTLogFormat(RCTLogLevel, const char *, int, NSString *, ...) NS_FORMAT_FUNCTION(4,5); +RCT_EXTERN void _RCTLogFormat(RCTLogLevel, const char *, int, NSString *, ...) NS_FORMAT_FUNCTION(4,5); #define _RCTLog(lvl, ...) do { \ if (lvl >= RCTLOG_FATAL_LEVEL) { RCTAssert(NO, __VA_ARGS__); } \ _RCTLogFormat(lvl, __FILE__, __LINE__, __VA_ARGS__); \ @@ -116,7 +113,3 @@ void _RCTLogFormat(RCTLogLevel, const char *, int, NSString *, ...) NS_FORMAT_FU #define RCTLogWarn(...) _RCTLog(RCTLogLevelWarning, __VA_ARGS__) #define RCTLogError(...) _RCTLog(RCTLogLevelError, __VA_ARGS__) #define RCTLogMustFix(...) _RCTLog(RCTLogLevelMustFix, __VA_ARGS__) - -#ifdef __cplusplus -} -#endif diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index 449980fc3b9620..6ca2d4eb8ecb31 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -11,6 +11,7 @@ #import "RCTAssert.h" #import "RCTBridge.h" +#import "RCTDefines.h" #import "RCTRedBox.h" @interface RCTBridge (Logging) @@ -36,7 +37,7 @@ static void RCTLogSetup() { RCTCurrentLogFunction = RCTDefaultLogFunction; -#if DEBUG +#if RCT_DEBUG RCTCurrentLogThreshold = RCTLogLevelInfo - 1; #else RCTCurrentLogThreshold = RCTLogLevelError; @@ -102,7 +103,7 @@ void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix) { NSString *threadName = [thread isMainThread] ? @"main" : thread.name; if (threadName.length == 0) { -#if DEBUG +#if DEBUG // This is DEBUG not RCT_DEBUG because it *really* must not ship in RC #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" threadName = @(dispatch_queue_get_label(dispatch_get_current_queue())); @@ -161,12 +162,7 @@ void _RCTLogFormat( NSString *format, ...) { -#if DEBUG - BOOL log = YES; -#else - BOOL log = (RCTCurrentLogFunction != nil); -#endif - + BOOL log = RCT_DEBUG || (RCTCurrentLogFunction != nil); if (log && level >= RCTCurrentLogThreshold) { // Get message @@ -188,17 +184,15 @@ void _RCTLogFormat( level, fileName ? @(fileName) : nil, (lineNumber >= 0) ? @(lineNumber) : nil, message ); -#if DEBUG - - // Log to red box - if (level >= RCTLOG_REDBOX_LEVEL) { - [[RCTRedBox sharedInstance] showErrorMessage:message]; - } - - // Log to JS executor - [RCTBridge logMessage:message level:level ? @(RCTLogLevels[level - 1]) : @"info"]; + if (RCT_DEBUG) { -#endif + // Log to red box + if (level >= RCTLOG_REDBOX_LEVEL) { + [[RCTRedBox sharedInstance] showErrorMessage:message]; + } + // Log to JS executor + [RCTBridge logMessage:message level:level ? @(RCTLogLevels[level - 1]) : @"info"]; + } } } diff --git a/React/Base/RCTProfile.h b/React/Base/RCTProfile.h index b3a1683d1d30cc..0c254c80a15c0d 100644 --- a/React/Base/RCTProfile.h +++ b/React/Base/RCTProfile.h @@ -9,45 +9,47 @@ #import +#import "RCTDefines.h" + /** * RCTProfile * * This file provides a set of functions and macros for performance profiling * - * NOTE: This API is a work in a work in progress, please consider it before - * before using. + * NOTE: This API is a work in a work in progress, please consider carefully + * before before using it. */ -#if DEBUG +#if RCT_DEV /** * Returns YES if the profiling information is currently being collected */ -BOOL RCTProfileIsProfiling(void); +RCT_EXTERN BOOL RCTProfileIsProfiling(void); /** * Start collecting profiling information */ -void RCTProfileInit(void); +RCT_EXTERN void RCTProfileInit(void); /** * Stop profiling and return a JSON string of the collected data - The data * returned is compliant with google's trace event format - the format used * as input to trace-viewer */ -NSString *RCTProfileEnd(void); +RCT_EXTERN NSString *RCTProfileEnd(void); /** * Collects the initial event information for the event and returns a reference ID */ -NSNumber *_RCTProfileBeginEvent(void); +RCT_EXTERN NSNumber *_RCTProfileBeginEvent(void); /** * The ID returned by BeginEvent should then be passed into EndEvent, with the * rest of the event information. Just at this point the event will actually be * registered */ -void _RCTProfileEndEvent(NSNumber *, NSString *, NSString *, id); +RCT_EXTERN void _RCTProfileEndEvent(NSNumber *, NSString *, NSString *, id); /** * This pair of macros implicitly handle the event ID when beginning and ending @@ -69,7 +71,7 @@ _RCTProfileEndEvent(__rct_profile_id, name, category, args) /** * An event that doesn't have a duration (i.e. Notification, VSync, etc) */ -void RCTProfileImmediateEvent(NSString *, NSTimeInterval , NSString *); +RCT_EXTERN void RCTProfileImmediateEvent(NSString *, NSTimeInterval , NSString *); /** * Helper to profile the duration of the execution of a block. This method uses diff --git a/React/Base/RCTProfile.m b/React/Base/RCTProfile.m index 71d34551e9c37a..929a026a9587a2 100644 --- a/React/Base/RCTProfile.m +++ b/React/Base/RCTProfile.m @@ -13,10 +13,11 @@ #import +#import "RCTDefines.h" #import "RCTLog.h" #import "RCTUtils.h" -#if DEBUG +#if RCT_DEV #pragma mark - Prototypes diff --git a/React/Base/RCTRedBox.m b/React/Base/RCTRedBox.m index 3bed3150578c6a..882a87bfe8d736 100644 --- a/React/Base/RCTRedBox.m +++ b/React/Base/RCTRedBox.m @@ -282,23 +282,14 @@ - (void)updateErrorMessage:(NSString *)message withStack:(NSArray *)stack - (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow { - -#if DEBUG - - dispatch_block_t block = ^{ - if (!_window) { - _window = [[RCTRedBoxWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - } - [_window showErrorMessage:message withStack:stack showIfHidden:shouldShow]; - }; - if ([NSThread isMainThread]) { - block(); - } else { - dispatch_async(dispatch_get_main_queue(), block); + if (RCT_DEBUG) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (!_window) { + _window = [[RCTRedBoxWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + } + [_window showErrorMessage:message withStack:stack showIfHidden:shouldShow]; + }); } - -#endif - } - (NSString *)currentErrorMessage diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h index d20ba8a5faf47e..812a651222a85c 100644 --- a/React/Base/RCTUtils.h +++ b/React/Base/RCTUtils.h @@ -13,43 +13,36 @@ #import #import "RCTAssert.h" - -#ifdef __cplusplus -extern "C" { -#endif +#import "RCTDefines.h" // Utility functions for JSON object <-> string serialization/deserialization -NSString *RCTJSONStringify(id jsonObject, NSError **error); -id RCTJSONParse(NSString *jsonString, NSError **error); +RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error); +RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error); // Get MD5 hash of a string (TODO: currently unused. Remove?) -NSString *RCTMD5Hash(NSString *string); +RCT_EXTERN NSString *RCTMD5Hash(NSString *string); // Get screen metrics in a thread-safe way -CGFloat RCTScreenScale(void); -CGSize RCTScreenSize(void); +RCT_EXTERN CGFloat RCTScreenScale(void); +RCT_EXTERN CGSize RCTScreenSize(void); // Round float coordinates to nearest whole screen pixel (not point) -CGFloat RCTRoundPixelValue(CGFloat value); -CGFloat RCTCeilPixelValue(CGFloat value); -CGFloat RCTFloorPixelValue(CGFloat value); +RCT_EXTERN CGFloat RCTRoundPixelValue(CGFloat value); +RCT_EXTERN CGFloat RCTCeilPixelValue(CGFloat value); +RCT_EXTERN CGFloat RCTFloorPixelValue(CGFloat value); // Get current time, for precise performance metrics -NSTimeInterval RCTTGetAbsoluteTime(void); +RCT_EXTERN NSTimeInterval RCTTGetAbsoluteTime(void); // Method swizzling -void RCTSwapClassMethods(Class cls, SEL original, SEL replacement); -void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement); +RCT_EXTERN void RCTSwapClassMethods(Class cls, SEL original, SEL replacement); +RCT_EXTERN void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement); // Module subclass support -BOOL RCTClassOverridesClassMethod(Class cls, SEL selector); -BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector); +RCT_EXTERN BOOL RCTClassOverridesClassMethod(Class cls, SEL selector); +RCT_EXTERN BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector); // Creates a standardized error object // TODO(#6472857): create NSErrors and automatically convert them over the bridge. -NSDictionary *RCTMakeError(NSString *message, id toStringify, NSDictionary *extraData); -NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary *extraData); - -#ifdef __cplusplus -} -#endif +RCT_EXTERN NSDictionary *RCTMakeError(NSString *message, id toStringify, NSDictionary *extraData); +RCT_EXTERN NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary *extraData); diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index d2aac3fbe6df7d..86444dd2a7f903 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -14,6 +14,7 @@ #import #import "RCTAssert.h" +#import "RCTDefines.h" #import "RCTLog.h" #import "RCTProfile.h" #import "RCTUtils.h" @@ -321,10 +322,9 @@ - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete { - -#if DEBUG - RCTAssert(RCTJSONParse(script, NULL) != nil, @"%@ wasn't valid JSON!", script); -#endif + if (RCT_DEBUG) { + RCTAssert(RCTJSONParse(script, NULL) != nil, @"%@ wasn't valid JSON!", script); + } __weak RCTContextExecutor *weakSelf = self; [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index ed30d874185465..ece5d06687f10a 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -9,6 +9,7 @@ #import "RCTExceptionsManager.h" +#import "RCTDefines.h" #import "RCTLog.h" #import "RCTRedBox.h" #import "RCTRootView.h" @@ -43,11 +44,10 @@ - (instancetype)init return; } -#if DEBUG - - [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; - -#else + if (RCT_DEBUG) { + [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; + return; + } static NSUInteger reloadRetries = 0; const NSUInteger maxMessageLength = 75; @@ -76,21 +76,14 @@ - (instancetype)init NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage]; [NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack]; } - -#endif - } RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message stack:(NSArray *)stack) { - -#if DEBUG - - [[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack]; - -#endif - + if (RCT_DEBUG) { + [[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack]; + } } @end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index e2dc8d560d089b..3f3ea75960dcb9 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -18,6 +18,7 @@ #import "RCTAssert.h" #import "RCTBridge.h" #import "RCTConvert.h" +#import "RCTDefines.h" #import "RCTLog.h" #import "RCTProfile.h" #import "RCTRootView.h" @@ -703,20 +704,16 @@ static BOOL RCTCallPropertySetter(NSString *key, SEL setter, id value, id view, ((void (*)(id, SEL, id, id, id))objc_msgSend)(manager, setter, value, view, defaultView); }; -#if DEBUG + if (RCT_DEBUG) { + NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([manager class])); + NSString *logPrefix = [NSString stringWithFormat: + @"Error setting property '%@' of %@ with tag #%@: ", + key, viewName, [view reactTag]]; - NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([manager class])); - NSString *logPrefix = [NSString stringWithFormat: - @"Error setting property '%@' of %@ with tag #%@: ", - key, viewName, [view reactTag]]; - - RCTPerformBlockWithLogPrefix(block, logPrefix); - -#else - - block(); - -#endif + RCTPerformBlockWithLogPrefix(block, logPrefix); + } else { + block(); + } return YES; } From a0db658982f0b9e8a42fb17bfbbf32da5d876572 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Tue, 21 Apr 2015 09:01:09 -0700 Subject: [PATCH 035/250] [MAdMan] flowify AdsManagerGeoUtils --- Libraries/Components/MapView/MapView.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index 7beeabbeac5f78..2f02b1b9dc5f51 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -23,6 +23,12 @@ var insetsDiffer = require('insetsDiffer'); var merge = require('merge'); type Event = Object; +type MapRegion = { + latitude: number; + longitude: number; + latitudeDelta: number; + longitudeDelta: number; +}; var MapView = React.createClass({ mixins: [NativeMethodsMixin], From 25ae5485da2236e2ace73b7e9d27fb41c3f62ead Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Tue, 21 Apr 2015 09:18:51 -0700 Subject: [PATCH 036/250] [ReactNative][MAdMan] helper for GraphQL-safe pixel size calculation --- Libraries/Utilities/PixelRatio.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Libraries/Utilities/PixelRatio.js b/Libraries/Utilities/PixelRatio.js index f8e23398a9c802..e6c96b7fa6b30e 100644 --- a/Libraries/Utilities/PixelRatio.js +++ b/Libraries/Utilities/PixelRatio.js @@ -36,8 +36,8 @@ var Dimensions = require('Dimensions'); * * ``` * var image = getImage({ - * width: 200 * PixelRatio.get(), - * height: 100 * PixelRatio.get() + * width: PixelRatio.getPixelSizeForLayoutSize(200), + * height: PixelRatio.getPixelSizeForLayoutSize(100), * }); * * ``` @@ -52,10 +52,21 @@ class PixelRatio { * - iPhone 6 * - PixelRatio.get() === 3 * - iPhone 6 plus + * - PixelRatio.get() === 3.5 + * - Nexus 6 */ static get(): number { return Dimensions.get('window').scale; } + + /** + * Converts a layout size (dp) to pixel size (px). + * + * Guaranteed to return an integer number. + */ + static getPixelSizeForLayoutSize(layoutSize: number): number { + return Math.round(layoutSize * PixelRatio.get()); + } } // No-op for iOS, but used on the web. Should not be documented. From ee898c24c79c40653cbba7d2789e7c88ef73cde4 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 21 Apr 2015 09:48:29 -0700 Subject: [PATCH 037/250] Removed debug code from release builds --- .../project.pbxproj | 12 +- .../RCTWebSocketExecutor.h | 6 + .../RCTWebSocketExecutor.m | 6 + Libraries/RCTWebSocketDebugger/SRWebSocket.m | 2 + React/Base/RCTBridge.m | 55 +++++-- React/Base/RCTConvert.h | 40 +++-- React/Base/RCTConvert.m | 137 +++++++++++------- React/Base/RCTLog.h | 2 +- React/Base/RCTLog.m | 6 +- React/Base/RCTRedBox.h | 6 + React/Base/RCTRedBox.m | 20 ++- React/Executors/RCTWebViewExecutor.h | 6 + React/Executors/RCTWebViewExecutor.m | 13 +- React/Modules/RCTExceptionsManager.m | 20 ++- React/Modules/RCTUIManager.m | 10 +- React/React.xcodeproj/project.pbxproj | 2 + React/Views/RCTViewManager.h | 4 +- 17 files changed, 226 insertions(+), 121 deletions(-) diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj b/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj index acb5daa3e736b2..4a9b599165984e 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 00D277161AB8C32C00DC1E48 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D277151AB8C32C00DC1E48 /* RCTWebSocketExecutor.m */; }; - 00D277191AB8C35800DC1E48 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D277181AB8C35800DC1E48 /* SRWebSocket.m */; }; + 00D277191AB8C35800DC1E48 /* RCT_SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D277181AB8C35800DC1E48 /* RCT_SRWebSocket.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -26,8 +26,8 @@ /* Begin PBXFileReference section */ 00D277141AB8C32C00DC1E48 /* RCTWebSocketExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketExecutor.h; sourceTree = ""; }; 00D277151AB8C32C00DC1E48 /* RCTWebSocketExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketExecutor.m; sourceTree = ""; }; - 00D277171AB8C35800DC1E48 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = ""; }; - 00D277181AB8C35800DC1E48 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = ""; }; + 00D277171AB8C35800DC1E48 /* RCT_SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCT_SRWebSocket.h; sourceTree = ""; }; + 00D277181AB8C35800DC1E48 /* RCT_SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCT_SRWebSocket.m; sourceTree = ""; }; 832C81801AAF6DEF007FA2F7 /* libRCTWebSocketDebugger.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocketDebugger.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -45,8 +45,8 @@ 832C81771AAF6DEF007FA2F7 = { isa = PBXGroup; children = ( - 00D277171AB8C35800DC1E48 /* SRWebSocket.h */, - 00D277181AB8C35800DC1E48 /* SRWebSocket.m */, + 00D277171AB8C35800DC1E48 /* RCT_SRWebSocket.h */, + 00D277181AB8C35800DC1E48 /* RCT_SRWebSocket.m */, 00D277141AB8C32C00DC1E48 /* RCTWebSocketExecutor.h */, 00D277151AB8C32C00DC1E48 /* RCTWebSocketExecutor.m */, 832C81811AAF6DEF007FA2F7 /* Products */, @@ -119,7 +119,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 00D277191AB8C35800DC1E48 /* SRWebSocket.m in Sources */, + 00D277191AB8C35800DC1E48 /* RCT_SRWebSocket.m in Sources */, 00D277161AB8C32C00DC1E48 /* RCTWebSocketExecutor.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.h b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.h index 3fc062a37eb639..9993cbc5a3060d 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.h +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.h @@ -7,6 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "RCTDefines.h" + +#if RCT_DEV // Debug executors are only supported in dev mode + #import "RCTJavaScriptExecutor.h" @interface RCTWebSocketExecutor : NSObject @@ -14,3 +18,5 @@ - (instancetype)initWithURL:(NSURL *)URL; @end + +#endif diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m index 4bdfab1bd53e04..eb6428fc27fe80 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m @@ -7,6 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "RCTDefines.h" + +#if RCT_DEV // Debug executors are only supported in dev mode + #import "RCTWebSocketExecutor.h" #import "RCTLog.h" @@ -189,3 +193,5 @@ - (void)dealloc } @end + +#endif diff --git a/Libraries/RCTWebSocketDebugger/SRWebSocket.m b/Libraries/RCTWebSocketDebugger/SRWebSocket.m index 589ab75ddb3af9..e98e9e45637167 100644 --- a/Libraries/RCTWebSocketDebugger/SRWebSocket.m +++ b/Libraries/RCTWebSocketDebugger/SRWebSocket.m @@ -19,6 +19,8 @@ #import +#pragma clang diagnostic ignored "-Wshadow" + //NOTE: libicucore ins't actually needed for the socket to function //and by commenting this out, we avoid the need to import it into every app. diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 40dfceec8f6ea4..1c676640eba501 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -314,7 +314,8 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName void (^addBlockArgument)(void) = ^{ RCT_ARG_BLOCK( - if (json && ![json isKindOfClass:[NSNumber class]]) { + + if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) { RCTLogError(@"Argument %tu (%@) of %@.%@ should be a number", index, json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName); return; @@ -391,7 +392,7 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName #define RCT_CASE(_value, _class, _logic) \ case _value: { \ RCT_ARG_BLOCK( \ - if (json && ![json isKindOfClass:[_class class]]) { \ + if (RCT_DEBUG && json && ![json isKindOfClass:[_class class]]) { \ RCTLogError(@"Argument %tu (%@) of %@.%@ should be of type %@", index, \ json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, [_class class]); \ return; \ @@ -407,7 +408,7 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName #define RCT_SIMPLE_CASE(_value, _type, _selector) \ case _value: { \ RCT_ARG_BLOCK( \ - if (json && ![json respondsToSelector:@selector(_selector)]) { \ + if (RCT_DEBUG && json && ![json respondsToSelector:@selector(_selector)]) { \ RCTLogError(@"Argument %tu (%@) of %@.%@ does not respond to selector: %@", \ index, json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \ return; \ @@ -888,6 +889,9 @@ - (void)setUp [loader loadBundleAtURL:_bundleURL onComplete:^(NSError *error) { _loading = NO; if (error != nil) { + +#if RCT_DEBUG // Red box is only available in debug mode + NSArray *stack = [[error userInfo] objectForKey:@"stack"]; if (stack) { [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription] @@ -896,7 +900,11 @@ - (void)setUp [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription] withDetails:[error localizedFailureReason]]; } + +#endif + } else { + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification object:self]; } @@ -936,6 +944,9 @@ - (void)bindKeys strongSelf.executorClass = Nil; [strongSelf reload]; }]; + +#if RCT_DEV // Debug executors are only available in dev mode + // reload in debug mode [commands registerKeyCommandWithInput:@"d" modifierFlags:UIKeyModifierCommand @@ -952,6 +963,7 @@ - (void)bindKeys [strongSelf reload]; }]; #endif +#endif } @@ -1156,14 +1168,18 @@ - (void)_handleBuffer:(id)buffer context:(NSNumber *)context return; } + NSArray *requestsArray = [RCTConvert NSArray:buffer]; + +#if RCT_DEBUG + if (![buffer isKindOfClass:[NSArray class]]) { RCTLogError(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class])); return; } - NSArray *requestsArray = (NSArray *)buffer; NSUInteger bufferRowCount = [requestsArray count]; NSUInteger expectedFieldsCount = RCTBridgeFieldResponseReturnValues + 1; + if (bufferRowCount != expectedFieldsCount) { RCTLogError(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount); return; @@ -1177,13 +1193,15 @@ - (void)_handleBuffer:(id)buffer context:(NSNumber *)context } } +#endif + NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs]; NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs]; NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss]; NSUInteger numRequests = [moduleIDs count]; - BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count]; - if (!allSame) { + + if (RCT_DEBUG && (numRequests != methodIDs.count || numRequests != paramsArrays.count)) { RCTLogError(@"Invalid data message - all must be length: %zd", numRequests); return; } @@ -1217,22 +1235,25 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i params:(NSArray *)params context:(NSNumber *)context { - if (![params isKindOfClass:[NSArray class]]) { + + if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) { RCTLogError(@"Invalid module/method/params tuple for request #%zd", i); return NO; } // Look up method NSArray *methods = RCTExportedMethodsByModuleID()[moduleID]; - if (methodID >= methods.count) { + + if (RCT_DEBUG && methodID >= methods.count) { RCTLogError(@"Unknown methodID: %zd for module: %zd (%@)", methodID, moduleID, RCTModuleNamesByID[moduleID]); return NO; } + RCTModuleMethod *method = methods[methodID]; // Look up module id module = self->_modulesByID[moduleID]; - if (!module) { + if (RCT_DEBUG && !module) { RCTLogError(@"No module found for name '%@'", RCTModuleNamesByID[moduleID]); return NO; } @@ -1249,13 +1270,17 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i return; } - @try { + if (!RCT_DEBUG) { [method invokeWithBridge:strongSelf module:module arguments:params context:context]; - } - @catch (NSException *exception) { - RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception); - if ([exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) { - @throw; + } else { + @try { + [method invokeWithBridge:strongSelf module:module arguments:params context:context]; + } + @catch (NSException *exception) { + RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception); + if ([exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) { + @throw; + } } } diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index e664f06e0863e1..59ba79d271795b 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -133,9 +133,11 @@ RCT_EXTERN BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json); RCT_EXTERN BOOL RCTCopyProperty(id target, id source, NSString *keyPath); /** - * Underlying implementation of RCT_ENUM_CONVERTER macro. Ignore this. + * Underlying implementations of RCT_XXX_CONVERTER macros. Ignore these. */ RCT_EXTERN NSNumber *RCTConvertEnumValue(const char *, NSDictionary *, NSNumber *, id); +RCT_EXTERN NSArray *RCTConvertArrayValue(SEL, id); +RCT_EXTERN void RCTLogConvertError(id, const char *); /** * This macro is used for creating simple converter functions that just call @@ -150,18 +152,19 @@ RCT_CUSTOM_CONVERTER(type, name, [json getter]) #define RCT_CUSTOM_CONVERTER(type, name, code) \ + (type)name:(id)json \ { \ - if (json == [NSNull null]) { \ - json = nil; \ - } \ - @try { \ + json = (json == (id)kCFNull) ? nil : json; \ + if (!RCT_DEBUG) { \ return code; \ + } else { \ + @try { \ + return code; \ + } \ + @catch (__unused NSException *e) { \ + RCTLogConvertError(json, #type); \ + json = nil; \ + return code; \ + } \ } \ - @catch (__unused NSException *e) { \ - RCTLogError(@"JSON value '%@' of type '%@' cannot be converted to '%s'", \ - json, [json classForCoder], #type); \ - json = nil; \ - return code; \ - } \ } /** @@ -190,15 +193,8 @@ RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter]) /** * This macro is used for creating converter functions for typed arrays. */ -#define RCT_ARRAY_CONVERTER(type) \ -+ (type##Array *)type##Array:(id)json \ -{ \ - NSMutableArray *values = [[NSMutableArray alloc] init]; \ - for (id jsonValue in [self NSArray:json]) { \ - id value = [self type:jsonValue]; \ - if (value) { \ - [values addObject:value]; \ - } \ - } \ - return values; \ +#define RCT_ARRAY_CONVERTER(type) \ ++ (NSArray *)type##Array:(id)json \ +{ \ + return RCTConvertArrayValue(@selector(type:), json); \ } diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 2aefe89405e96e..f1ed77298dafdb 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -11,8 +11,16 @@ #import +#import "RCTDefines.h" + @implementation RCTConvert +void RCTLogConvertError(id json, const char *type) +{ + RCTLogError(@"JSON value '%@' of type '%@' cannot be converted to %s", + json, [json classForCoder], type); +} + RCT_CONVERTER(BOOL, BOOL, boolValue) RCT_NUMBER_CONVERTER(double, doubleValue) RCT_NUMBER_CONVERTER(float, floatValue) @@ -228,57 +236,52 @@ + (NSDate *)NSDate:(id)json }), UIBarStyleDefault, integerValue) // TODO: normalise the use of w/width so we can do away with the alias values (#6566645) +static void RCTConvertCGStructValue(const char *type, NSArray *fields, NSDictionary *aliases, CGFloat *result, id json) +{ + NSUInteger count = fields.count; + if ([json isKindOfClass:[NSArray class]]) { + if (RCT_DEBUG && [json count] != count) { + RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); + } else { + for (NSUInteger i = 0; i < count; i++) { + result[i] = [RCTConvert CGFloat:json[i]]; + } + } + } else if ([json isKindOfClass:[NSDictionary class]]) { + if (aliases.count) { + json = [json mutableCopy]; + for (NSString *alias in aliases) { + NSString *key = aliases[alias]; + NSNumber *number = json[alias]; + if (number) { + RCTLogWarn(@"Using deprecated '%@' property for '%s'. Use '%@' instead.", alias, type, key); + ((NSMutableDictionary *)json)[key] = number; + } + } + } + for (NSUInteger i = 0; i < count; i++) { + result[i] = [RCTConvert CGFloat:json[fields[i]]]; + } + } else if (RCT_DEBUG && json && json != (id)kCFNull) { + RCTLogConvertError(json, type); + } +} + /** * This macro is used for creating converter functions for structs that consist * of a number of CGFloat properties, such as CGPoint, CGRect, etc. */ -#define RCT_CGSTRUCT_CONVERTER(type, values, _aliases) \ -+ (type)type:(id)json \ -{ \ - @try { \ - static NSArray *fields; \ - static NSUInteger count; \ - static dispatch_once_t onceToken; \ - dispatch_once(&onceToken, ^{ \ - fields = values; \ - count = [fields count]; \ - }); \ - type result; \ - if ([json isKindOfClass:[NSArray class]]) { \ - if ([json count] != count) { \ - RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \ - } else { \ - for (NSUInteger i = 0; i < count; i++) { \ - ((CGFloat *)&result)[i] = [self CGFloat:json[i]]; \ - } \ - } \ - } else if ([json isKindOfClass:[NSDictionary class]]) { \ - NSDictionary *aliases = _aliases; \ - if (aliases.count) { \ - json = [json mutableCopy]; \ - for (NSString *alias in aliases) { \ - NSString *key = aliases[alias]; \ - NSNumber *number = json[alias]; \ - if (number) { \ - RCTLogWarn(@"Using deprecated '%@' property for '%s'. Use '%@' instead.", alias, #type, key); \ - ((NSMutableDictionary *)json)[key] = number; \ - } \ - } \ - } \ - for (NSUInteger i = 0; i < count; i++) { \ - ((CGFloat *)&result)[i] = [self CGFloat:json[fields[i]]]; \ - } \ - } else if (json && json != [NSNull null]) { \ - RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", \ - #type, [json classForCoder], json); \ - } \ - return result; \ - } \ - @catch (__unused NSException *e) { \ - RCTLogError(@"JSON value '%@' cannot be converted to '%s'", json, #type); \ - type result; \ - return result; \ - } \ +#define RCT_CGSTRUCT_CONVERTER(type, values, aliases) \ ++ (type)type:(id)json \ +{ \ + static NSArray *fields; \ + static dispatch_once_t onceToken; \ + dispatch_once(&onceToken, ^{ \ + fields = values; \ + }); \ + type result; \ + RCTConvertCGStructValue(#type, fields, aliases, (CGFloat *)&result, json); \ + return result; \ } RCT_CUSTOM_CONVERTER(CGFloat, CGFloat, [self double:json]) @@ -525,9 +528,7 @@ + (UIColor *)UIColor:(id)json } else if ([json isKindOfClass:[NSArray class]]) { if ([json count] < 3 || [json count] > 4) { - RCTLogError(@"Expected array with count 3 or 4, but count is %zd: %@", [json count], json); - } else { // Color array @@ -545,10 +546,9 @@ + (UIColor *)UIColor:(id)json blue:[self double:json[@"b"]] alpha:[self double:json[@"a"] ?: @1]]; - } else if (json && ![json isKindOfClass:[NSNull class]]) { - - RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, received %@: %@", - [json classForCoder], json); + } + else if (RCT_DEBUG && json && json != (id)kCFNull) { + RCTLogConvertError(json, "a color"); } // Default color @@ -573,8 +573,12 @@ + (UIImage *)UIImage:(id)json // TODO: we might as well cache the result of these checks (and possibly the // image itself) so as to reduce overhead on subsequent checks of the same input - if (![json isKindOfClass:[NSString class]]) { - RCTLogError(@"Expected NSString for UIImage, received %@: %@", [json classForCoder], json); + if (!json || json == (id)kCFNull) { + return nil; + } + + if (RCT_DEBUG && ![json isKindOfClass:[NSString class]]) { + RCTLogConvertError(json, "an image"); return nil; } @@ -761,6 +765,29 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family return bestMatch; } +NSArray *RCTConvertArrayValue(SEL type, id json) +{ + __block BOOL copy = NO; + __block NSArray *values = json = [RCTConvert NSArray:json]; + [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, BOOL *stop) { + id value = ((id(*)(Class, SEL, id))objc_msgSend)([RCTConvert class], type, jsonValue); + if (copy) { + if (value) { + [(NSMutableArray *)values addObject:value]; + } + } else if (value != jsonValue) { + // Converted value is different, so we'll need to copy the array + values = [[NSMutableArray alloc] initWithCapacity:values.count]; + for (NSInteger i = 0; i < idx; i++) { + [(NSMutableArray *)values addObject:json[i]]; + } + [(NSMutableArray *)values addObject:value]; + copy = YES; + } + }]; + return values; +} + RCT_ARRAY_CONVERTER(NSString) RCT_ARRAY_CONVERTER(NSDictionary) RCT_ARRAY_CONVERTER(NSURL) diff --git a/React/Base/RCTLog.h b/React/Base/RCTLog.h index 7fa25e6a88ce42..75cbe722e09b43 100644 --- a/React/Base/RCTLog.h +++ b/React/Base/RCTLog.h @@ -51,7 +51,7 @@ RCT_EXTERN NSString *RCTThreadName(NSThread *); * A method to generate a string from a collection of log data. To omit any * particular data from the log, just pass nil or zero for the argument. */ -NSString *RCTFormatLog( +RCT_EXTERN NSString *RCTFormatLog( NSDate *timestamp, NSThread *thread, RCTLogLevel level, diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index 6ca2d4eb8ecb31..fb70fe6d3053df 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -184,7 +184,7 @@ void _RCTLogFormat( level, fileName ? @(fileName) : nil, (lineNumber >= 0) ? @(lineNumber) : nil, message ); - if (RCT_DEBUG) { +#if RCT_DEBUG // Red box is only available in debug mode // Log to red box if (level >= RCTLOG_REDBOX_LEVEL) { @@ -193,6 +193,8 @@ void _RCTLogFormat( // Log to JS executor [RCTBridge logMessage:message level:level ? @(RCTLogLevels[level - 1]) : @"info"]; - } + +#endif + } } diff --git a/React/Base/RCTRedBox.h b/React/Base/RCTRedBox.h index 9a3a9b49a872ee..058759da7a0a5f 100644 --- a/React/Base/RCTRedBox.h +++ b/React/Base/RCTRedBox.h @@ -7,6 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "RCTDefines.h" + +#if RCT_DEBUG // Red box is only available in debug mode + #import @interface RCTRedBox : NSObject @@ -23,3 +27,5 @@ - (void)dismiss; @end + +#endif diff --git a/React/Base/RCTRedBox.m b/React/Base/RCTRedBox.m index 882a87bfe8d736..6268402526315e 100644 --- a/React/Base/RCTRedBox.m +++ b/React/Base/RCTRedBox.m @@ -7,6 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "RCTDefines.h" + +#if RCT_DEBUG // Red box is only available in debug mode + #import "RCTRedBox.h" #import "RCTBridge.h" @@ -282,14 +286,12 @@ - (void)updateErrorMessage:(NSString *)message withStack:(NSArray *)stack - (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow { - if (RCT_DEBUG) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (!_window) { - _window = [[RCTRedBoxWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - } - [_window showErrorMessage:message withStack:stack showIfHidden:shouldShow]; - }); - } + dispatch_async(dispatch_get_main_queue(), ^{ + if (!_window) { + _window = [[RCTRedBoxWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + } + [_window showErrorMessage:message withStack:stack showIfHidden:shouldShow]; + }); } - (NSString *)currentErrorMessage @@ -307,3 +309,5 @@ - (void)dismiss } @end + +#endif diff --git a/React/Executors/RCTWebViewExecutor.h b/React/Executors/RCTWebViewExecutor.h index 77d8a8310715c5..db8710c7d579bf 100644 --- a/React/Executors/RCTWebViewExecutor.h +++ b/React/Executors/RCTWebViewExecutor.h @@ -7,6 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "RCTDefines.h" + +#if RCT_DEV // Debug executors are only supported in dev mode + #import #import "RCTJavaScriptExecutor.h" @@ -40,3 +44,5 @@ - (UIWebView *)invalidateAndReclaimWebView; @end + +#endif diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m index 0bc6fdfc81f6d4..09628850fb3aa8 100644 --- a/React/Executors/RCTWebViewExecutor.m +++ b/React/Executors/RCTWebViewExecutor.m @@ -7,6 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "RCTDefines.h" + +#if RCT_DEV // Debug executors are only supported in dev mode + #import "RCTWebViewExecutor.h" #import @@ -188,9 +192,14 @@ - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete { - RCTAssert(!_objectsToInject[objectName], - @"already injected object named %@", _objectsToInject[objectName]); + if (RCT_DEBUG) { + RCTAssert(!_objectsToInject[objectName], + @"already injected object named %@", _objectsToInject[objectName]); + } _objectsToInject[objectName] = script; onComplete(nil); } + @end + +#endif diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index ece5d06687f10a..878138282f9d1f 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -44,10 +44,11 @@ - (instancetype)init return; } - if (RCT_DEBUG) { - [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; - return; - } +#if RCT_DEBUG // Red box is only available in debug mode + + [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; + +#else static NSUInteger reloadRetries = 0; const NSUInteger maxMessageLength = 75; @@ -76,14 +77,21 @@ - (instancetype)init NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage]; [NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack]; } + +#endif + } RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message stack:(NSArray *)stack) { - if (RCT_DEBUG) { + +#if RCT_DEBUG // Red box is only available in debug mode + [[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack]; - } + +#endif + } @end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 3f3ea75960dcb9..fc688e491cf55d 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -222,6 +222,7 @@ @implementation RCTUIManager return name; } +// TODO: only send name once instead of a dictionary of name and type keyed by name static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewName) { NSMutableDictionary *nativeProps = [[NSMutableDictionary alloc] init]; @@ -234,8 +235,13 @@ @implementation RCTUIManager SEL getInfo = method_getName(method); const char *selName = sel_getName(getInfo); if (strlen(selName) > prefixLength && strncmp(selName, prefix, prefixLength) == 0) { - NSDictionary *info = ((NSDictionary *(*)(id, SEL))method_getImplementation(method))(managerClass, getInfo); - nativeProps[info[@"name"]] = info; + NSString *name = @(selName); + NSRange nameRange = [name rangeOfString:@"_"]; + if (nameRange.length) { + name = [name substringFromIndex:nameRange.location + 1]; + NSString *type = ((NSString *(*)(id, SEL))method_getImplementation(method))(managerClass, getInfo); + nativeProps[name] = @{@"name": name, @"type": type}; + } } } return @{ diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 48d7aee0459b40..f32e86ff1dc8bc 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -106,6 +106,7 @@ 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarItemManager.m; sourceTree = ""; }; 137327E51AA5CF210034F82E /* RCTTabBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarManager.h; sourceTree = ""; }; 137327E61AA5CF210034F82E /* RCTTabBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarManager.m; sourceTree = ""; }; + 1384149E1ADFCA4A003E0667 /* RCTDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = ""; }; 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = ""; }; 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTKeyCommands.m; sourceTree = ""; }; 13B07FC71A68125100A75B9A /* Layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Layout.c; sourceTree = ""; }; @@ -364,6 +365,7 @@ 83CBBA491A601E3B00E9B192 /* Base */ = { isa = PBXGroup; children = ( + 1384149E1ADFCA4A003E0667 /* RCTDefines.h */, 14200DA81AC179B3008EE6BA /* RCTJavaScriptLoader.h */, 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */, 83CBBA4A1A601E3B00E9B192 /* RCTAssert.h */, diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index ed97cf3b2bf3f0..3788832ef5857d 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -130,11 +130,11 @@ RCT_CUSTOM_SHADOW_PROPERTY(name, type, RCTShadowView) { \ * refer to "json", "view" and "defaultView" to implement the required logic. */ #define RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass) \ -+ (NSDictionary *)getPropConfigView_##name { return @{@"name": @#name, @"type": @#type}; } \ ++ (NSString *)getPropConfigView_##name { return @#type; } \ - (void)set_##name:(id)json forView:(viewClass *)view withDefaultView:(viewClass *)defaultView #define RCT_CUSTOM_SHADOW_PROPERTY(name, type, viewClass) \ -+ (NSDictionary *)getPropConfigShadow_##name { return @{@"name": @#name, @"type": @#type}; } \ ++ (NSString *)getPropConfigShadow_##name { return @#type; } \ - (void)set_##name:(id)json forShadowView:(viewClass *)view withDefaultView:(viewClass *)defaultView /** From 2294da777cf08f899dcc95256056e2c1958b8a88 Mon Sep 17 00:00:00 2001 From: Jiajie Zhu Date: Tue, 21 Apr 2015 10:19:18 -0700 Subject: [PATCH 038/250] [catlyst|madman] make map fire onRegionChange for user zoom in/out again --- React/Views/RCTMapManager.m | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m index 8de35141586a29..f9a6b9175ccbf0 100644 --- a/React/Views/RCTMapManager.m +++ b/React/Views/RCTMapManager.m @@ -67,15 +67,13 @@ - (void)mapView:(RCTMap *)mapView regionWillChangeAnimated:(BOOL)animated { [self _regionChanged:mapView]; - if (animated) { - mapView.regionChangeObserveTimer = [NSTimer timerWithTimeInterval:RCTMapRegionChangeObserveInterval - target:self - selector:@selector(_onTick:) - userInfo:@{ RCTMapViewKey: mapView } - repeats:YES]; - - [[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer forMode:NSRunLoopCommonModes]; - } + mapView.regionChangeObserveTimer = [NSTimer timerWithTimeInterval:RCTMapRegionChangeObserveInterval + target:self + selector:@selector(_onTick:) + userInfo:@{ RCTMapViewKey: mapView } + repeats:YES]; + + [[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer forMode:NSRunLoopCommonModes]; } - (void)mapView:(RCTMap *)mapView regionDidChangeAnimated:(BOOL)animated From 77d908b975a652f8f7e657e9afc801892cc6a060 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 21 Apr 2015 10:57:53 -0700 Subject: [PATCH 039/250] [react-packager] Allow json files as modules --- .../DependencyResolver/ModuleDescriptor.js | 2 + .../__tests__/DependencyGraph-test.js | 40 +++++++++++++++++++ .../haste/DependencyGraph/index.js | 13 +++++- .../src/Packager/__tests__/Packager-test.js | 18 ++++++++- packager/react-packager/src/Packager/index.js | 15 +++++++ packager/react-packager/src/Server/index.js | 2 +- 6 files changed, 86 insertions(+), 4 deletions(-) diff --git a/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js b/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js index 3cdfa1c6a6e2bb..90db1c4ade5f54 100644 --- a/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js +++ b/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js @@ -45,6 +45,8 @@ function ModuleDescriptor(fields) { this.altId = fields.altId; + this.isJSON = fields.isJSON; + this._fields = fields; } diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js index d3f6ce289eb590..cda717e875ac98 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js @@ -101,6 +101,46 @@ describe('DependencyGraph', function() { }); }); + pit('should get json dependencies', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'package.json': JSON.stringify({ + name: 'package' + }), + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("./a.json")' + ].join('\n'), + 'a.json': JSON.stringify({}), + } + }); + + var dgraph = new DependencyGraph({ + roots: [root], + fileWatcher: fileWatcher + }); + return dgraph.load().then(function() { + expect(dgraph.getOrderedDependencies('/root/index.js')) + .toEqual([ + { + id: 'index', + altId: 'package/index', + path: '/root/index.js', + dependencies: ['./a.json'] + }, + { + id: 'package/a.json', + isJSON: true, + path: '/root/a.json', + dependencies: [] + }, + ]); + }); + }); + pit('should get dependencies with deprecated assets', function() { var root = '/root'; fs.__setMockFilesystem({ diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js index 66534597750f01..6afb5f25c879e9 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js @@ -66,7 +66,7 @@ function DependecyGraph(options) { this._debugUpdateEvents = []; this._moduleExtPattern = new RegExp( - '\.(' + ['js'].concat(this._assetExts).join('|') + ')$' + '\.(' + ['js', 'json'].concat(this._assetExts).join('|') + ')$' ); // Kick off the search process to precompute the dependency graph. @@ -259,7 +259,7 @@ DependecyGraph.prototype.resolveDependency = function( } // JS modules can be required without extensios. - if (!this._isFileAsset(modulePath)) { + if (!this._isFileAsset(modulePath) && !modulePath.match(/\.json$/)) { modulePath = withExtJs(modulePath); } @@ -432,6 +432,15 @@ DependecyGraph.prototype._processModule = function(modulePath) { return Promise.resolve(module); } + if (extname(modulePath) === 'json') { + moduleData.id = this._lookupName(modulePath); + moduleData.isJSON = true; + moduleData.dependencies = []; + module = new ModuleDescriptor(moduleData); + this._updateGraphWithModule(module); + return Promise.resolve(module); + } + var self = this; return readFile(modulePath, 'utf8') .then(function(content) { diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index c177321651bc67..763e6dd6d17b9a 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -43,6 +43,10 @@ describe('Packager', function() { }; }); + require('fs').readFile.mockImpl(function(file, callback) { + callback(null, '{"json":true}'); + }); + var packager = new Packager({projectRoots: ['/root']}); var modules = [ {id: 'foo', path: '/root/foo.js', dependencies: []}, @@ -60,7 +64,13 @@ describe('Packager', function() { isAsset: true, resolution: 2, dependencies: [] - } + }, + { + id: 'package/file.json', + path: '/root/file.json', + isJSON: true, + dependencies: [], + }, ]; getDependencies.mockImpl(function() { @@ -137,6 +147,12 @@ describe('Packager', function() { '/root/img/new_image.png' ]); + expect(p.addModule.mock.calls[4]).toEqual([ + 'lol module.exports = {"json":true}; lol', + 'module.exports = {"json":true};', + '/root/file.json' + ]); + expect(p.finalize.mock.calls[0]).toEqual([ {runMainModule: true} ]); diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index aab55c0842a062..c651dde78b8576 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -21,6 +21,7 @@ var declareOpts = require('../lib/declareOpts'); var imageSize = require('image-size'); var sizeOf = Promise.promisify(imageSize); +var readFile = Promise.promisify(fs.readFile); var validateOpts = declareOpts({ projectRoots: { @@ -147,6 +148,8 @@ Packager.prototype._transformModule = function(ppackage, module) { transform = this.generateAssetModule_DEPRECATED(ppackage, module); } else if (module.isAsset) { transform = this.generateAssetModule(ppackage, module); + } else if (module.isJSON) { + transform = generateJSONModule(module); } else { transform = this._transformer.loadFileAndTransform( path.resolve(module.path) @@ -206,6 +209,18 @@ Packager.prototype.generateAssetModule = function(ppackage, module) { var code = 'module.exports = ' + JSON.stringify(img) + ';'; + return { + code: code, + sourceCode: code, + sourcePath: module.path, + }; + }); +}; + +function generateJSONModule(module) { + return readFile(module.path).then(function(data) { + var code = 'module.exports = ' + data.toString('utf8') + ';'; + return { code: code, sourceCode: code, diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index e0bfeb2f339d63..525636cb532de4 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -80,7 +80,7 @@ function Server(options) { dir: dir, globs: [ '**/*.js', - '**/package.json', + '**/*.json', ].concat(assetGlobs), }; }); From f1351a4040776e60c927cfa23a2a965a312b5ba4 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Tue, 21 Apr 2015 11:05:37 -0700 Subject: [PATCH 040/250] [react_native] JS files from D2009158: [react_native] Update other stacktrace-parser From c46c4a0ad4261ab3bd94568bafa43bda0475281a Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 21 Apr 2015 10:59:29 -0700 Subject: [PATCH 041/250] [react-packager] bump watchman watch timeout to 10 seconds --- packager/react-packager/src/FileWatcher/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/react-packager/src/FileWatcher/index.js b/packager/react-packager/src/FileWatcher/index.js index b629dfbf01f3c6..38ad19bf952675 100644 --- a/packager/react-packager/src/FileWatcher/index.js +++ b/packager/react-packager/src/FileWatcher/index.js @@ -26,7 +26,7 @@ var detectingWatcherClass = new Promise(function(resolve) { module.exports = FileWatcher; -var MAX_WAIT_TIME = 3000; +var MAX_WAIT_TIME = 10000; // Singleton var fileWatcher = null; From 173615ae266f70d31088c5486fb5f810d1524e93 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 21 Apr 2015 10:58:20 -0700 Subject: [PATCH 042/250] [react-packager] Add jpe?g to asset extensions --- packager/packager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packager/packager.js b/packager/packager.js index 23da3a78c62ca7..212e17f715e547 100644 --- a/packager/packager.js +++ b/packager/packager.js @@ -200,6 +200,7 @@ function getAppMiddleware(options) { cacheVersion: '2', transformModulePath: require.resolve('./transformer.js'), assetRoots: options.assetRoots, + assetExts: ['png', 'jpeg', 'jpg'] }); } From 5fb5148e3d8c0bc045692b7f8ce1ee75f2253544 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Tue, 21 Apr 2015 10:50:10 -0700 Subject: [PATCH 043/250] [SliderIOS] Apply value after minimum/maximumValue in order to ensure it is properly set Summary: `value` is clamped between min/max and so order of prop application matters - `value` always ended up being set first in my tests, and consequently a value outside of the default range 0-1 would not work. So this applies the value when the min/max are set. [Gist of broken example](https://gist.github.com/brentvatne/fc637b3e21d012966f3a) ![screenshot](http://url.brentvatne.ca/SQPC.png) ^ the second slider here should have it's cursor in the middle /cc @tadeuzagallo Closes https://github.com/facebook/react-native/pull/835 Github Author: Brent Vatne Test Plan: Imported from GitHub, without a `Test Plan:` line. --- React/Views/RCTSlider.h | 14 ++++++++++++++ React/Views/RCTSlider.m | 35 ++++++++++++++++++++++++++++++++++ React/Views/RCTSliderManager.m | 20 +++++++++---------- 3 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 React/Views/RCTSlider.h create mode 100644 React/Views/RCTSlider.m diff --git a/React/Views/RCTSlider.h b/React/Views/RCTSlider.h new file mode 100644 index 00000000000000..916419a29d3c90 --- /dev/null +++ b/React/Views/RCTSlider.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface RCTSlider : UISlider + +@end diff --git a/React/Views/RCTSlider.m b/React/Views/RCTSlider.m new file mode 100644 index 00000000000000..04e8d841e75f6f --- /dev/null +++ b/React/Views/RCTSlider.m @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTSlider.h" + +@implementation RCTSlider +{ + float _unclippedValue; +} + +- (void)setValue:(float)value +{ + _unclippedValue = value; + super.value = value; +} + +- (void)setMinimumValue:(float)minimumValue +{ + super.minimumValue = minimumValue; + super.value = _unclippedValue; +} + +- (void)setMaximumValue:(float)maximumValue +{ + super.maximumValue = maximumValue; + super.value = _unclippedValue; +} + +@end diff --git a/React/Views/RCTSliderManager.m b/React/Views/RCTSliderManager.m index 58b763b9244a4f..a103da98d7f46a 100644 --- a/React/Views/RCTSliderManager.m +++ b/React/Views/RCTSliderManager.m @@ -11,6 +11,7 @@ #import "RCTBridge.h" #import "RCTEventDispatcher.h" +#import "RCTSlider.h" #import "UIView+React.h" @implementation RCTSliderManager @@ -19,32 +20,31 @@ @implementation RCTSliderManager - (UIView *)view { - UISlider *slider = [[UISlider alloc] init]; + RCTSlider *slider = [[RCTSlider alloc] init]; [slider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; [slider addTarget:self action:@selector(sliderTouchEnd:) forControlEvents:UIControlEventTouchUpInside]; return slider; } -- (void)sliderValueChanged:(UISlider *)sender +static void RCTSendSliderEvent(RCTSliderManager *self, UISlider *sender, BOOL continuous) { NSDictionary *event = @{ @"target": sender.reactTag, @"value": @(sender.value), - @"continuous": @YES, + @"continuous": @(continuous), }; [self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event]; } -- (void)sliderTouchEnd:(UISlider *)sender +- (void)sliderValueChanged:(UISlider *)sender { - NSDictionary *event = @{ - @"target": sender.reactTag, - @"value": @(sender.value), - @"continuous": @NO, - }; + RCTSendSliderEvent(self, sender, YES); +} - [self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event]; +- (void)sliderTouchEnd:(UISlider *)sender +{ + RCTSendSliderEvent(self, sender, NO); } RCT_EXPORT_VIEW_PROPERTY(value, float); From 45c10ffc538de36a1508572de869c7b7e5bd01e5 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Tue, 21 Apr 2015 11:41:34 -0700 Subject: [PATCH 044/250] [ReactNative] Navigator touch grant bug from D2001635 --- Libraries/CustomComponents/Navigator/Navigator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 7a030b45580ea4..2d7a4a68f89fa7 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -640,7 +640,7 @@ var Navigator = React.createClass({ this.state.fromIndex = this.state.presentedIndex; var gestureSceneDelta = this._deltaForGestureAction(this._activeGestureAction); this.state.toIndex = this.state.presentedIndex + gestureSceneDelta; - this.onAnimationStart(); + this._onAnimationStart(); } }, From c0c2d4ca00cdbca93dacd5eea8a8dc07ef9dbae9 Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Tue, 21 Apr 2015 11:54:14 -0700 Subject: [PATCH 045/250] [react_native] JS files from D2009265: Fix resizeMode for images --- Libraries/Image/ImageResizeMode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/Image/ImageResizeMode.js b/Libraries/Image/ImageResizeMode.js index 9a7a2f9541f9ff..b947b95258f38c 100644 --- a/Libraries/Image/ImageResizeMode.js +++ b/Libraries/Image/ImageResizeMode.js @@ -30,8 +30,8 @@ var ImageResizeMode = keyMirror({ cover: null, /** * stretch - The image will be stretched to fill the entire frame of the - * view without clipping. This may change the aspect ratio of the image, - * distoring it. Only supported on iOS. + * view without clipping. This may change the aspect ratio of the image, + * distoring it. */ stretch: null, }); From c6ad7b85d1c67dd85ebc659f6560d061e1ade2d2 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Tue, 21 Apr 2015 10:48:54 -0700 Subject: [PATCH 046/250] [ReactNative] Use network image for new image assets --- Libraries/Image/Image.ios.js | 20 +++-- .../__tests__/resolveAssetSource-test.js | 76 +++++++++++++++++++ Libraries/Image/resolveAssetSource.js | 66 ++++++++++++++++ 3 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 Libraries/Image/__tests__/resolveAssetSource-test.js create mode 100644 Libraries/Image/resolveAssetSource.js diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index d519c1ac5308e1..be95a3f3ff91f0 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -12,11 +12,11 @@ 'use strict'; var EdgeInsetsPropType = require('EdgeInsetsPropType'); +var ImageResizeMode = require('ImageResizeMode'); +var ImageStylePropTypes = require('ImageStylePropTypes'); var NativeMethodsMixin = require('NativeMethodsMixin'); var NativeModules = require('NativeModules'); var PropTypes = require('ReactPropTypes'); -var ImageResizeMode = require('ImageResizeMode'); -var ImageStylePropTypes = require('ImageStylePropTypes'); var React = require('React'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StyleSheet = require('StyleSheet'); @@ -26,8 +26,9 @@ var flattenStyle = require('flattenStyle'); var invariant = require('invariant'); var merge = require('merge'); var requireNativeComponent = require('requireNativeComponent'); -var warning = require('warning'); +var resolveAssetSource = require('resolveAssetSource'); var verifyPropTypes = require('verifyPropTypes'); +var warning = require('warning'); /** * A react component for displaying different types of images, @@ -122,10 +123,15 @@ var Image = React.createClass({ 'not be set directly on Image.'); } } - var style = flattenStyle([styles.base, this.props.style]); - invariant(style, "style must be initialized"); var source = this.props.source; - invariant(source, "source must be initialized"); + invariant(source, 'source must be initialized'); + + var {width, height} = source; + var style = flattenStyle([{width, height}, styles.base, this.props.style]); + invariant(style, 'style must be initialized'); + + source = resolveAssetSource(source); + var isNetwork = source.uri && source.uri.match(/^https?:/); invariant( !(isNetwork && source.isStatic), @@ -171,8 +177,8 @@ var styles = StyleSheet.create({ }, }); -var RCTStaticImage = requireNativeComponent('RCTStaticImage', null); var RCTNetworkImage = requireNativeComponent('RCTNetworkImageView', null); +var RCTStaticImage = requireNativeComponent('RCTStaticImage', null); var nativeOnlyProps = { src: true, diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js new file mode 100644 index 00000000000000..69bcb116a917cf --- /dev/null +++ b/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +jest.dontMock('../resolveAssetSource'); + +var resolveAssetSource; +var SourceCode; + +describe('resolveAssetSource', () => { + beforeEach(() => { + jest.resetModuleRegistry(); + SourceCode = require('NativeModules').SourceCode; + resolveAssetSource = require('../resolveAssetSource'); + }); + + it('returns same source for simple static and network images', () => { + var source1 = {uri: 'https://www.facebook.com/logo'}; + expect(resolveAssetSource(source1)).toBe(source1); + + var source2 = {isStatic: true, uri: 'logo'}; + expect(resolveAssetSource(source2)).toBe(source2); + }); + + describe('bundle was loaded from network', () => { + beforeEach(() => { + SourceCode.scriptURL = 'http://10.0.0.1:8081/main.bundle'; + }); + + it('uses network image', () => { + var source = { + path: '/Users/react/project/logo.png', + uri: 'assets/logo.png', + }; + expect(resolveAssetSource(source)).toEqual({ + isStatic: false, + uri: 'http://10.0.0.1:8081/assets/logo.png', + }); + }); + + it('does not change deprecated assets', () => { + // Deprecated require('image!logo') should stay unchanged + var source = { + path: '/Users/react/project/logo.png', + uri: 'logo', + deprecated: true, + }; + expect(resolveAssetSource(source)).toEqual({ + isStatic: true, + uri: 'logo', + }); + }); + }); + + describe('bundle was loaded from file', () => { + it('uses pre-packed image', () => { + SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle'; + + var source = { + path: '/Users/react/project/logo.png', + uri: 'assets/logo.png', + }; + expect(resolveAssetSource(source)).toEqual({ + isStatic: true, + uri: 'assets/logo.png', + }); + }); + }); + +}); diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js new file mode 100644 index 00000000000000..137f92f1c17c7d --- /dev/null +++ b/Libraries/Image/resolveAssetSource.js @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule resolveAssetSource + */ +'use strict'; + +var SourceCode = require('NativeModules').SourceCode; + +var _serverURL; + +function getServerURL() { + if (_serverURL === undefined) { + var scriptURL = SourceCode.scriptURL; + var serverURLMatch = scriptURL && scriptURL.match(/^https?:\/\/.*?\//); + if (serverURLMatch) { + _serverURL = serverURLMatch[0]; + } else { + _serverURL = null; + } + } + + return _serverURL; +} + +// TODO(frantic): +// * Use something other than `path`/`isStatic` for asset identification, `__packager_asset`? +// * Add cache invalidating hashsum +// * Move code that selects scale to client +function resolveAssetSource(source) { + if (source.deprecated) { + return { + ...source, + path: undefined, + isStatic: true, + deprecated: undefined, + }; + } + + var serverURL = getServerURL(); + if (source.path) { + if (serverURL) { + return { + ...source, + path: undefined, + uri: serverURL + source.uri, + isStatic: false, + }; + } else { + return { + ...source, + path: undefined, + isStatic: true, + }; + } + } + + return source; +} + +module.exports = resolveAssetSource; From 40eeaf5b37daeeacd0d612e086426c32de23967e Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Tue, 21 Apr 2015 13:31:20 -0700 Subject: [PATCH 047/250] [ReactNative] Navigator contextual popToRoute and imperitive vs contextual docs --- .../CustomComponents/Navigator/Navigator.js | 84 ++++++++++++++----- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 2d7a4a68f89fa7..f4fcdd443776fd 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -135,11 +135,12 @@ var GESTURE_ACTIONS = [ * /> * ``` * - * ### Navigation Methods + * ### Navigator Methods * - * `Navigator` can be told to navigate in two ways. If you have a ref to - * the element, you can invoke several methods on it to trigger navigation: + * If you have a ref to the Navigator element, you can invoke several methods + * on it to trigger navigation: * + * - `getCurrentRoutes()` - returns the current list of routes * - `jumpBack()` - Jump backward without unmounting the current scene * - `jumpForward()` - Jump forward to the next scene in the route stack * - `jumpTo(route)` - Transition to an existing scene without unmounting @@ -156,18 +157,39 @@ var GESTURE_ACTIONS = [ * - `popToTop()` - Pop to the first scene in the stack, unmounting every * other scene * - * ### Navigator Object + * ### Navigation Context * - * The navigator object is made available to scenes through the `renderScene` - * function. The object has all of the navigation methods on it, as well as a - * few utilities: + * The navigator context object is made available to scenes through the + * `renderScene` function. Alternatively, any scene or component inside a + * Navigator can get the navigation context by calling + * `Navigator.getContext(this)`. * - * - `parentNavigator` - a refrence to the parent navigator object that was - * passed in through props.navigator - * - `onWillFocus` - used to pass a navigation focus event up to the parent - * navigator - * - `onDidFocus` - used to pass a navigation focus event up to the parent - * navigator + * Unlike the Navigator methods, the functions in navigation context do not + * directly control a specific navigator. Instead, the navigator context allows + * a scene to request navigation from its parents. Navigation requests will + * travel up through the hierarchy of Navigators, and will be resolved by the + * deepest active navigator. + * + * Navigation context objects contain the following: + * + * - `getCurrentRoutes()` - returns the routes for the closest navigator + * - `jumpBack()` - Jump backward without unmounting the current scene + * - `jumpForward()` - Jump forward to the next scene in the route stack + * - `jumpTo(route)` - Transition to an existing scene without unmounting + * - `parentNavigator` - a refrence to the parent navigation context + * - `push(route)` - Navigate forward to a new scene, squashing any scenes + * that you could `jumpForward` to + * - `pop()` - Transition back and unmount the current scene + * - `replace(route)` - Replace the current scene with a new route + * - `replaceAtIndex(route, index)` - Replace a scene as specified by an index + * - `replacePrevious(route)` - Replace the previous scene + * - `route` - The route that was used to render the scene with this context + * - `immediatelyResetRouteStack(routeStack)` - Reset every scene with an + * array of routes + * - `popToRoute(route)` - Pop to a particular scene, as specified by it's + * route. All scenes after it will be unmounted + * - `popToTop()` - Pop to the first scene in the stack, unmounting every + * other scene * */ var Navigator = React.createClass({ @@ -306,25 +328,30 @@ var Navigator = React.createClass({ this.parentNavigator = getNavigatorContext(this) || this.props.navigator; this._subRouteFocus = []; this.navigatorContext = { + // Actions for child navigators or interceptors: setHandlerForRoute: this.setHandlerForRoute, request: this.request, + // Contextual utilities parentNavigator: this.parentNavigator, getCurrentRoutes: this.getCurrentRoutes, + // `route` is injected by NavigatorStaticContextContainer + + // Contextual nav actions + pop: this.requestPop, + popToRoute: this.requestPopTo, - // Legacy, imperitive nav actions. Use request when possible. + // Legacy, imperitive nav actions. Will transition these to contextual actions jumpBack: this.jumpBack, jumpForward: this.jumpForward, jumpTo: this.jumpTo, push: this.push, - pop: this.pop, replace: this.replace, replaceAtIndex: this.replaceAtIndex, replacePrevious: this.replacePrevious, replacePreviousAndPop: this.replacePreviousAndPop, immediatelyResetRouteStack: this.immediatelyResetRouteStack, resetTo: this.resetTo, - popToRoute: this.popToRoute, popToTop: this.popToTop, }; this._handlers = {}; @@ -349,6 +376,14 @@ var Navigator = React.createClass({ return this._handleRequest.apply(null, arguments); }, + requestPop: function() { + return this.request('pop'); + }, + + requestPopTo: function(route) { + return this.request('pop', route); + }, + _handleRequest: function(action, arg1, arg2) { var childHandler = this._handlers[this.state.presentedIndex]; if (childHandler && childHandler(action, arg1, arg2)) { @@ -356,7 +391,7 @@ var Navigator = React.createClass({ } switch (action) { case 'pop': - return this._handlePop(); + return this._handlePop(arg1); case 'push': return this._handlePush(arg1); default: @@ -365,11 +400,20 @@ var Navigator = React.createClass({ } }, - _handlePop: function() { + _handlePop: function(route) { + if (route) { + var hasRoute = this.state.routeStack.indexOf(route) !== -1; + if (hasRoute) { + this.popToRoute(route); + return true; + } else { + return false; + } + } if (this.state.presentedIndex === 0) { return false; } - this._popN(1); + this.pop(); return true; }, @@ -904,7 +948,7 @@ var Navigator = React.createClass({ }, pop: function() { - return this.request('pop'); + this._popN(1); }, /** From 32084c90a22846efed9c92f0b89c9c8addb3dfa4 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 21 Apr 2015 13:25:19 -0700 Subject: [PATCH 048/250] Added missing RCTDefines.h to React lib --- React/React.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index f32e86ff1dc8bc..38c207459e3ac0 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -109,6 +109,7 @@ 1384149E1ADFCA4A003E0667 /* RCTDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = ""; }; 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = ""; }; 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTKeyCommands.m; sourceTree = ""; }; + 13AF1F851AE6E777005F5298 /* RCTDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = ""; }; 13B07FC71A68125100A75B9A /* Layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Layout.c; sourceTree = ""; }; 13B07FC81A68125100A75B9A /* Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Layout.h; sourceTree = ""; }; 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAlertManager.h; sourceTree = ""; }; @@ -377,6 +378,7 @@ 830BA4541A8E3BDA00D53203 /* RCTCache.m */, 83CBBACA1A6023D300E9B192 /* RCTConvert.h */, 83CBBACB1A6023D300E9B192 /* RCTConvert.m */, + 13AF1F851AE6E777005F5298 /* RCTDefines.h */, 83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */, 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */, 83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */, From 2ee7ebae1fc42b83f7c57e5a93f77e01a0d0b2e3 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 21 Apr 2015 21:08:20 -0100 Subject: [PATCH 049/250] Fixed broken font weight on iPhone 5 --- .../RCTWebSocketDebugger.xcodeproj/project.pbxproj | 12 ++++++------ React/Base/RCTConvert.h | 5 +---- React/React.xcodeproj/project.pbxproj | 8 ++++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj b/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj index 4a9b599165984e..d9e3b9f5b695ae 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 00D277161AB8C32C00DC1E48 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D277151AB8C32C00DC1E48 /* RCTWebSocketExecutor.m */; }; - 00D277191AB8C35800DC1E48 /* RCT_SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D277181AB8C35800DC1E48 /* RCT_SRWebSocket.m */; }; + 13AF20421AE707C5005F5298 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AF20411AE707C5005F5298 /* SRWebSocket.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -26,8 +26,8 @@ /* Begin PBXFileReference section */ 00D277141AB8C32C00DC1E48 /* RCTWebSocketExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketExecutor.h; sourceTree = ""; }; 00D277151AB8C32C00DC1E48 /* RCTWebSocketExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketExecutor.m; sourceTree = ""; }; - 00D277171AB8C35800DC1E48 /* RCT_SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCT_SRWebSocket.h; sourceTree = ""; }; - 00D277181AB8C35800DC1E48 /* RCT_SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCT_SRWebSocket.m; sourceTree = ""; }; + 13AF20401AE707C5005F5298 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = ""; }; + 13AF20411AE707C5005F5298 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = ""; }; 832C81801AAF6DEF007FA2F7 /* libRCTWebSocketDebugger.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocketDebugger.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -45,8 +45,8 @@ 832C81771AAF6DEF007FA2F7 = { isa = PBXGroup; children = ( - 00D277171AB8C35800DC1E48 /* RCT_SRWebSocket.h */, - 00D277181AB8C35800DC1E48 /* RCT_SRWebSocket.m */, + 13AF20401AE707C5005F5298 /* SRWebSocket.h */, + 13AF20411AE707C5005F5298 /* SRWebSocket.m */, 00D277141AB8C32C00DC1E48 /* RCTWebSocketExecutor.h */, 00D277151AB8C32C00DC1E48 /* RCTWebSocketExecutor.m */, 832C81811AAF6DEF007FA2F7 /* Products */, @@ -119,7 +119,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 00D277191AB8C35800DC1E48 /* RCT_SRWebSocket.m in Sources */, + 13AF20421AE707C5005F5298 /* SRWebSocket.m in Sources */, 00D277161AB8C32C00DC1E48 /* RCTWebSocketExecutor.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 59ba79d271795b..46bbbf8a4634c9 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -7,8 +7,6 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import - #import #import @@ -186,8 +184,7 @@ RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter]) dispatch_once(&onceToken, ^{ \ mapping = values; \ }); \ - NSNumber *converted = RCTConvertEnumValue(#type, mapping, @(default), json); \ - return ((type(*)(id, SEL))objc_msgSend)(converted, @selector(getter)); \ + return [RCTConvertEnumValue(#type, mapping, @(default), json) getter]; \ } /** diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 38c207459e3ac0..8823e15655c0d3 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */; }; 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E61AA5CF210034F82E /* RCTTabBarManager.m */; }; 13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */; }; + 13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AF20441AE707F9005F5298 /* RCTSlider.m */; }; 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */; }; 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */; }; 13B07FF21A69327A00A75B9A /* RCTTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEE1A69327A00A75B9A /* RCTTiming.m */; }; @@ -106,10 +107,11 @@ 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarItemManager.m; sourceTree = ""; }; 137327E51AA5CF210034F82E /* RCTTabBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarManager.h; sourceTree = ""; }; 137327E61AA5CF210034F82E /* RCTTabBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarManager.m; sourceTree = ""; }; - 1384149E1ADFCA4A003E0667 /* RCTDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = ""; }; 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = ""; }; 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTKeyCommands.m; sourceTree = ""; }; 13AF1F851AE6E777005F5298 /* RCTDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = ""; }; + 13AF20431AE707F8005F5298 /* RCTSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSlider.h; sourceTree = ""; }; + 13AF20441AE707F9005F5298 /* RCTSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSlider.m; sourceTree = ""; }; 13B07FC71A68125100A75B9A /* Layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Layout.c; sourceTree = ""; }; 13B07FC81A68125100A75B9A /* Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Layout.h; sourceTree = ""; }; 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAlertManager.h; sourceTree = ""; }; @@ -295,6 +297,8 @@ 13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */, 13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */, 13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */, + 13AF20431AE707F8005F5298 /* RCTSlider.h */, + 13AF20441AE707F9005F5298 /* RCTSlider.m */, 14F484541AABFCE100FDF6B9 /* RCTSliderManager.h */, 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */, 14F362071AABD06A001CE568 /* RCTSwitch.h */, @@ -366,7 +370,6 @@ 83CBBA491A601E3B00E9B192 /* Base */ = { isa = PBXGroup; children = ( - 1384149E1ADFCA4A003E0667 /* RCTDefines.h */, 14200DA81AC179B3008EE6BA /* RCTJavaScriptLoader.h */, 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */, 83CBBA4A1A601E3B00E9B192 /* RCTAssert.h */, @@ -490,6 +493,7 @@ 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */, 83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */, 83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */, + 13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */, 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */, 832348161A77A5AA00B55238 /* Layout.c in Sources */, 14F4D38B1AE1B7E40049C042 /* RCTProfile.m in Sources */, From 404f7d9dbfbf6e1552f4502b35d067a4c54ecd15 Mon Sep 17 00:00:00 2001 From: xcatliu Date: Tue, 21 Apr 2015 16:09:56 -0700 Subject: [PATCH 050/250] Fix AlertIOS Docs Summary: The curly braces seems to be redundant. Closes https://github.com/facebook/react-native/pull/811 Github Author: xcatliu Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Utilities/AlertIOS.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Utilities/AlertIOS.js b/Libraries/Utilities/AlertIOS.js index de72d7b9f07306..0d1612bc9e0d9e 100644 --- a/Libraries/Utilities/AlertIOS.js +++ b/Libraries/Utilities/AlertIOS.js @@ -37,7 +37,7 @@ var DEFAULT_BUTTON = { * {text: 'Foo', onPress: () => console.log('Foo Pressed!')}, * {text: 'Bar', onPress: () => console.log('Bar Pressed!')}, * ] - * )} + * ) * ``` */ From 17e5b04d1a84483244d8ad91f8f1fd0b5c78ef7e Mon Sep 17 00:00:00 2001 From: Mike Wilcox Date: Tue, 21 Apr 2015 16:37:54 -0700 Subject: [PATCH 051/250] Fix Typo Summary: * This PR fixes a typo for the NetInfo docs page Closes https://github.com/facebook/react-native/pull/937 Github Author: Mike Wilcox Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Examples/UIExplorer/NetInfoExample.js | 4 ++-- Libraries/Network/NetInfo.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Examples/UIExplorer/NetInfoExample.js b/Examples/UIExplorer/NetInfoExample.js index 18a79ae4a571f4..c322a743228306 100644 --- a/Examples/UIExplorer/NetInfoExample.js +++ b/Examples/UIExplorer/NetInfoExample.js @@ -131,12 +131,12 @@ exports.description = 'Monitor network status'; exports.examples = [ { title: 'NetInfo.isConnected', - description: 'Asyncronously load and observe connectivity', + description: 'Asynchronously load and observe connectivity', render(): ReactElement { return ; } }, { title: 'NetInfo.reachabilityIOS', - description: 'Asyncronously load and observe iOS reachability', + description: 'Asynchronously load and observe iOS reachability', render(): ReactElement { return ; } }, { diff --git a/Libraries/Network/NetInfo.js b/Libraries/Network/NetInfo.js index d98b997ca82a72..2b65671a9c30d8 100644 --- a/Libraries/Network/NetInfo.js +++ b/Libraries/Network/NetInfo.js @@ -34,7 +34,7 @@ type ReachabilityStateIOS = $Enum<{ * * ### reachabilityIOS * - * Asyncronously determine if the device is online and on a cellular network. + * Asynchronously determine if the device is online and on a cellular network. * * - `none` - device is offline * - `wifi` - device is online and connected via wifi, or is the iOS simulator @@ -60,7 +60,7 @@ type ReachabilityStateIOS = $Enum<{ * * ### isConnected * - * Available on all platforms. Asyncronously fetch a boolean to determine + * Available on all platforms. Asynchronously fetch a boolean to determine * internet connectivity. * * ``` From 368e507b3879231e07b5ee869f688eecc71ea213 Mon Sep 17 00:00:00 2001 From: Josh Zana Date: Tue, 21 Apr 2015 16:43:07 -0700 Subject: [PATCH 052/250] Implement XmlHttpRequestBase#getAllResponseHeaders and getResponseHeader Summary: Used https://github.com/facebook/react-native/pull/382 as inspiration but modified to return null instead of undefined as per the spec at https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest This unblocks use of Dropbox.js within a react-native app, as well as any other libraries that make use of these methods in XHR usage. Closes https://github.com/facebook/react-native/issues/872 Closes https://github.com/facebook/react-native/pull/892 Github Author: Josh Zana Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Network/XMLHttpRequestBase.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Libraries/Network/XMLHttpRequestBase.js b/Libraries/Network/XMLHttpRequestBase.js index d7619d07546f07..cfd1e35cc9b673 100644 --- a/Libraries/Network/XMLHttpRequestBase.js +++ b/Libraries/Network/XMLHttpRequestBase.js @@ -53,13 +53,22 @@ class XMLHttpRequestBase { } getAllResponseHeaders(): ?string { - /* Stub */ - return ''; + if (this.responseHeaders) { + var headers = []; + for (var headerName in this.responseHeaders) { + headers.push(headerName + ': ' + this.responseHeaders[headerName]); + } + return headers.join('\n'); + } + return null; } getResponseHeader(header: string): ?string { - /* Stub */ - return ''; + if (this.responseHeaders) { + var value = this.responseHeaders[header]; + return value !== undefined ? value : null; + } + return null; } setRequestHeader(header: string, value: any): void { From b1a15004de3027bb3d454cfdea61841a8c867426 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 21 Apr 2015 18:26:26 -0700 Subject: [PATCH 053/250] Fixed release builds on UIExplorer --- Libraries/RCTTest/RCTTestModule.h | 1 + Libraries/RCTTest/RCTTestRunner.m | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Libraries/RCTTest/RCTTestModule.h b/Libraries/RCTTest/RCTTestModule.h index a8a2da16ee9aa2..f248cbfca40818 100644 --- a/Libraries/RCTTest/RCTTestModule.h +++ b/Libraries/RCTTest/RCTTestModule.h @@ -10,6 +10,7 @@ #import #import "RCTBridgeModule.h" +#import "RCTDefines.h" @class FBSnapshotTestController; diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index 75a8118318ee10..4d5963743f4a9c 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -10,6 +10,7 @@ #import "RCTTestRunner.h" #import "FBSnapshotTestController.h" +#import "RCTDefines.h" #import "RCTRedBox.h" #import "RCTRootView.h" #import "RCTTestModule.h" @@ -75,6 +76,8 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictiona vc.view = [[UIView alloc] init]; [vc.view addSubview:rootView]; // Add as subview so it doesn't get resized +#if RCT_DEBUG // Prevents build errors, as RCTRedBox is underfined if RCT_DEBUG=0 + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage]; while ([date timeIntervalSinceNow] > 0 && ![testModule isDone] && error == nil) { @@ -95,6 +98,13 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictiona } else { RCTAssert([testModule isDone], @"Test didn't finish within %d seconds", TIMEOUT_SECONDS); } + +#else + + expectErrorBlock(@"RCTRedBox unavailable. Set RCT_DEBUG=1 for testing."); + +#endif + } @end From 901c24ebb8d81885043c8d236ed07e756ab81c6a Mon Sep 17 00:00:00 2001 From: James Ide Date: Tue, 21 Apr 2015 19:13:40 -0700 Subject: [PATCH 054/250] [Text] Ensure that the text background is transparent by default Summary: For a very simple view I was observing that the text background was black and had to manually be set to transparent. This ensures that text nodes have a transparent background by default. Closes https://github.com/facebook/react-native/pull/256 Github Author: James Ide Test Plan: This example component no longer renders what looks like a black block, and instead displays legible text. var Example = React.createClass({ render: function() { return ( hello ); }, }); var styles = StyleSheet.create({ container: { flex: 1, }, }; --- Libraries/Text/RCTText.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Libraries/Text/RCTText.m b/Libraries/Text/RCTText.m index 84f6b85e1f0882..e51686ac7bcc18 100644 --- a/Libraries/Text/RCTText.m +++ b/Libraries/Text/RCTText.m @@ -25,6 +25,7 @@ - (instancetype)initWithFrame:(CGRect)frame if ((self = [super initWithFrame:frame])) { _textStorage = [[NSTextStorage alloc] init]; + self.opaque = NO; self.contentMode = UIViewContentModeRedraw; } From 58a550fa0625f31108e06f32ebb2958e2ffefcf0 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 21 Apr 2015 21:07:17 -0700 Subject: [PATCH 055/250] [ReactNative] use requireNativeComponent to clean up a bunch of boilerplate --- .../ActivityIndicatorIOS.ios.js | 26 ++++---- .../DatePicker/DatePickerIOS.ios.js | 19 +----- Libraries/Components/MapView/MapView.js | 62 +++++++------------ .../Components/SwitchIOS/SwitchIOS.ios.js | 27 ++------ .../Components/TabBarIOS/TabBarIOS.ios.js | 9 +-- .../Components/TabBarIOS/TabBarItemIOS.ios.js | 17 +---- Libraries/Components/View/View.js | 16 ++++- Libraries/Components/WebView/WebView.ios.js | 20 +----- Libraries/ReactIOS/verifyPropTypes.js | 2 +- React/Views/RCTSwitchManager.m | 11 +++- React/Views/RCTTabBarItemManager.m | 2 +- 11 files changed, 77 insertions(+), 134 deletions(-) diff --git a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js index f2bcbfd516c650..3a44020a663590 100644 --- a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js +++ b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js @@ -15,13 +15,12 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var NativeModules = require('NativeModules'); var PropTypes = require('ReactPropTypes'); var React = require('React'); -var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StyleSheet = require('StyleSheet'); var View = require('View'); -var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); var keyMirror = require('keyMirror'); -var merge = require('merge'); +var requireNativeComponent = require('requireNativeComponent'); +var verifyPropTypes = require('verifyPropTypes'); var SpinnerSize = keyMirror({ large: null, @@ -100,14 +99,17 @@ var styles = StyleSheet.create({ } }); -var UIActivityIndicatorView = createReactIOSNativeComponentClass({ - validAttributes: merge( - ReactIOSViewAttributes.UIView, { - activityIndicatorViewStyle: true, // UIActivityIndicatorViewStyle=UIActivityIndicatorViewStyleWhite - animating: true, - color: true, - }), - uiViewClassName: 'UIActivityIndicatorView', -}); +var UIActivityIndicatorView = requireNativeComponent( + 'UIActivityIndicatorView', + null +); +if (__DEV__) { + var nativeOnlyProps = {activityIndicatorViewStyle: true}; + verifyPropTypes( + ActivityIndicatorIOS, + UIActivityIndicatorView.viewConfig, + nativeOnlyProps + ); +} module.exports = ActivityIndicatorIOS; diff --git a/Libraries/Components/DatePicker/DatePickerIOS.ios.js b/Libraries/Components/DatePicker/DatePickerIOS.ios.js index 9bd0a2ac473080..41fc9b8779f50d 100644 --- a/Libraries/Components/DatePicker/DatePickerIOS.ios.js +++ b/Libraries/Components/DatePicker/DatePickerIOS.ios.js @@ -16,14 +16,11 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var PropTypes = require('ReactPropTypes'); var React = require('React'); -var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var RCTDatePickerIOSConsts = require('NativeModules').UIManager.RCTDatePicker.Constants; var StyleSheet = require('StyleSheet'); var View = require('View'); -var createReactIOSNativeComponentClass = - require('createReactIOSNativeComponentClass'); -var merge = require('merge'); +var requireNativeComponent = require('requireNativeComponent'); var DATEPICKER = 'datepicker'; @@ -148,18 +145,6 @@ var styles = StyleSheet.create({ }, }); -var rkDatePickerIOSAttributes = merge(ReactIOSViewAttributes.UIView, { - date: true, - maximumDate: true, - minimumDate: true, - mode: true, - minuteInterval: true, - timeZoneOffsetInMinutes: true, -}); - -var RCTDatePickerIOS = createReactIOSNativeComponentClass({ - validAttributes: rkDatePickerIOSAttributes, - uiViewClassName: 'RCTDatePicker', -}); +var RCTDatePickerIOS = requireNativeComponent('RCTDatePicker', DatePickerIOS); module.exports = DatePickerIOS; diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index 2f02b1b9dc5f51..e38dd9564bbc3c 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -13,6 +13,7 @@ var EdgeInsetsPropType = require('EdgeInsetsPropType'); var NativeMethodsMixin = require('NativeMethodsMixin'); +var Platform = require('Platform'); var React = require('React'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var View = require('View'); @@ -21,6 +22,7 @@ var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentC var deepDiffer = require('deepDiffer'); var insetsDiffer = require('insetsDiffer'); var merge = require('merge'); +var requireNativeComponent = require('requireNativeComponent'); type Event = Object; type MapRegion = { @@ -156,46 +158,30 @@ var MapView = React.createClass({ }, render: function() { - return ( - - ); + return ; }, - }); -var RCTMap = createReactIOSNativeComponentClass({ - validAttributes: merge( - ReactIOSViewAttributes.UIView, { - showsUserLocation: true, - zoomEnabled: true, - rotateEnabled: true, - pitchEnabled: true, - scrollEnabled: true, - region: {diff: deepDiffer}, - annotations: {diff: deepDiffer}, - maxDelta: true, - minDelta: true, - legalLabelInsets: {diff: insetsDiffer}, - } - ), - uiViewClassName: 'RCTMap', -}); +if (Platform.OS === 'android') { + var RCTMap = createReactIOSNativeComponentClass({ + validAttributes: merge( + ReactIOSViewAttributes.UIView, { + showsUserLocation: true, + zoomEnabled: true, + rotateEnabled: true, + pitchEnabled: true, + scrollEnabled: true, + region: {diff: deepDiffer}, + annotations: {diff: deepDiffer}, + maxDelta: true, + minDelta: true, + legalLabelInsets: {diff: insetsDiffer}, + } + ), + uiViewClassName: 'RCTMap', + }); +} else { + var RCTMap = requireNativeComponent('RCTMap', MapView); +} module.exports = MapView; diff --git a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js index 70222794758e96..5a56e36b753d4f 100644 --- a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js +++ b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js @@ -16,11 +16,9 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var PropTypes = require('ReactPropTypes'); var React = require('React'); -var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StyleSheet = require('StyleSheet'); -var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); -var merge = require('merge'); +var requireNativeComponent = require('requireNativeComponent'); var SWITCH = 'switch'; @@ -88,20 +86,16 @@ var SwitchIOS = React.createClass({ // The underlying switch might have changed, but we're controlled, // and so want to ensure it represents our value. - this.refs[SWITCH].setNativeProps({on: this.props.value}); + this.refs[SWITCH].setNativeProps({value: this.props.value}); }, render: function() { return ( ); } @@ -114,17 +108,6 @@ var styles = StyleSheet.create({ }, }); -var rkSwitchAttributes = merge(ReactIOSViewAttributes.UIView, { - onTintColor: true, - tintColor: true, - thumbTintColor: true, - on: true, - enabled: true, -}); - -var RCTSwitch = createReactIOSNativeComponentClass({ - validAttributes: rkSwitchAttributes, - uiViewClassName: 'RCTSwitch', -}); +var RCTSwitch = requireNativeComponent('RCTSwitch', SwitchIOS); module.exports = SwitchIOS; diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js index 05ac37c74f5bc7..4163b2d7861b69 100644 --- a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js @@ -12,12 +12,11 @@ 'use strict'; var React = require('React'); -var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StyleSheet = require('StyleSheet'); var TabBarItemIOS = require('TabBarItemIOS'); var View = require('View'); -var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); +var requireNativeComponent = require('requireNativeComponent'); var TabBarIOS = React.createClass({ statics: { @@ -43,10 +42,6 @@ var styles = StyleSheet.create({ } }); -var config = { - validAttributes: ReactIOSViewAttributes.UIView, - uiViewClassName: 'RCTTabBar', -}; -var RCTTabBar = createReactIOSNativeComponentClass(config); +var RCTTabBar = requireNativeComponent('RCTTabBar', TabBarIOS); module.exports = TabBarIOS; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js index 86b38a8cda836d..32945c4343474e 100644 --- a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -13,13 +13,11 @@ var Image = require('Image'); var React = require('React'); -var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StaticContainer = require('StaticContainer.react'); var StyleSheet = require('StyleSheet'); var View = require('View'); -var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); -var merge = require('merge'); +var requireNativeComponent = require('requireNativeComponent'); var TabBarItemIOS = React.createClass({ propTypes: { @@ -121,7 +119,7 @@ var TabBarItemIOS = React.createClass({ selectedIcon={this.props.selectedIcon && this.props.selectedIcon.uri} onPress={this.props.onPress} selected={this.props.selected} - badgeValue={badge} + badge={badge} title={this.props.title} style={[styles.tab, this.props.style]}> {tabContents} @@ -140,15 +138,6 @@ var styles = StyleSheet.create({ } }); -var RCTTabBarItem = createReactIOSNativeComponentClass({ - validAttributes: merge(ReactIOSViewAttributes.UIView, { - title: true, - icon: true, - selectedIcon: true, - selected: true, - badgeValue: true, - }), - uiViewClassName: 'RCTTabBarItem', -}); +var RCTTabBarItem = requireNativeComponent('RCTTabBarItem', TabBarItemIOS); module.exports = TabBarItemIOS; diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 0fdfa1fc8b76c4..0da57f554257e5 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -13,12 +13,13 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var PropTypes = require('ReactPropTypes'); +var RCTUIManager = require('NativeModules').UIManager; var React = require('React'); +var ReactIOSStyleAttributes = require('ReactIOSStyleAttributes'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var StyleSheetPropType = require('StyleSheetPropType'); var ViewStylePropTypes = require('ViewStylePropTypes'); - var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); var stylePropType = StyleSheetPropType(ViewStylePropTypes); @@ -157,17 +158,26 @@ var View = React.createClass({ }, }); - var RCTView = createReactIOSNativeComponentClass({ validAttributes: ReactIOSViewAttributes.RCTView, uiViewClassName: 'RCTView', }); RCTView.propTypes = View.propTypes; +if (__DEV__) { + var viewConfig = RCTUIManager.viewConfigs && RCTUIManager.viewConfigs.RCTView || {}; + for (var prop in viewConfig.nativeProps) { + var viewAny: any = View; // Appease flow + if (!viewAny.propTypes[prop] && !ReactIOSStyleAttributes[prop]) { + throw new Error( + 'View is missing propType for native prop `' + prop + '`' + ); + } + } +} var ViewToExport = RCTView; if (__DEV__) { ViewToExport = View; } - module.exports = ViewToExport; diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 6257c12b71a8c2..c4e4fbcd3299c8 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -19,16 +19,13 @@ var StyleSheet = require('StyleSheet'); var Text = require('Text'); var View = require('View'); -var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); +var invariant = require('invariant'); var keyMirror = require('keyMirror'); -var insetsDiffer = require('insetsDiffer'); -var merge = require('merge'); +var requireNativeComponent = require('requireNativeComponent'); var PropTypes = React.PropTypes; var RCTWebViewManager = require('NativeModules').WebViewManager; -var invariant = require('invariant'); - var BGWASH = 'rgba(255,255,255,0.8)'; var RCT_WEBVIEW_REF = 'webview'; @@ -213,18 +210,7 @@ var WebView = React.createClass({ }, }); -var RCTWebView = createReactIOSNativeComponentClass({ - validAttributes: merge(ReactIOSViewAttributes.UIView, { - url: true, - html: true, - bounces: true, - scrollEnabled: true, - contentInset: {diff: insetsDiffer}, - automaticallyAdjustContentInsets: true, - shouldInjectAJAXHandler: true - }), - uiViewClassName: 'RCTWebView', -}); +var RCTWebView = requireNativeComponent('RCTWebView', WebView); var styles = StyleSheet.create({ container: { diff --git a/Libraries/ReactIOS/verifyPropTypes.js b/Libraries/ReactIOS/verifyPropTypes.js index 032e572ece118d..ab1d61728895fd 100644 --- a/Libraries/ReactIOS/verifyPropTypes.js +++ b/Libraries/ReactIOS/verifyPropTypes.js @@ -23,7 +23,7 @@ function verifyPropTypes( return; // This happens for UnimplementedView. } var nativeProps = viewConfig.nativeProps; - for (var prop in viewConfig.nativeProps) { + for (var prop in nativeProps) { if (!component.propTypes[prop] && !View.propTypes[prop] && !ReactIOSStyleAttributes[prop] && diff --git a/React/Views/RCTSwitchManager.m b/React/Views/RCTSwitchManager.m index eb0d626e62e161..c60d83e81ebb2f 100644 --- a/React/Views/RCTSwitchManager.m +++ b/React/Views/RCTSwitchManager.m @@ -42,7 +42,14 @@ - (void)onChange:(RCTSwitch *)sender RCT_EXPORT_VIEW_PROPERTY(onTintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(thumbTintColor, UIColor); -RCT_EXPORT_VIEW_PROPERTY(on, BOOL); -RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL); +RCT_REMAP_VIEW_PROPERTY(value, on, BOOL); +RCT_CUSTOM_VIEW_PROPERTY(disabled, BOOL, RCTSwitch) +{ + if (json) { + view.enabled = !([RCTConvert BOOL:json]); + } else { + view.enabled = defaultView.enabled; + } +} @end diff --git a/React/Views/RCTTabBarItemManager.m b/React/Views/RCTTabBarItemManager.m index 8bbe782b7346ce..cdfa8669ce98d5 100644 --- a/React/Views/RCTTabBarItemManager.m +++ b/React/Views/RCTTabBarItemManager.m @@ -24,7 +24,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(selected, BOOL); RCT_EXPORT_VIEW_PROPERTY(icon, NSString); RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage, UIImage); -RCT_REMAP_VIEW_PROPERTY(badgeValue, barItem.badgeValue, NSString); +RCT_REMAP_VIEW_PROPERTY(badge, barItem.badgeValue, NSString); RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem) { view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title; From 0f7ebf23a92f464e3b124a40f9bb89b8505ff874 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Wed, 22 Apr 2015 03:45:08 -0700 Subject: [PATCH 056/250] [react_native] JS files from D1999034: [react_native] Fix source maps on Android --- .../JavaScriptAppEngine/Initialization/loadSourceMap.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js index 9ecf2543b94d44..4d4d21e25d6942 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js +++ b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js @@ -39,7 +39,10 @@ function fetchSourceMap(): Promise { .then(response => response.text()) } -function extractSourceMapURL({url, text}): string { +function extractSourceMapURL({url, text, fullSourceMappingURL}): string { + if (fullSourceMappingURL) { + return fullSourceMappingURL; + } var mapURL = SourceMapURL.getFrom(text); var baseURL = url.match(/(.+:\/\/.*?)\//)[1]; return baseURL + mapURL; From 462224727aa5ee5f0d8db8d94a13fd8e305e2e45 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 22 Apr 2015 04:03:46 -0700 Subject: [PATCH 057/250] Reduced prop mapping overhead --- Libraries/ReactIOS/requireNativeComponent.js | 9 ++-- React/Modules/RCTUIManager.m | 44 ++++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/Libraries/ReactIOS/requireNativeComponent.js b/Libraries/ReactIOS/requireNativeComponent.js index 55ad8a6b989e9e..4d9241853626a4 100644 --- a/Libraries/ReactIOS/requireNativeComponent.js +++ b/Libraries/ReactIOS/requireNativeComponent.js @@ -41,18 +41,19 @@ function requireNativeComponent( viewName: string, wrapperComponent: ?Function ): Function { - var viewConfig = RCTUIManager.viewConfigs && RCTUIManager.viewConfigs[viewName]; - if (!viewConfig) { + var viewConfig = RCTUIManager[viewName]; + if (!viewConfig || !viewConfig.nativeProps) { return UnimplementedView; } var nativeProps = { - ...RCTUIManager.viewConfigs.RCTView.nativeProps, + ...RCTUIManager.RCTView.nativeProps, ...viewConfig.nativeProps, }; + viewConfig.uiViewClassName = viewName; viewConfig.validAttributes = {}; for (var key in nativeProps) { // TODO: deep diff by default in diffRawProperties instead of setting it here - var differ = TypeToDifferMap[nativeProps[key].type] || deepDiffer; + var differ = TypeToDifferMap[nativeProps[key]] || deepDiffer; viewConfig.validAttributes[key] = {diff: differ}; } if (__DEV__) { diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index fc688e491cf55d..b7ae182bad84f9 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -225,29 +225,23 @@ @implementation RCTUIManager // TODO: only send name once instead of a dictionary of name and type keyed by name static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewName) { - NSMutableDictionary *nativeProps = [[NSMutableDictionary alloc] init]; - static const char *prefix = "getPropConfig"; - static const NSUInteger prefixLength = sizeof("getPropConfig") - 1; - unsigned int methodCount = 0; - Method *methods = class_copyMethodList(objc_getMetaClass(class_getName(managerClass)), &methodCount); - for (unsigned int i = 0; i < methodCount; i++) { + unsigned int count = 0; + Method *methods = class_copyMethodList(object_getClass(managerClass), &count); + NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithCapacity:count]; + for (unsigned int i = 0; i < count; i++) { Method method = methods[i]; - SEL getInfo = method_getName(method); - const char *selName = sel_getName(getInfo); - if (strlen(selName) > prefixLength && strncmp(selName, prefix, prefixLength) == 0) { - NSString *name = @(selName); - NSRange nameRange = [name rangeOfString:@"_"]; + NSString *methodName = NSStringFromSelector(method_getName(method)); + if ([methodName hasPrefix:@"getPropConfig"]) { + NSRange nameRange = [methodName rangeOfString:@"_"]; if (nameRange.length) { - name = [name substringFromIndex:nameRange.location + 1]; - NSString *type = ((NSString *(*)(id, SEL))method_getImplementation(method))(managerClass, getInfo); - nativeProps[name] = @{@"name": name, @"type": type}; + NSString *name = [methodName substringFromIndex:nameRange.location + 1]; + NSString *type = [managerClass valueForKey:methodName]; + props[name] = type; } } } - return @{ - @"uiViewClassName": viewName, - @"nativeProps": nativeProps - }; + free(methods); + return props; } - (instancetype)init @@ -1390,17 +1384,22 @@ - (NSDictionary *)constantsToExport } mutableCopy]; [_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTViewManager *manager, BOOL *stop) { + NSMutableDictionary *constantsNamespace = [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]]; + + // Add custom constants // TODO: should these be inherited? NSDictionary *constants = RCTClassOverridesInstanceMethod([manager class], @selector(constantsToExport)) ? [manager constantsToExport] : nil; if (constants.count) { - NSMutableDictionary *constantsNamespace = [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]]; RCTAssert(constantsNamespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name); // add an additional 'Constants' namespace for each class constantsNamespace[@"Constants"] = constants; - allJSConstants[name] = [constantsNamespace copy]; } + + // Add native props + constantsNamespace[@"nativeProps"] = _viewConfigs[name]; + + allJSConstants[name] = [constantsNamespace copy]; }]; - allJSConstants[@"viewConfigs"] = _viewConfigs; return allJSConstants; } @@ -1409,8 +1408,7 @@ - (NSDictionary *)constantsToExport errorCallback:(RCTResponseSenderBlock)errorCallback) { if (_nextLayoutAnimation) { - RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.", - _nextLayoutAnimation, config); + RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.", _nextLayoutAnimation, config); } if (config[@"delete"] != nil) { RCTLogError(@"LayoutAnimation only supports create and update right now. Config: %@", config); From 3595b79ec3d270107e797d3f1ad5583098d8147d Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Wed, 22 Apr 2015 07:03:55 -0700 Subject: [PATCH 058/250] [ReactNative] Move VSync bound events to JS thread --- .../RCTWebSocketExecutor.m | 5 ++ React/Base/RCTBridge.m | 39 +++++--- React/Base/RCTJavaScriptExecutor.h | 7 ++ React/Base/RCTProfile.m | 90 +++++++++++-------- React/Modules/RCTTiming.m | 2 - 5 files changed, 92 insertions(+), 51 deletions(-) diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m index eb6428fc27fe80..753c8d76d38324 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m @@ -175,6 +175,11 @@ - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)object }); } +- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block +{ + dispatch_async(dispatch_get_main_queue(), block); +} + - (void)invalidate { _socket.delegate = nil; diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 1c676640eba501..a4a4362b0323b2 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -694,7 +694,7 @@ - (NSString *)description @interface RCTDisplayLink : NSObject -- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector NS_DESIGNATED_INITIALIZER; @end @@ -708,14 +708,16 @@ @implementation RCTDisplayLink { __weak RCTBridge *_bridge; CADisplayLink *_displayLink; + SEL _selector; } -- (instancetype)initWithBridge:(RCTBridge *)bridge +- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector { if ((self = [super init])) { _bridge = bridge; + _selector = selector; _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update:)]; - [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; } return self; } @@ -735,7 +737,10 @@ - (void)invalidate - (void)_update:(CADisplayLink *)displayLink { - [_bridge _update:displayLink]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [_bridge performSelector:_selector withObject:displayLink]; +#pragma clang diagnostic pop } @end @@ -770,6 +775,7 @@ @implementation RCTBridge NSURL *_bundleURL; RCTBridgeModuleProviderBlock _moduleProvider; RCTDisplayLink *_displayLink; + RCTDisplayLink *_vsyncDisplayLink; NSMutableSet *_frameUpdateObservers; NSMutableArray *_scheduledCalls; RCTSparseArray *_scheduledCallbacks; @@ -799,11 +805,15 @@ - (void)setUp _latestJSExecutor = _javaScriptExecutor; _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; _methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL); - _displayLink = [[RCTDisplayLink alloc] initWithBridge:self]; _frameUpdateObservers = [[NSMutableSet alloc] init]; _scheduledCalls = [[NSMutableArray alloc] init]; _scheduledCallbacks = [[RCTSparseArray alloc] init]; + [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ + _displayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_jsThreadUpdate:)]; + }]; + _vsyncDisplayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_mainThreadUpdate:)]; + // Register passed-in module instances NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init]; for (id module in _moduleProvider ? _moduleProvider() : nil) { @@ -1008,6 +1018,7 @@ - (void)invalidate _javaScriptExecutor = nil; [_displayLink invalidate]; + [_vsyncDisplayLink invalidate]; _frameUpdateObservers = nil; // Invalidate modules @@ -1294,9 +1305,9 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i return YES; } -- (void)_update:(CADisplayLink *)displayLink +- (void)_jsThreadUpdate:(CADisplayLink *)displayLink { - RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); + RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g"); RCTProfileBeginEvent(); RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink]; @@ -1306,13 +1317,6 @@ - (void)_update:(CADisplayLink *)displayLink } } - [self _runScheduledCalls]; - - RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil); -} - -- (void)_runScheduledCalls -{ #if BATCHED_BRIDGE NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls]; @@ -1330,6 +1334,13 @@ - (void)_runScheduledCalls } #endif + + RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil); +} + +- (void)_mainThreadUpdate:(CADisplayLink *)displayLink +{ + RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); } - (void)addFrameUpdateObserver:(id)observer diff --git a/React/Base/RCTJavaScriptExecutor.h b/React/Base/RCTJavaScriptExecutor.h index 8ff5a16585d8bc..eb7fd7d31947cd 100644 --- a/React/Base/RCTJavaScriptExecutor.h +++ b/React/Base/RCTJavaScriptExecutor.h @@ -42,6 +42,13 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error); - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete; + +/** + * Enqueue a block to run in the executors JS thread. Fallback to `dispatch_async` + * on the main queue if the executor doesn't own a thread. + */ +- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block; + @end static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID"; diff --git a/React/Base/RCTProfile.m b/React/Base/RCTProfile.m index 929a026a9587a2..19c6900c73abbc 100644 --- a/React/Base/RCTProfile.m +++ b/React/Base/RCTProfile.m @@ -36,6 +36,7 @@ NSUInteger RCTProfileEventID = 0; NSMutableDictionary *RCTProfileOngoingEvents; NSTimeInterval RCTProfileStartTime; +NSLock *_RCTProfileLock; #pragma mark - Macros @@ -51,6 +52,11 @@ return __VA_ARGS__; \ } +#define RCTProfileLock(...) \ +[_RCTProfileLock lock]; \ +__VA_ARGS__ \ +[_RCTProfileLock unlock] + #pragma mark - Private Helpers NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp) @@ -66,7 +72,6 @@ NSDictionary *RCTProfileGetMemoryUsage(void) { - CHECK(@{}); struct task_basic_info info; mach_msg_type_number_t size = sizeof(info); kern_return_t kerr = task_info(mach_task_self(), @@ -88,66 +93,81 @@ BOOL RCTProfileIsProfiling(void) { - return RCTProfileInfo != nil; + RCTProfileLock( + BOOL profiling = RCTProfileInfo != nil; + ); + return profiling; } void RCTProfileInit(void) { - RCTProfileStartTime = CACurrentMediaTime(); - RCTProfileOngoingEvents = [[NSMutableDictionary alloc] init]; - RCTProfileInfo = @{ - RCTProfileTraceEvents: [[NSMutableArray alloc] init], - RCTProfileSamples: [[NSMutableArray alloc] init], - }; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _RCTProfileLock = [[NSLock alloc] init]; + }); + RCTProfileLock( + RCTProfileStartTime = CACurrentMediaTime(); + RCTProfileOngoingEvents = [[NSMutableDictionary alloc] init]; + RCTProfileInfo = @{ + RCTProfileTraceEvents: [[NSMutableArray alloc] init], + RCTProfileSamples: [[NSMutableArray alloc] init], + }; + ); } NSString *RCTProfileEnd(void) { - NSString *log = RCTJSONStringify(RCTProfileInfo, NULL); - RCTProfileEventID = 0; - RCTProfileInfo = nil; - RCTProfileOngoingEvents = nil; + RCTProfileLock( + NSString *log = RCTJSONStringify(RCTProfileInfo, NULL); + RCTProfileEventID = 0; + RCTProfileInfo = nil; + RCTProfileOngoingEvents = nil; + ); return log; } NSNumber *_RCTProfileBeginEvent(void) { CHECK(@0); - NSNumber *eventID = @(++RCTProfileEventID); - RCTProfileOngoingEvents[eventID] = RCTProfileTimestamp(CACurrentMediaTime()); + RCTProfileLock( + NSNumber *eventID = @(++RCTProfileEventID); + RCTProfileOngoingEvents[eventID] = RCTProfileTimestamp(CACurrentMediaTime()); + ); return eventID; } void _RCTProfileEndEvent(NSNumber *eventID, NSString *name, NSString *categories, id args) { CHECK(); - NSNumber *startTimestamp = RCTProfileOngoingEvents[eventID]; - if (!startTimestamp) { - return; - } - - NSNumber *endTimestamp = RCTProfileTimestamp(CACurrentMediaTime()); - - RCTProfileAddEvent(RCTProfileTraceEvents, - @"name": name, - @"cat": categories, - @"ph": @"X", - @"ts": startTimestamp, - @"dur": @(endTimestamp.doubleValue - startTimestamp.doubleValue), - @"args": args ?: @[], + RCTProfileLock( + NSNumber *startTimestamp = RCTProfileOngoingEvents[eventID]; + if (startTimestamp) { + NSNumber *endTimestamp = RCTProfileTimestamp(CACurrentMediaTime()); + + RCTProfileAddEvent(RCTProfileTraceEvents, + @"name": name, + @"cat": categories, + @"ph": @"X", + @"ts": startTimestamp, + @"dur": @(endTimestamp.doubleValue - startTimestamp.doubleValue), + @"args": args ?: @[], + ); + [RCTProfileOngoingEvents removeObjectForKey:eventID]; + } ); - [RCTProfileOngoingEvents removeObjectForKey:eventID]; } void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString *scope) { CHECK(); - RCTProfileAddEvent(RCTProfileTraceEvents, - @"name": name, - @"ts": RCTProfileTimestamp(timestamp), - @"scope": scope, - @"ph": @"i", - @"args": RCTProfileGetMemoryUsage(), + RCTProfileLock( + RCTProfileAddEvent(RCTProfileTraceEvents, + @"name": name, + @"ts": RCTProfileTimestamp(timestamp), + @"scope": scope, + @"ph": @"i", + @"args": RCTProfileGetMemoryUsage(), + ); ); } diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index 62d42a7bbf532d..1d99c1a2d42663 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -142,8 +142,6 @@ - (void)startTimers - (void)didUpdateFrame:(RCTFrameUpdate *)update { - RCTAssertMainThread(); - NSMutableArray *timersToCall = [[NSMutableArray alloc] init]; for (RCTTimer *timer in _timers.allObjects) { if ([timer updateFoundNeedsJSUpdate]) { From eafe93096caa3cad22c5ccbaa64abd9708825f70 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Wed, 22 Apr 2015 09:10:58 -0700 Subject: [PATCH 059/250] [react_native] JS files from D2012956: [react_native] Never return null from XHR.getAllResponseHeaders if request has completed --- Libraries/Network/XMLHttpRequestBase.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Libraries/Network/XMLHttpRequestBase.js b/Libraries/Network/XMLHttpRequestBase.js index cfd1e35cc9b673..4a4f16ac685178 100644 --- a/Libraries/Network/XMLHttpRequestBase.js +++ b/Libraries/Network/XMLHttpRequestBase.js @@ -60,6 +60,7 @@ class XMLHttpRequestBase { } return headers.join('\n'); } + // according to the spec, return null <==> no response has been received return null; } @@ -131,7 +132,7 @@ class XMLHttpRequestBase { return; } this.status = status; - this.responseHeaders = responseHeaders; + this.responseHeaders = responseHeaders || {}; this.responseText = responseText; this._setReadyState(this.DONE); this._sendLoad(); From 7aa413d6198fef69be1ac98200d4fa75daa54519 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Wed, 22 Apr 2015 10:09:02 -0700 Subject: [PATCH 060/250] [ReactNative] Fix Android back btn regression from D2010265 --- Libraries/CustomComponents/Navigator/Navigator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index f4fcdd443776fd..ea8977ff87ca38 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -459,7 +459,7 @@ var Navigator = React.createClass({ }, _handleAndroidBackPress: function() { - var didPop = this.pop(); + var didPop = this.requestPop(); if (!didPop) { BackAndroid.exitApp(); } From 0727cde42cda9d1e8c495297a789e4166ae12455 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Wed, 22 Apr 2015 10:10:56 -0700 Subject: [PATCH 061/250] [ReactNative] Quick fix to busted redboxes --- .../JavaScriptAppEngine/Initialization/ExceptionsManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index d9118b7484f0e7..c5476eaab5cfe3 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -28,7 +28,7 @@ type Exception = { function handleException(e: Exception) { var stack = parseErrorStack(e); console.error( - 'Error: ' + + 'Err0r: ' + '\n stack: \n' + stackToString(stack) + '\n URL: ' + e.sourceURL + '\n line: ' + e.line + From b4c82a4089d2eed5ed79120886852cfeb35fae67 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Wed, 22 Apr 2015 11:04:24 -0700 Subject: [PATCH 062/250] [react-packager] Additional data to asset modules --- .../AssetServer/__tests__/AssetServer-test.js | 141 ++++++++++++------ .../react-packager/src/AssetServer/index.js | 63 +++++--- .../__tests__/DependencyGraph-test.js | 2 +- .../haste/DependencyGraph/index.js | 6 +- .../src/Packager/__tests__/Packager-test.js | 31 +++- packager/react-packager/src/Packager/index.js | 21 ++- packager/react-packager/src/Server/index.js | 9 +- packager/react-packager/src/__mocks__/fs.js | 12 +- .../__tests__/extractAssetResolution-test.js | 24 ++- ...tResolution.js => getAssetDataFromName.js} | 9 +- 10 files changed, 225 insertions(+), 93 deletions(-) rename packager/react-packager/src/lib/{extractAssetResolution.js => getAssetDataFromName.js} (63%) diff --git a/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js b/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js index eede72c0ca3cbb..94dba8cff78ae4 100644 --- a/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js +++ b/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js @@ -3,6 +3,7 @@ jest .autoMockOff() .mock('../../lib/declareOpts') + .mock('crypto') .mock('fs'); var fs = require('fs'); @@ -10,63 +11,65 @@ var AssetServer = require('../'); var Promise = require('bluebird'); describe('AssetServer', function() { - pit('should work for the simple case', function() { - var server = new AssetServer({ - projectRoots: ['/root'], - assetExts: ['png'], - }); + describe('assetServer.get', function() { + pit('should work for the simple case', function() { + var server = new AssetServer({ + projectRoots: ['/root'], + assetExts: ['png'], + }); - fs.__setMockFilesystem({ - 'root': { - imgs: { - 'b.png': 'b image', - 'b@2x.png': 'b2 image', + fs.__setMockFilesystem({ + 'root': { + imgs: { + 'b.png': 'b image', + 'b@2x.png': 'b2 image', + } } - } - }); + }); - return Promise.all([ - server.get('imgs/b.png'), - server.get('imgs/b@1x.png'), - ]).then(function(resp) { - resp.forEach(function(data) { - expect(data).toBe('b image'); + return Promise.all([ + server.get('imgs/b.png'), + server.get('imgs/b@1x.png'), + ]).then(function(resp) { + resp.forEach(function(data) { + expect(data).toBe('b image'); + }); }); }); - }); - pit.only('should pick the bigger one', function() { - var server = new AssetServer({ - projectRoots: ['/root'], - assetExts: ['png'], - }); + pit('should pick the bigger one', function() { + var server = new AssetServer({ + projectRoots: ['/root'], + assetExts: ['png'], + }); - fs.__setMockFilesystem({ - 'root': { - imgs: { - 'b@1x.png': 'b1 image', - 'b@2x.png': 'b2 image', - 'b@4x.png': 'b4 image', - 'b@4.5x.png': 'b4.5 image', + fs.__setMockFilesystem({ + 'root': { + imgs: { + 'b@1x.png': 'b1 image', + 'b@2x.png': 'b2 image', + 'b@4x.png': 'b4 image', + 'b@4.5x.png': 'b4.5 image', + } } - } - }); + }); - return server.get('imgs/b@3x.png').then(function(data) { - expect(data).toBe('b4 image'); + return server.get('imgs/b@3x.png').then(function(data) { + expect(data).toBe('b4 image'); + }); }); - }); - pit('should support multiple project roots', function() { - var server = new AssetServer({ - projectRoots: ['/root'], - assetExts: ['png'], - }); + pit('should support multiple project roots', function() { + var server = new AssetServer({ + projectRoots: ['/root', '/root2'], + assetExts: ['png'], + }); - fs.__setMockFilesystem({ - 'root': { - imgs: { - 'b.png': 'b image', + fs.__setMockFilesystem({ + 'root': { + imgs: { + 'b.png': 'b image', + }, }, 'root2': { 'newImages': { @@ -75,11 +78,53 @@ describe('AssetServer', function() { }, }, }, - } + }); + + return server.get('newImages/imgs/b.png').then(function(data) { + expect(data).toBe('b1 image'); + }); }); + }); + + describe('assetSerer.getAssetData', function() { + pit('should get assetData', function() { + var hash = { + update: jest.genMockFn(), + digest: jest.genMockFn(), + }; + + hash.digest.mockImpl(function() { + return 'wow such hash'; + }); + require('crypto').createHash.mockImpl(function() { + return hash; + }); + + var server = new AssetServer({ + projectRoots: ['/root'], + assetExts: ['png'], + }); - return server.get('newImages/imgs/b.png').then(function(data) { - expect(data).toBe('b1 image'); + fs.__setMockFilesystem({ + 'root': { + imgs: { + 'b@1x.png': 'b1 image', + 'b@2x.png': 'b2 image', + 'b@4x.png': 'b4 image', + 'b@4.5x.png': 'b4.5 image', + } + } + }); + + return server.getAssetData('imgs/b.png').then(function(data) { + expect(hash.update.mock.calls.length).toBe(4); + expect(data).toEqual({ + type: 'png', + name: 'b', + scales: [1, 2, 4, 4.5], + hash: 'wow such hash', + }); + }); }); }); }); diff --git a/packager/react-packager/src/AssetServer/index.js b/packager/react-packager/src/AssetServer/index.js index bdabafff4a177a..6f07dd01d38f89 100644 --- a/packager/react-packager/src/AssetServer/index.js +++ b/packager/react-packager/src/AssetServer/index.js @@ -9,10 +9,11 @@ 'use strict'; var declareOpts = require('../lib/declareOpts'); -var extractAssetResolution = require('../lib/extractAssetResolution'); +var getAssetDataFromName = require('../lib/getAssetDataFromName'); var path = require('path'); var Promise = require('bluebird'); var fs = require('fs'); +var crypto = require('crypto'); var lstat = Promise.promisify(fs.lstat); var readDir = Promise.promisify(fs.readdir); @@ -44,11 +45,11 @@ function AssetServer(options) { * * 1. We first parse the directory of the asset * 2. We check to find a matching directory in one of the project roots - * 3. We then build a map of all assets and their resolutions in this directory + * 3. We then build a map of all assets and their scales in this directory * 4. Then pick the closest resolution (rounding up) to the requested one */ -AssetServer.prototype.get = function(assetPath) { +AssetServer.prototype._getAssetRecord = function(assetPath) { var filename = path.basename(assetPath); return findRoot( @@ -60,13 +61,7 @@ AssetServer.prototype.get = function(assetPath) { readDir(dir), ]; }).spread(function(dir, files) { - // Easy case. File exactly what the client requested. - var index = files.indexOf(filename); - if (index > -1) { - return readFile(path.join(dir, filename)); - } - - var assetData = extractAssetResolution(filename); + var assetData = getAssetDataFromName(filename); var map = buildAssetMap(dir, files); var record = map[assetData.assetName]; @@ -74,8 +69,15 @@ AssetServer.prototype.get = function(assetPath) { throw new Error('Asset not found'); } - for (var i = 0; i < record.resolutions.length; i++) { - if (record.resolutions[i] >= assetData.resolution) { + return record; + }); +}; + +AssetServer.prototype.get = function(assetPath) { + var assetData = getAssetDataFromName(assetPath); + return this._getAssetRecord(assetPath).then(function(record) { + for (var i = 0; i < record.scales.length; i++) { + if (record.scales[i] >= assetData.resolution) { return readFile(record.files[i]); } } @@ -84,6 +86,33 @@ AssetServer.prototype.get = function(assetPath) { }); }; +AssetServer.prototype.getAssetData = function(assetPath) { + var nameData = getAssetDataFromName(assetPath); + var data = { + name: nameData.name, + type: 'png', + }; + + return this._getAssetRecord(assetPath).then(function(record) { + data.scales = record.scales; + + return Promise.all( + record.files.map(function(file) { + return lstat(file); + }) + ); + }).then(function(stats) { + var hash = crypto.createHash('md5'); + + stats.forEach(function(stat) { + hash.update(stat.mtime.getTime().toString()); + }); + + data.hash = hash.digest('hex'); + return data; + }); +}; + function findRoot(roots, dir) { return Promise.some( roots.map(function(root) { @@ -105,26 +134,26 @@ function findRoot(roots, dir) { } function buildAssetMap(dir, files) { - var assets = files.map(extractAssetResolution); + var assets = files.map(getAssetDataFromName); var map = Object.create(null); assets.forEach(function(asset, i) { var file = files[i]; var record = map[asset.assetName]; if (!record) { record = map[asset.assetName] = { - resolutions: [], + scales: [], files: [], }; } var insertIndex; - var length = record.resolutions.length; + var length = record.scales.length; for (insertIndex = 0; insertIndex < length; insertIndex++) { - if (asset.resolution < record.resolutions[insertIndex]) { + if (asset.resolution < record.scales[insertIndex]) { break; } } - record.resolutions.splice(insertIndex, 0, asset.resolution); + record.scales.splice(insertIndex, 0, asset.resolution); record.files.splice(insertIndex, 0, path.join(dir, file)); }); diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js index cda717e875ac98..9cb08122c23fcb 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js @@ -14,7 +14,7 @@ jest .dontMock('absolute-path') .dontMock('../docblock') .dontMock('../../replacePatterns') - .dontMock('../../../../lib/extractAssetResolution') + .dontMock('../../../../lib/getAssetDataFromName') .setMock('../../../ModuleDescriptor', function(data) {return data;}); describe('DependencyGraph', function() { diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js index 6afb5f25c879e9..08a4b513bf0023 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js @@ -18,7 +18,7 @@ var isAbsolutePath = require('absolute-path'); var debug = require('debug')('DependecyGraph'); var util = require('util'); var declareOpts = require('../../../lib/declareOpts'); -var extractAssetResolution = require('../../../lib/extractAssetResolution'); +var getAssetDataFromName = require('../../../lib/getAssetDataFromName'); var readFile = Promise.promisify(fs.readFile); var readDir = Promise.promisify(fs.readdir); @@ -422,7 +422,7 @@ DependecyGraph.prototype._processModule = function(modulePath) { var module; if (this._assetExts.indexOf(extname(modulePath)) > -1) { - var assetData = extractAssetResolution(this._lookupName(modulePath)); + var assetData = getAssetDataFromName(this._lookupName(modulePath)); moduleData.id = assetData.assetName; moduleData.resolution = assetData.resolution; moduleData.isAsset = true; @@ -651,7 +651,7 @@ DependecyGraph.prototype._processAsset_DEPRECATED = function(file) { path: path.resolve(file), isAsset_DEPRECATED: true, dependencies: [], - resolution: extractAssetResolution(file).resolution, + resolution: getAssetDataFromName(file).resolution, }); } }; diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index 763e6dd6d17b9a..8e1420a3a6c0dd 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -43,11 +43,20 @@ describe('Packager', function() { }; }); + require('fs').readFile.mockImpl(function(file, callback) { callback(null, '{"json":true}'); }); - var packager = new Packager({projectRoots: ['/root']}); + var assetServer = { + getAssetData: jest.genMockFn(), + }; + + var packager = new Packager({ + projectRoots: ['/root'], + assetServer: assetServer, + }); + var modules = [ {id: 'foo', path: '/root/foo.js', dependencies: []}, {id: 'bar', path: '/root/bar.js', dependencies: []}, @@ -97,6 +106,15 @@ describe('Packager', function() { cb(null, { width: 50, height: 100 }); }); + assetServer.getAssetData.mockImpl(function() { + return { + scales: [1,2,3], + hash: 'i am a hash', + name: 'img', + type: 'png', + }; + }); + return packager.package('/root/foo.js', true, 'source_map_url') .then(function(p) { expect(p.addModule.mock.calls[0]).toEqual([ @@ -111,6 +129,7 @@ describe('Packager', function() { ]); var imgModule_DEPRECATED = { + __packager_asset: true, isStatic: true, path: '/root/img/img.png', uri: 'img', @@ -130,11 +149,15 @@ describe('Packager', function() { ]); var imgModule = { - isStatic: true, - path: '/root/img/new_image.png', - uri: 'assets/img/new_image.png', + __packager_asset: true, + fileSystemLocation: '/root/img', + httpServerLocation: '/assets/img', width: 25, height: 50, + scales: [1, 2, 3], + hash: 'i am a hash', + name: 'img', + type: 'png', }; expect(p.addModule.mock.calls[3]).toEqual([ diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index c651dde78b8576..8563e27280f3c3 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -67,6 +67,10 @@ var validateOpts = declareOpts({ type: 'object', required: true, }, + assetServer: { + type: 'object', + required: true, + } }); function Packager(options) { @@ -94,6 +98,7 @@ function Packager(options) { }); this._projectRoots = opts.projectRoots; + this._assetServer = opts.assetServer; } Packager.prototype.kill = function() { @@ -173,6 +178,7 @@ Packager.prototype.getGraphDebugInfo = function() { Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) { return sizeOf(module.path).then(function(dimensions) { var img = { + __packager_asset: true, isStatic: true, path: module.path, uri: module.id.replace(/^[^!]+!/, ''), @@ -196,13 +202,20 @@ Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) { Packager.prototype.generateAssetModule = function(ppackage, module) { var relPath = getPathRelativeToRoot(this._projectRoots, module.path); - return sizeOf(module.path).then(function(dimensions) { + return Promise.all([ + sizeOf(module.path), + this._assetServer.getAssetData(relPath), + ]).spread(function(dimensions, assetData) { var img = { - isStatic: true, - path: module.path, //TODO(amasad): this should be path inside tar file. - uri: path.join('assets', relPath), + __packager_asset: true, + fileSystemLocation: path.dirname(module.path), + httpServerLocation: path.join('/assets', path.dirname(relPath)), width: dimensions.width / module.resolution, height: dimensions.height / module.resolution, + scales: assetData.scales, + hash: assetData.hash, + name: assetData.name, + type: assetData.type, }; ppackage.addAsset(img); diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index 525636cb532de4..79022b21181166 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -100,15 +100,16 @@ function Server(options) { ? FileWatcher.createDummyWatcher() : new FileWatcher(watchRootConfigs); - var packagerOpts = Object.create(opts); - packagerOpts.fileWatcher = this._fileWatcher; - this._packager = new Packager(packagerOpts); - this._assetServer = new AssetServer({ projectRoots: opts.projectRoots, assetExts: opts.assetExts, }); + var packagerOpts = Object.create(opts); + packagerOpts.fileWatcher = this._fileWatcher; + packagerOpts.assetServer = this._assetServer; + this._packager = new Packager(packagerOpts); + var onFileChange = this._onFileChange.bind(this); this._fileWatcher.on('all', onFileChange); diff --git a/packager/react-packager/src/__mocks__/fs.js b/packager/react-packager/src/__mocks__/fs.js index 0ea13d15d04688..d0e08a2f4baacf 100644 --- a/packager/react-packager/src/__mocks__/fs.js +++ b/packager/react-packager/src/__mocks__/fs.js @@ -67,6 +67,12 @@ fs.lstat.mockImpl(function(filepath, callback) { return callback(e); } + var mtime = { + getTime: function() { + return Math.ceil(Math.random() * 10000000); + } + }; + if (node && typeof node === 'object' && node.SYMLINK == null) { callback(null, { isDirectory: function() { @@ -74,7 +80,8 @@ fs.lstat.mockImpl(function(filepath, callback) { }, isSymbolicLink: function() { return false; - } + }, + mtime: mtime, }); } else { callback(null, { @@ -86,7 +93,8 @@ fs.lstat.mockImpl(function(filepath, callback) { return true; } return false; - } + }, + mtime: mtime, }); } }); diff --git a/packager/react-packager/src/lib/__tests__/extractAssetResolution-test.js b/packager/react-packager/src/lib/__tests__/extractAssetResolution-test.js index ad5ac3fbfe5edd..d0309ca6a30335 100644 --- a/packager/react-packager/src/lib/__tests__/extractAssetResolution-test.js +++ b/packager/react-packager/src/lib/__tests__/extractAssetResolution-test.js @@ -1,42 +1,52 @@ 'use strict'; jest.autoMockOff(); -var extractAssetResolution = require('../extractAssetResolution'); +var getAssetDataFromName = require('../getAssetDataFromName'); -describe('extractAssetResolution', function() { +describe('getAssetDataFromName', function() { it('should extract resolution simple case', function() { - var data = extractAssetResolution('test@2x.png'); + var data = getAssetDataFromName('test@2x.png'); expect(data).toEqual({ assetName: 'test.png', resolution: 2, + type: 'png', + name: 'test', }); }); it('should default resolution to 1', function() { - var data = extractAssetResolution('test.png'); + var data = getAssetDataFromName('test.png'); expect(data).toEqual({ assetName: 'test.png', resolution: 1, + type: 'png', + name: 'test', }); }); it('should support float', function() { - var data = extractAssetResolution('test@1.1x.png'); + var data = getAssetDataFromName('test@1.1x.png'); expect(data).toEqual({ assetName: 'test.png', resolution: 1.1, + type: 'png', + name: 'test', }); - data = extractAssetResolution('test@.1x.png'); + data = getAssetDataFromName('test@.1x.png'); expect(data).toEqual({ assetName: 'test.png', resolution: 0.1, + type: 'png', + name: 'test', }); - data = extractAssetResolution('test@0.2x.png'); + data = getAssetDataFromName('test@0.2x.png'); expect(data).toEqual({ assetName: 'test.png', resolution: 0.2, + type: 'png', + name: 'test', }); }); }); diff --git a/packager/react-packager/src/lib/extractAssetResolution.js b/packager/react-packager/src/lib/getAssetDataFromName.js similarity index 63% rename from packager/react-packager/src/lib/extractAssetResolution.js rename to packager/react-packager/src/lib/getAssetDataFromName.js index 8fb91afc4ebcff..c4848fd179b020 100644 --- a/packager/react-packager/src/lib/extractAssetResolution.js +++ b/packager/react-packager/src/lib/getAssetDataFromName.js @@ -2,7 +2,7 @@ var path = require('path'); -function extractAssetResolution(filename) { +function getAssetDataFromName(filename) { var ext = path.extname(filename); var re = new RegExp('@([\\d\\.]+)x\\' + ext + '$'); @@ -19,10 +19,13 @@ function extractAssetResolution(filename) { } } + var assetName = match ? filename.replace(re, ext) : filename; return { resolution: resolution, - assetName: match ? filename.replace(re, ext) : filename, + assetName: assetName, + type: ext.slice(1), + name: path.basename(assetName, ext) }; } -module.exports = extractAssetResolution; +module.exports = getAssetDataFromName; From fc6e209223fb80316e5e2cd1dd654af6ae0eb273 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 22 Apr 2015 13:23:48 -0700 Subject: [PATCH 063/250] Fixed broken struct arguments --- Libraries/Geolocation/RCTLocationObserver.m | 7 ++++--- React/Base/RCTBridge.m | 9 ++++++--- React/Modules/RCTUIManager.m | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index 3e864657b82c5d..5d56caccbe314b 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -163,12 +163,12 @@ - (void)timeout:(NSTimer *)timer #pragma mark - Public API -RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options) +RCT_EXPORT_METHOD(startObserving:(NSDictionary *)optionsJSON) { [self checkLocationConfig]; // Select best options - _observerOptions = options; + _observerOptions = [RCTConvert RCTLocationOptions:optionsJSON]; for (RCTLocationRequest *request in _pendingRequests) { _observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy); } @@ -189,7 +189,7 @@ - (void)timeout:(NSTimer *)timer } } -RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options +RCT_EXPORT_METHOD(getCurrentPosition:(NSDictionary *)optionsJSON withSuccessCallback:(RCTResponseSenderBlock)successBlock errorCallback:(RCTResponseSenderBlock)errorBlock) { @@ -219,6 +219,7 @@ - (void)timeout:(NSTimer *)timer } // Check if previous recorded location exists and is good enough + RCTLocationOptions options = [RCTConvert RCTLocationOptions:optionsJSON]; if (_lastLocationEvent && CFAbsoluteTimeGetCurrent() - [RCTConvert NSTimeInterval:_lastLocationEvent[@"timestamp"]] < options.maximumAge && [_lastLocationEvent[@"coords"][@"accuracy"] doubleValue] >= options.accuracy) { diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index a4a4362b0323b2..48fd672a3926d4 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -375,10 +375,14 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName RCT_CONVERT_CASE('B', BOOL) RCT_CONVERT_CASE('@', id) RCT_CONVERT_CASE('^', void *) - + case '{': + RCTAssert(NO, @"Argument %zd of %C[%@ %@] is defined as %@, however RCT_EXPORT_METHOD() " + "does not currently support struct-type arguments.", i - 2, + [reactMethodName characterAtIndex:0], _moduleClassName, + objCMethodName, argumentName); + break; default: defaultCase(argumentType); - break; } } else if ([argumentName isEqualToString:@"RCTResponseSenderBlock"]) { addBlockArgument(); @@ -434,7 +438,6 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName default: defaultCase(argumentType); - break; } } } diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index b7ae182bad84f9..451a343d04960d 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1001,11 +1001,12 @@ static void RCTMeasureLayout(RCTShadowView *view, * Only layouts for views that are within the rect passed in are returned. Invokes the error callback if the * passed in parent view does not exist. Invokes the supplied callback with the array of computed layouts. */ -RCT_EXPORT_METHOD(measureViewsInRect:(CGRect)rect +RCT_EXPORT_METHOD(measureViewsInRect:(id)rectJSON parentView:(NSNumber *)reactTag errorCallback:(RCTResponseSenderBlock)errorCallback callback:(RCTResponseSenderBlock)callback) { + CGRect rect = [RCTConvert CGRect:rectJSON]; RCTShadowView *shadowView = _shadowViewRegistry[reactTag]; if (!shadowView) { RCTLogError(@"Attempting to measure view that does not exist (tag #%@)", reactTag); @@ -1101,8 +1102,9 @@ static void RCTMeasureLayout(RCTShadowView *view, } RCT_EXPORT_METHOD(zoomToRect:(NSNumber *)reactTag - withRect:(CGRect)rect) + withRect:(id)rectJSON) { + CGRect rect = [RCTConvert CGRect:rectJSON]; [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ UIView *view = viewRegistry[reactTag]; if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) { From c5ea25f7fb9cc162552c71c644d7bf8bd6954024 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 22 Apr 2015 13:11:30 -0700 Subject: [PATCH 064/250] [ReactNative] Adopt client asset managing code to server changes --- Libraries/Image/Image.ios.js | 4 +- .../__tests__/resolveAssetSource-test.js | 67 +++++++++++++------ Libraries/Image/resolveAssetSource.js | 63 ++++++++++------- 3 files changed, 88 insertions(+), 46 deletions(-) diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index be95a3f3ff91f0..e917b6b637355b 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -123,15 +123,13 @@ var Image = React.createClass({ 'not be set directly on Image.'); } } - var source = this.props.source; + var source = resolveAssetSource(this.props.source); invariant(source, 'source must be initialized'); var {width, height} = source; var style = flattenStyle([{width, height}, styles.base, this.props.style]); invariant(style, 'style must be initialized'); - source = resolveAssetSource(source); - var isNetwork = source.uri && source.uri.match(/^https?:/); invariant( !(isNetwork && source.isStatic), diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js index 69bcb116a917cf..b385e29aa8d692 100644 --- a/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -13,6 +13,10 @@ jest.dontMock('../resolveAssetSource'); var resolveAssetSource; var SourceCode; +function expectResolvesAsset(input, expectedSource) { + expect(resolveAssetSource(input)).toEqual(expectedSource); +} + describe('resolveAssetSource', () => { beforeEach(() => { jest.resetModuleRegistry(); @@ -34,41 +38,66 @@ describe('resolveAssetSource', () => { }); it('uses network image', () => { - var source = { - path: '/Users/react/project/logo.png', - uri: 'assets/logo.png', - }; - expect(resolveAssetSource(source)).toEqual({ + expectResolvesAsset({ + __packager_asset: true, + fileSystemLocation: '/root/app/module/a', + httpServerLocation: '/assets/module/a', + width: 100, + height: 200, + scales: [1], + hash: '5b6f00f', + name: 'logo', + type: 'png', + }, { isStatic: false, - uri: 'http://10.0.0.1:8081/assets/logo.png', + width: 100, + height: 200, + uri: 'http://10.0.0.1:8081/assets/module/a/logo.png?hash=5b6f00f', }); }); it('does not change deprecated assets', () => { - // Deprecated require('image!logo') should stay unchanged - var source = { - path: '/Users/react/project/logo.png', - uri: 'logo', + expectResolvesAsset({ + __packager_asset: true, deprecated: true, - }; - expect(resolveAssetSource(source)).toEqual({ + fileSystemLocation: '/root/app/module/a', + httpServerLocation: '/assets/module/a', + width: 100, + height: 200, + scales: [1], + hash: '5b6f00f', + name: 'logo', + type: 'png', + }, { isStatic: true, + width: 100, + height: 200, uri: 'logo', }); }); }); describe('bundle was loaded from file', () => { - it('uses pre-packed image', () => { + beforeEach(() => { SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle'; + }); - var source = { - path: '/Users/react/project/logo.png', - uri: 'assets/logo.png', - }; - expect(resolveAssetSource(source)).toEqual({ + it('uses pre-packed image', () => { + expectResolvesAsset({ + __packager_asset: true, + fileSystemLocation: '/root/app/module/a', + httpServerLocation: '/assets/module/a', + width: 100, + height: 200, + scales: [1], + hash: '5b6f00f', + name: 'logo', + type: 'png', + }, { isStatic: true, - uri: 'assets/logo.png', + width: 100, + height: 200, + uri: 'assets/module/a/logo.png', }); }); }); diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js index 137f92f1c17c7d..2325e2c8c3b13a 100644 --- a/Libraries/Image/resolveAssetSource.js +++ b/Libraries/Image/resolveAssetSource.js @@ -17,9 +17,9 @@ var _serverURL; function getServerURL() { if (_serverURL === undefined) { var scriptURL = SourceCode.scriptURL; - var serverURLMatch = scriptURL && scriptURL.match(/^https?:\/\/.*?\//); - if (serverURLMatch) { - _serverURL = serverURLMatch[0]; + var match = scriptURL && scriptURL.match(/^https?:\/\/.*?\//); + if (match) { + _serverURL = match[0]; } else { _serverURL = null; } @@ -29,35 +29,50 @@ function getServerURL() { } // TODO(frantic): -// * Use something other than `path`/`isStatic` for asset identification, `__packager_asset`? -// * Add cache invalidating hashsum -// * Move code that selects scale to client +// * Pick best scale and append @Nx to file path +// * We are currently using httpServerLocation for both http and in-app bundle function resolveAssetSource(source) { + if (!source.__packager_asset) { + return source; + } + + // Deprecated assets are managed by Xcode for now, + // just returning image name as `uri` + // Examples: + // require('image!deprecatd_logo_example') + // require('./new-hotness-logo-example.png') if (source.deprecated) { return { - ...source, - path: undefined, + width: source.width, + height: source.height, isStatic: true, - deprecated: undefined, + uri: source.name || source.uri, // TODO(frantic): remove uri }; } + // TODO(frantic): currently httpServerLocation is used both as + // path in http URL and path within IPA. Should we have zipArchiveLocation? + var path = source.httpServerLocation; + if (path[0] === '/') { + path = path.substr(1); + } + var serverURL = getServerURL(); - if (source.path) { - if (serverURL) { - return { - ...source, - path: undefined, - uri: serverURL + source.uri, - isStatic: false, - }; - } else { - return { - ...source, - path: undefined, - isStatic: true, - }; - } + if (serverURL) { + return { + width: source.width, + height: source.height, + uri: serverURL + path + '/' + source.name + '.' + source.type + + '?hash=' + source.hash, + isStatic: false, + }; + } else { + return { + width: source.width, + height: source.height, + uri: path + '/' + source.name + '.' + source.type, + isStatic: true, + }; } return source; From e63bfae8f648e3136e713736a6d12e1ccc43b8d3 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Wed, 22 Apr 2015 15:43:11 -0700 Subject: [PATCH 065/250] [ReactNative] console.error shows RedBox with pretty stack trace --- Libraries/AppRegistry/AppRegistry.js | 2 +- .../Initialization/ExceptionsManager.js | 28 +++++++++++-------- .../InitializeJavaScriptAppEngine.js | 3 +- React/Executors/RCTContextExecutor.m | 17 +++++------ .../haste/polyfills/console.js | 18 ++++++++---- 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/Libraries/AppRegistry/AppRegistry.js b/Libraries/AppRegistry/AppRegistry.js index f36f881323adce..63bd1d9a0bd3f4 100644 --- a/Libraries/AppRegistry/AppRegistry.js +++ b/Libraries/AppRegistry/AppRegistry.js @@ -69,7 +69,7 @@ var AppRegistry = { 'Running application "' + appKey + '" with appParams: ' + JSON.stringify(appParameters) + '. ' + '__DEV__ === ' + __DEV__ + - ', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') + + ', development-level warnings are ' + (__DEV__ ? 'ON' : 'OFF') + ', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON') ); invariant( diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index c5476eaab5cfe3..fb57a956a60000 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -25,17 +25,11 @@ type Exception = { message: string; } -function handleException(e: Exception) { - var stack = parseErrorStack(e); - console.error( - 'Err0r: ' + - '\n stack: \n' + stackToString(stack) + - '\n URL: ' + e.sourceURL + - '\n line: ' + e.line + - '\n message: ' + e.message - ); - +function reportException(e: Exception, stack?: any) { if (RCTExceptionsManager) { + if (!stack) { + stack = parseErrorStack(e); + } RCTExceptionsManager.reportUnhandledException(e.message, stack); if (__DEV__) { (sourceMapPromise = sourceMapPromise || loadSourceMap()) @@ -50,6 +44,18 @@ function handleException(e: Exception) { } } +function handleException(e: Exception) { + var stack = parseErrorStack(e); + console.log( + 'Error: ' + + '\n stack: \n' + stackToString(stack) + + '\n URL: ' + e.sourceURL + + '\n line: ' + e.line + + '\n message: ' + e.message + ); + reportException(e, stack); +} + function stackToString(stack) { var maxLength = Math.max.apply(null, stack.map(frame => frame.methodName.length)); return stack.map(frame => stackFrameToString(frame, maxLength)).join('\n'); @@ -71,4 +77,4 @@ function fillSpaces(n) { return new Array(n + 1).join(' '); } -module.exports = { handleException }; +module.exports = { handleException, reportException }; diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index 51f6809ccd9b28..bbfee4eb39a37f 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -77,6 +77,7 @@ function handleErrorWithRedBox(e) { function setupRedBoxErrorHandler() { var ErrorUtils = require('ErrorUtils'); ErrorUtils.setGlobalHandler(handleErrorWithRedBox); + GLOBAL.reportException = require('ExceptionsManager').reportException; } /** @@ -134,8 +135,8 @@ function setupGeolocation() { } setupDocumentShim(); -setupRedBoxErrorHandler(); setupTimers(); +setupRedBoxErrorHandler(); // needs to happen after setupTimers setupAlert(); setupPromise(); setupXHR(); diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 86444dd2a7f903..8f931d96ab3b3f 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -74,12 +74,12 @@ @implementation RCTContextExecutor static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { if (argumentCount > 0) { - JSStringRef string = JSValueToStringCopy(context, arguments[0], exception); - if (!string) { + JSStringRef messageRef = JSValueToStringCopy(context, arguments[0], exception); + if (!messageRef) { return JSValueMakeUndefined(context); } - NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string); - JSStringRelease(string); + NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, messageRef); + JSStringRelease(messageRef); NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: @"( stack: )?([_a-z0-9]*)@?(http://|file:///)[a-z.0-9:/_-]+/([a-z0-9_]+).includeRequire.runModule.bundle(:[0-9]+:[0-9]+)" options:NSRegularExpressionCaseInsensitive @@ -89,14 +89,11 @@ static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, range:(NSRange){0, message.length} withTemplate:@"[$4$5] \t$2"]; - // TODO: it would be good if log level was sent as a param, instead of this hack RCTLogLevel level = RCTLogLevelInfo; - if ([message rangeOfString:@"error" options:NSCaseInsensitiveSearch].length) { - level = RCTLogLevelError; - } else if ([message rangeOfString:@"warning" options:NSCaseInsensitiveSearch].length) { - level = RCTLogLevelWarning; + if (argumentCount > 1) { + level = MAX(level, JSValueToNumber(context, arguments[1], exception) - 1); } - _RCTLogFormat(level, NULL, -1, @"%@", message); + RCTGetLogFunction()(level, nil, nil, message); } return JSValueMakeUndefined(context); diff --git a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js index 91fb970f885521..4f513f45b692d1 100644 --- a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js +++ b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js @@ -23,7 +23,7 @@ log: 1, info: 2, warn: 3, - error: 4 + error: 4, }; function setupConsole(global) { @@ -35,8 +35,10 @@ function getNativeLogFunction(level) { return function() { var str = Array.prototype.map.call(arguments, function(arg) { - if (arg == null) { - return arg === null ? 'null' : 'undefined'; + if (arg === undefined) { + return 'undefined'; + } else if (arg === null) { + return 'null'; } else if (typeof arg === 'string') { return '"' + arg + '"'; } else { @@ -48,14 +50,18 @@ if (typeof arg.toString === 'function') { try { return arg.toString(); - } catch (E) { - return 'unknown'; - } + } catch (E) {} } + return '["' + typeof arg + '" failed to stringify]'; } } }).join(', '); global.nativeLoggingHook(str, level); + if (global.reportException && level === LOG_LEVELS.error) { + var error = new Error(str); + error.framesToPop = 1; + global.reportException(error); + } }; } From 27252e611c0f7b0c82df79ec67e7b31be502e93d Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Wed, 22 Apr 2015 16:16:48 -0700 Subject: [PATCH 066/250] [FBRhinos] add sms cmd for device configuration --- React/Base/RCTBridge.h | 2 +- React/Base/RCTJavaScriptLoader.m | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index a37b9ac7db946f..6dfbe2414557f8 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -92,7 +92,7 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method; /** * URL of the script that was loaded into the bridge. */ -@property (nonatomic, copy, readonly) NSURL *bundleURL; +@property (nonatomic, copy) NSURL *bundleURL; @property (nonatomic, strong) Class executorClass; diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index 4eaaf278dd480e..dd8fab461868fd 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -85,8 +85,9 @@ - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onCom // Handle general request errors if (error) { if ([[error domain] isEqualToString:NSURLErrorDomain]) { + NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:[scriptURL absoluteString]]; NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: @"Could not connect to development server. Ensure node server is running - run 'npm start' from ReactKit root", + NSLocalizedDescriptionKey: desc, NSLocalizedFailureReasonErrorKey: [error localizedDescription], NSUnderlyingErrorKey: error, }; From af61b13b9eef968898955b01d4b02915aa7a3667 Mon Sep 17 00:00:00 2001 From: James Ide Date: Wed, 22 Apr 2015 16:12:46 -0700 Subject: [PATCH 067/250] [RootView] Fix positioning of the root view content (frame -> bounds) Summary: The root view's content was being rendered at the wrong offset when it was not positioned at (0, 0) exactly, because the shadow view's frame was set to the root view's frame when it should have been set to the root view's bounds instead. Closes https://github.com/facebook/react-native/pull/963 Github Author: James Ide Test Plan: Render a root view positioned at (0, 100) and see that its content is positioned where the root view is, not at (0, 200). --- React/Base/RCTRootView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 24cfe8417101bd..45624efdcc1e8a 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -149,7 +149,7 @@ - (void)layoutSubviews [super layoutSubviews]; if (_contentView) { _contentView.frame = self.bounds; - [_bridge.uiManager setFrame:self.frame forRootView:_contentView]; + [_bridge.uiManager setFrame:self.bounds forRootView:_contentView]; } } From b2e8dc983486f1f7502f137082b78381b63c1545 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 22 Apr 2015 16:30:27 -0700 Subject: [PATCH 068/250] [ReactNative] Backport packager logs redirect --- packager/packager.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packager/packager.sh b/packager/packager.sh index 93a017c358c0ac..f763b9bae2faf4 100755 --- a/packager/packager.sh +++ b/packager/packager.sh @@ -7,6 +7,12 @@ # LICENSE file in the root directory of this source tree. An additional grant # of patent rights can be found in the PATENTS file in the same directory. +if [ $REACT_PACKAGER_LOG ]; +then + echo "Logs will be redirected to $REACT_PACKAGER_LOG" + exec &> $REACT_PACKAGER_LOG +fi + ulimit -n 4096 THIS_DIR=$(dirname "$0") From ffb3026419561d0cc7518ea2f8e9c0b675a9ff23 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 22 Apr 2015 16:31:13 -0700 Subject: [PATCH 069/250] [ReactNative] Pick correct assets depending on device scale --- .../__tests__/resolveAssetSource-test.js | 30 +++++++++++++++++++ Libraries/Image/resolveAssetSource.js | 24 +++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js index b385e29aa8d692..26f3c9ea32e999 100644 --- a/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -56,6 +56,25 @@ describe('resolveAssetSource', () => { }); }); + it('picks matching scale', () => { + expectResolvesAsset({ + __packager_asset: true, + fileSystemLocation: '/root/app/module/a', + httpServerLocation: '/assets/module/a', + width: 100, + height: 200, + scales: [1, 2, 3], + hash: '5b6f00f', + name: 'logo', + type: 'png', + }, { + isStatic: false, + width: 100, + height: 200, + uri: 'http://10.0.0.1:8081/assets/module/a/logo@2x.png?hash=5b6f00f', + }); + }); + it('does not change deprecated assets', () => { expectResolvesAsset({ __packager_asset: true, @@ -103,3 +122,14 @@ describe('resolveAssetSource', () => { }); }); + +describe('resolveAssetSource.pickScale', () => { + it('picks matching scale', () => { + expect(resolveAssetSource.pickScale([1], 2)).toBe(1); + expect(resolveAssetSource.pickScale([1, 2, 3], 2)).toBe(2); + expect(resolveAssetSource.pickScale([1, 2], 3)).toBe(2); + expect(resolveAssetSource.pickScale([1, 2, 3, 4], 3.5)).toBe(4); + expect(resolveAssetSource.pickScale([3, 4], 2)).toBe(3); + expect(resolveAssetSource.pickScale([], 2)).toBe(1); + }); +}); diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js index 2325e2c8c3b13a..da136e9a73d583 100644 --- a/Libraries/Image/resolveAssetSource.js +++ b/Libraries/Image/resolveAssetSource.js @@ -10,6 +10,7 @@ */ 'use strict'; +var PixelRatio = require('PixelRatio'); var SourceCode = require('NativeModules').SourceCode; var _serverURL; @@ -28,6 +29,20 @@ function getServerURL() { return _serverURL; } +function pickScale(scales, deviceScale) { + // Packager guarantees that `scales` array is sorted + for (var i = 0; i < scales.length; i++) { + if (scales[i] >= deviceScale) { + return scales[i]; + } + } + + // If nothing matches, device scale is larger than any available + // scales, so we return the biggest one. Unless the array is empty, + // in which case we default to 1 + return scales[scales.length - 1] || 1; +} + // TODO(frantic): // * Pick best scale and append @Nx to file path // * We are currently using httpServerLocation for both http and in-app bundle @@ -57,12 +72,16 @@ function resolveAssetSource(source) { path = path.substr(1); } + var scale = pickScale(source.scales, PixelRatio.get()); + var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x'; + + var fileName = source.name + scaleSuffix + '.' + source.type; var serverURL = getServerURL(); if (serverURL) { return { width: source.width, height: source.height, - uri: serverURL + path + '/' + source.name + '.' + source.type + + uri: serverURL + path + '/' + fileName + '?hash=' + source.hash, isStatic: false, }; @@ -70,7 +89,7 @@ function resolveAssetSource(source) { return { width: source.width, height: source.height, - uri: path + '/' + source.name + '.' + source.type, + uri: path + '/' + fileName, isStatic: true, }; } @@ -79,3 +98,4 @@ function resolveAssetSource(source) { } module.exports = resolveAssetSource; +module.exports.pickScale = pickScale; From 2bda21fbf0e6cd5de0619b776bdaf30ec42dd6c3 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Wed, 22 Apr 2015 17:50:54 -0700 Subject: [PATCH 070/250] [ReactNative] Flush ReactUpdates only once per batch --- Libraries/Utilities/MessageQueue.js | 32 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/Libraries/Utilities/MessageQueue.js b/Libraries/Utilities/MessageQueue.js index c047d06deecf93..576aaa9917bba2 100644 --- a/Libraries/Utilities/MessageQueue.js +++ b/Libraries/Utilities/MessageQueue.js @@ -10,7 +10,9 @@ * @flow */ 'use strict'; + var ErrorUtils = require('ErrorUtils'); +var ReactUpdates = require('ReactUpdates'); var invariant = require('invariant'); var warning = require('warning'); @@ -307,21 +309,23 @@ var MessageQueueMixin = { ); }, - processBatch: function (batch) { + processBatch: function(batch) { var self = this; - batch.forEach(function (call) { - invariant( - call.module === 'BatchedBridge', - 'All the calls should pass through the BatchedBridge module' - ); - if (call.method === 'callFunctionReturnFlushedQueue') { - self.callFunction.apply(self, call.args); - } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { - self.invokeCallback.apply(self, call.args); - } else { - throw new Error( - 'Unrecognized method called on BatchedBridge: ' + call.method); - } + ReactUpdates.batchedUpdates(function() { + batch.forEach(function(call) { + invariant( + call.module === 'BatchedBridge', + 'All the calls should pass through the BatchedBridge module' + ); + if (call.method === 'callFunctionReturnFlushedQueue') { + self.callFunction.apply(self, call.args); + } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { + self.invokeCallback.apply(self, call.args); + } else { + throw new Error( + 'Unrecognized method called on BatchedBridge: ' + call.method); + } + }); }); return this.flushedQueue(); }, From 621a30c8b80d8f52eaac9065b0f9b2171919885c Mon Sep 17 00:00:00 2001 From: "Dr. Kibitz" Date: Thu, 23 Apr 2015 04:13:30 -0700 Subject: [PATCH 071/250] Fixes #813 Summary: Also fix RCTShadowText export name. Closes https://github.com/facebook/react-native/pull/857 Github Author: "Dr. Kibitz" Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Text/RCTShadowText.m | 8 +++++++- Libraries/Text/RCTTextManager.m | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m index 4201b1b4e8d0dc..4ea9d3bd6fc093 100644 --- a/Libraries/Text/RCTShadowText.m +++ b/Libraries/Text/RCTShadowText.m @@ -22,15 +22,21 @@ static css_dim_t RCTMeasure(void *context, float width) RCTShadowText *shadowText = (__bridge RCTShadowText *)context; NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[shadowText attributedString]]; + NSTextStorage *previousTextStorage = shadowText.layoutManager.textStorage; + if (previousTextStorage) { + [previousTextStorage removeLayoutManager:shadowText.layoutManager]; + } [textStorage addLayoutManager:shadowText.layoutManager]; shadowText.textContainer.size = CGSizeMake(isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX); - shadowText.layoutManager.textStorage = textStorage; [shadowText.layoutManager ensureLayoutForTextContainer:shadowText.textContainer]; CGSize computedSize = [shadowText.layoutManager usedRectForTextContainer:shadowText.textContainer].size; [textStorage removeLayoutManager:shadowText.layoutManager]; + if (previousTextStorage) { + [previousTextStorage addLayoutManager:shadowText.layoutManager]; + } css_dim_t result; result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width); diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m index b8dabcf2c34f1c..e5e9ad00a1ad23 100644 --- a/Libraries/Text/RCTTextManager.m +++ b/Libraries/Text/RCTTextManager.m @@ -46,7 +46,7 @@ - (RCTShadowView *)shadowView RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString) RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL) RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat) -RCT_EXPORT_SHADOW_PROPERTY(maxNumberOfLines, NSInteger) +RCT_EXPORT_SHADOW_PROPERTY(maximumNumberOfLines, NSInteger) RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize) RCT_EXPORT_SHADOW_PROPERTY(textAlign, NSTextAlignment) RCT_REMAP_SHADOW_PROPERTY(backgroundColor, textBackgroundColor, UIColor) From c219f618186ba274504493a537b7844e069a81c4 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 23 Apr 2015 05:42:54 -0700 Subject: [PATCH 072/250] Add minimumTrackTintColor and maximumTrackTintColor to SliderIOS Summary: There are still many other props that can be added to further customize the SliderIOS component, but I had a specific need for these two so I just went ahead and added them. Closes https://github.com/facebook/react-native/pull/799 Github Author: Brent Vatne Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Components/SliderIOS/SliderIOS.js | 14 ++++++++++++++ React/Views/RCTSliderManager.m | 2 ++ 2 files changed, 16 insertions(+) diff --git a/Libraries/Components/SliderIOS/SliderIOS.js b/Libraries/Components/SliderIOS/SliderIOS.js index 0c2d02da04f4ac..bfb2b92714c768 100644 --- a/Libraries/Components/SliderIOS/SliderIOS.js +++ b/Libraries/Components/SliderIOS/SliderIOS.js @@ -51,6 +51,18 @@ var SliderIOS = React.createClass({ */ maximumValue: PropTypes.number, + /** + * The color used for the track to the left of the button. Overrides the + * default blue gradient image. + */ + minimumTrackTintColor: PropTypes.string, + + /** + * The color used for the track to the right of the button. Overrides the + * default blue gradient image. + */ + maximumTrackTintColor: PropTypes.string, + /** * Callback continuously called while the user is dragging the slider. */ @@ -81,6 +93,8 @@ var SliderIOS = React.createClass({ value={this.props.value} maximumValue={this.props.maximumValue} minimumValue={this.props.minimumValue} + minimumTrackTintColor={this.props.minimumTrackTintColor} + maximumTrackTintColor={this.props.maximumTrackTintColor} onChange={this._onValueChange} /> ); diff --git a/React/Views/RCTSliderManager.m b/React/Views/RCTSliderManager.m index a103da98d7f46a..f57e1f36209a2a 100644 --- a/React/Views/RCTSliderManager.m +++ b/React/Views/RCTSliderManager.m @@ -50,5 +50,7 @@ - (void)sliderTouchEnd:(UISlider *)sender RCT_EXPORT_VIEW_PROPERTY(value, float); RCT_EXPORT_VIEW_PROPERTY(minimumValue, float); RCT_EXPORT_VIEW_PROPERTY(maximumValue, float); +RCT_EXPORT_VIEW_PROPERTY(minimumTrackTintColor, UIColor); +RCT_EXPORT_VIEW_PROPERTY(maximumTrackTintColor, UIColor); @end From a36da5130bbf023ab3a8ece77a81c7a5298541e7 Mon Sep 17 00:00:00 2001 From: Ivan Starkov Date: Thu, 23 Apr 2015 06:10:36 -0700 Subject: [PATCH 073/250] Add Linear easing to AnimationUtils Summary: Simple linear easing also very useful. Closes https://github.com/facebook/react-native/pull/794 Github Author: Ivan Starkov Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Animation/AnimationUtils.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Libraries/Animation/AnimationUtils.js b/Libraries/Animation/AnimationUtils.js index d6d95f62de248b..ac62465a02e219 100644 --- a/Libraries/Animation/AnimationUtils.js +++ b/Libraries/Animation/AnimationUtils.js @@ -20,6 +20,9 @@ type EasingFunction = (t: number) => number; var defaults = { + linear: function(t: number): number { + return t; + }, easeInQuad: function(t: number): number { return t * t; }, From aa3d3435474ef88b7bdd728ad5f9df0f60fd83ee Mon Sep 17 00:00:00 2001 From: Eduardo Date: Thu, 23 Apr 2015 06:22:16 -0700 Subject: [PATCH 074/250] NavigatorIOS custom nav bar colors Summary: NavigatorIOS with custom nav bar custom colors, working example included in UIExplorer. Based on pull request #318 to complete pending work based on comments. Closes https://github.com/facebook/react-native/pull/695 Github Author: Eduardo Test Plan: Imported from GitHub, without a `Test Plan:` line. --- .../UIExplorer/Navigator/NavigatorExample.js | 2 + .../UIExplorer/NavigatorIOSColorsExample.js | 90 +++++++++++++++++++ Examples/UIExplorer/UIExplorerList.js | 10 +-- .../Components/Navigation/NavigatorIOS.ios.js | 14 ++- React/Views/RCTWrapperViewController.m | 5 +- 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 Examples/UIExplorer/NavigatorIOSColorsExample.js diff --git a/Examples/UIExplorer/Navigator/NavigatorExample.js b/Examples/UIExplorer/Navigator/NavigatorExample.js index ef8001f94c6ee7..4a182a5a40b8cc 100644 --- a/Examples/UIExplorer/Navigator/NavigatorExample.js +++ b/Examples/UIExplorer/Navigator/NavigatorExample.js @@ -175,4 +175,6 @@ var styles = StyleSheet.create({ } }); +TabBarExample.external = true; + module.exports = TabBarExample; diff --git a/Examples/UIExplorer/NavigatorIOSColorsExample.js b/Examples/UIExplorer/NavigatorIOSColorsExample.js new file mode 100644 index 00000000000000..386586f230bb89 --- /dev/null +++ b/Examples/UIExplorer/NavigatorIOSColorsExample.js @@ -0,0 +1,90 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +'use strict'; + +var React = require('react-native'); +var { + NavigatorIOS, + StatusBarIOS, + StyleSheet, + Text, + View +} = React; + +var EmptyPage = React.createClass({ + + render: function() { + return ( + + + {this.props.text} + + + ); + }, + +}); + +var NavigatorIOSColors = React.createClass({ + + statics: { + title: ' - Custom', + description: 'iOS navigation with custom nav bar colors', + }, + + render: function() { + // Set StatusBar with light contents to get better contrast + StatusBarIOS.setStyle(StatusBarIOS.Style['lightContent']); + + return ( + ', + rightButtonTitle: 'Done', + onRightButtonPress: () => { + StatusBarIOS.setStyle(StatusBarIOS.Style['default']); + this.props.onExampleExit(); + }, + passProps: { + text: 'The nav bar has custom colors with tintColor, ' + + 'barTintColor and titleTextColor props.', + }, + }} + tintColor="#FFFFFF" + barTintColor="#183E63" + titleTextColor="#FFFFFF" + /> + ); + }, + +}); + +var styles = StyleSheet.create({ + container: { + flex: 1, + }, + emptyPage: { + flex: 1, + paddingTop: 64, + }, + emptyPageText: { + margin: 10, + }, +}); + +NavigatorIOSColors.external = true; + +module.exports = NavigatorIOSColors; diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index 308249572da166..73010617732abd 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -26,7 +26,6 @@ var { TouchableHighlight, View, } = React; -var NavigatorExample = require('./Navigator/NavigatorExample'); var { TestModule } = React.addons; @@ -39,7 +38,8 @@ var COMPONENTS = [ require('./ListViewExample'), require('./ListViewPagingExample'), require('./MapViewExample'), - NavigatorExample, + require('./Navigator/NavigatorExample'), + require('./NavigatorIOSColorsExample'), require('./NavigatorIOSExample'), require('./PickerIOSExample'), require('./ScrollViewExample'), @@ -181,10 +181,8 @@ class UIExplorerList extends React.Component { } _onPressRow(example) { - if (example === NavigatorExample) { - this.props.onExternalExampleRequested( - NavigatorExample - ); + if (example.external) { + this.props.onExternalExampleRequested(example); return; } var Component = makeRenderable(example); diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index fd90847f05c61f..3babd140954201 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -252,6 +252,16 @@ var NavigatorIOS = React.createClass({ */ tintColor: PropTypes.string, + /** + * The background color of the navigation bar + */ + barTintColor: PropTypes.string, + + /** + * The text color of the navigation bar title + */ + titleTextColor: PropTypes.string, + }, navigator: (undefined: ?Object), @@ -554,7 +564,9 @@ var NavigatorIOS = React.createClass({ rightButtonTitle={route.rightButtonTitle} onNavRightButtonTap={route.onRightButtonPress} navigationBarHidden={this.props.navigationBarHidden} - tintColor={this.props.tintColor}> + tintColor={this.props.tintColor} + barTintColor={this.props.barTintColor} + titleTextColor={this.props.titleTextColor}> Date: Thu, 23 Apr 2015 08:04:16 -0700 Subject: [PATCH 075/250] Bump .buckversion to 6cdb82cb7493a86c39d0f0dc3c102d0f470f55de. --- Libraries/LinkingIOS/RCTLinkingManager.h | 2 +- .../PushNotificationIOS/RCTPushNotificationManager.h | 2 +- React/Base/RCTConvert.h | 7 +++---- React/Executors/RCTContextExecutor.h | 2 +- React/Modules/RCTUIManager.h | 8 ++++---- React/Views/RCTShadowView.h | 3 +-- React/Views/RCTViewManager.h | 6 +++--- 7 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Libraries/LinkingIOS/RCTLinkingManager.h b/Libraries/LinkingIOS/RCTLinkingManager.h index 81680f056a3844..caa3aa2a3a9264 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.h +++ b/Libraries/LinkingIOS/RCTLinkingManager.h @@ -9,7 +9,7 @@ #import -#import "../../React/Base/RCTBridgeModule.h" +#import "RCTBridgeModule.h" @interface RCTLinkingManager : NSObject diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index 4e791c68002c1e..b60a646e416f0f 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -9,7 +9,7 @@ #import -#import "../../React/Base/RCTBridgeModule.h" +#import "RCTBridgeModule.h" @interface RCTPushNotificationManager : NSObject diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 46bbbf8a4634c9..eb81c167c9aa02 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -10,12 +10,11 @@ #import #import -#import "../Layout/Layout.h" -#import "../Views/RCTAnimationType.h" -#import "../Views/RCTPointerEvents.h" - +#import "Layout.h" +#import "RCTAnimationType.h" #import "RCTDefines.h" #import "RCTLog.h" +#import "RCTPointerEvents.h" /** * This class provides a collection of conversion functions for mapping diff --git a/React/Executors/RCTContextExecutor.h b/React/Executors/RCTContextExecutor.h index 6e62d87b6db8c2..159965a2fbed01 100644 --- a/React/Executors/RCTContextExecutor.h +++ b/React/Executors/RCTContextExecutor.h @@ -9,7 +9,7 @@ #import -#import "../Base/RCTJavaScriptExecutor.h" +#import "RCTJavaScriptExecutor.h" // TODO (#5906496): Might RCTJSCoreExecutor be a better name for this? diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index 4f42cd0b7ff33d..6fa0e3249185d3 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -9,10 +9,10 @@ #import -#import "../Base/RCTBridge.h" -#import "../Base/RCTBridgeModule.h" -#import "../Base/RCTInvalidating.h" -#import "../Views/RCTViewManager.h" +#import "RCTBridge.h" +#import "RCTBridgeModule.h" +#import "RCTInvalidating.h" +#import "RCTViewManager.h" @protocol RCTScrollableProtocol; diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index 6efb0c1d118967..8d68855f7f186a 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -9,8 +9,7 @@ #import -#import "../Layout/Layout.h" - +#import "Layout.h" #import "RCTViewNodeProtocol.h" @class RCTSparseArray; diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index 3788832ef5857d..77dc6669708900 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -9,9 +9,9 @@ #import -#import "../Base/RCTBridgeModule.h" -#import "../Base/RCTConvert.h" -#import "../Base/RCTLog.h" +#import "RCTBridgeModule.h" +#import "RCTConvert.h" +#import "RCTLog.h" @class RCTBridge; @class RCTEventDispatcher; From b72acc2313db90b414e53b4ede533071284de4c2 Mon Sep 17 00:00:00 2001 From: Robert Payne Date: Thu, 23 Apr 2015 09:28:09 -0700 Subject: [PATCH 076/250] Add support for exporting Swift modules Summary: External modules are any Objective-C class in which the implementation is private. This currently will be most useful for Swift classes but also has potential to allow exposing methods on 3rd party libraries to the bridge. Closes https://github.com/facebook/react-native/pull/982 Github Author: Robert Payne Test Plan: Imported from GitHub, without a `Test Plan:` line. --- React/Base/RCTBridgeModule.h | 59 ++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index 12f7803ea73108..dd6f61e235e279 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -73,12 +73,67 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response); * { ... } */ #define RCT_REMAP_METHOD(js_name, method) \ + RCT_EXTERN_REMAP_METHOD(js_name, method) \ + - (void)method + +/** + * Use this macro in a private Objective-C implementation file to automatically + * register an external module with the bridge when it loads. This allows you to + * register Swift or private Objective-C classes with the bridge. + * + * For example if one wanted to export a Swift class to the bridge: + * + * MyModule.swift: + * + * @objc(MyModule) class MyModule: NSObject { + * + * @objc func doSomething(string: String! withFoo a: Int, bar b: Int) { ... } + * + * } + * + * MyModuleExport.m: + * + * #import "RCTBridgeModule.h" + * + * @interface RCT_EXTERN_MODULE(MyModule, NSObject) + * + * RCT_EXTERN_METHOD(doSomething:(NSString *)string withFoo:(NSInteger)a bar:(NSInteger)b) + * + * @end + * + * This will now expose MyModule and the method to JavaScript via + * `NativeModules.MyModule.doSomething` + */ +#define RCT_EXTERN_MODULE(objc_name, objc_supername) \ + RCT_EXTERN_REMAP_MODULE(, objc_name, objc_supername) + +/** + * Similar to RCT_EXTERN_MODULE but allows setting a custom JavaScript name + */ +#define RCT_EXTERN_REMAP_MODULE(js_name, objc_name, objc_supername) \ + objc_name : objc_supername \ + @end \ + @interface objc_name (RCTExternModule) \ + @end \ + @implementation objc_name (RCTExternModule) \ + RCT_EXPORT_MODULE(js_name) + +/** + * Use this macro in accordance with RCT_EXTERN_MODULE to export methods + * of an external module. + */ +#define RCT_EXTERN_METHOD(method) \ + RCT_EXTERN_REMAP_METHOD(, method) + +/** + * Similar to RCT_EXTERN_REMAP_METHOD but allows setting a custom JavaScript name + */ +#define RCT_EXTERN_REMAP_METHOD(js_name, method) \ - (void)__rct_export__##method { \ __attribute__((used, section("__DATA,RCTExport"))) \ __attribute__((__aligned__(1))) \ static const char *__rct_export_entry__[] = { __func__, #method, #js_name }; \ - } \ - - (void)method + } /** * Deprecated, do not use. From 7de74b129d62bfd6d02470d2b01713d40f840684 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Thu, 23 Apr 2015 09:41:45 -0700 Subject: [PATCH 077/250] [ReactNative] Move NativeModules mock to OSS --- .../__mocks__/NativeModules.js | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js diff --git a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js new file mode 100644 index 00000000000000..28da1bc321fe53 --- /dev/null +++ b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js @@ -0,0 +1,44 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + */ +'use strict'; + +var NativeModules = { + I18n: { + translationsDictionary: { + 'Good bye, {name}!|Bye message': '¡Adiós {name}!', + }, + }, + Timing: { + createTimer: jest.genMockFunction(), + deleteTimer: jest.genMockFunction(), + }, + GraphPhotoUpload: { + upload: jest.genMockFunction(), + }, + FacebookSDK: { + login: jest.genMockFunction(), + logout: jest.genMockFunction(), + queryGraphPath: jest.genMockFunction().mockImpl( + (path, method, params, callback) => callback() + ), + }, + DataManager: { + queryData: jest.genMockFunction(), + }, + UIManager: { + customBubblingEventTypes: {}, + customDirectEventTypes: {}, + }, + AsyncLocalStorage: { + getItem: jest.genMockFunction(), + setItem: jest.genMockFunction(), + removeItem: jest.genMockFunction(), + clear: jest.genMockFunction(), + }, + SourceCode: { + scriptURL: null, + }, +}; + +module.exports = NativeModules; From d2dbf4e0edf7167c036a6346f974750ee1749d2d Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 23 Apr 2015 10:42:00 -0700 Subject: [PATCH 078/250] [ReactNative] Disable console.error => redboxes to unwedge Android --- .../src/DependencyResolver/haste/polyfills/console.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js index 4f513f45b692d1..11464cce5c62dd 100644 --- a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js +++ b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js @@ -60,7 +60,11 @@ if (global.reportException && level === LOG_LEVELS.error) { var error = new Error(str); error.framesToPop = 1; - global.reportException(error); + // TODO(sahrens): re-enable this when we have a way to turn + // it off by default for MAdMan/Android, and/or all + // consumers of console.error() are fixed, including + // CatalystErrorHandlerModuleTestCase + // global.reportException(error); } }; } From 357a54500e5d50402aef4e55631d7b5fd4153042 Mon Sep 17 00:00:00 2001 From: Bill Fisher Date: Thu, 23 Apr 2015 10:23:07 -0700 Subject: [PATCH 079/250] Implement transform styles, redux --- .../Components/View/ViewStylePropTypes.js | 3 + Libraries/ReactIOS/NativeMethodsMixin.js | 3 +- Libraries/ReactIOS/ReactIOSNativeComponent.js | 3 +- Libraries/StyleSheet/precomputeStyle.js | 161 ++++++++++++++++++ Libraries/Utilities/MatrixMath.js | 131 ++++++++++++++ 5 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 Libraries/StyleSheet/precomputeStyle.js create mode 100755 Libraries/Utilities/MatrixMath.js diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index bb22c6b26511bf..7bb795f160972b 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -34,7 +34,10 @@ var ViewStylePropTypes = { ), shadowOpacity: ReactPropTypes.number, shadowRadius: ReactPropTypes.number, + transform: ReactPropTypes.arrayOf(ReactPropTypes.object), transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), + + // DEPRECATED rotation: ReactPropTypes.number, scaleX: ReactPropTypes.number, scaleY: ReactPropTypes.number, diff --git a/Libraries/ReactIOS/NativeMethodsMixin.js b/Libraries/ReactIOS/NativeMethodsMixin.js index ec72a0b4f4696b..9d413e5c7ca7a1 100644 --- a/Libraries/ReactIOS/NativeMethodsMixin.js +++ b/Libraries/ReactIOS/NativeMethodsMixin.js @@ -19,6 +19,7 @@ var TextInputState = require('TextInputState'); var flattenStyle = require('flattenStyle'); var invariant = require('invariant'); var mergeFast = require('mergeFast'); +var precomputeStyle = require('precomputeStyle'); type MeasureOnSuccessCallback = ( x: number, @@ -93,7 +94,7 @@ var NativeMethodsMixin = { break; } } - var style = flattenStyle(nativeProps.style); + var style = precomputeStyle(flattenStyle(nativeProps.style)); var props = null; if (hasOnlyStyle) { diff --git a/Libraries/ReactIOS/ReactIOSNativeComponent.js b/Libraries/ReactIOS/ReactIOSNativeComponent.js index b9abd5965c5f07..7f27ae0ea7dae3 100644 --- a/Libraries/ReactIOS/ReactIOSNativeComponent.js +++ b/Libraries/ReactIOS/ReactIOSNativeComponent.js @@ -23,6 +23,7 @@ var styleDiffer = require('styleDiffer'); var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev'); var diffRawProperties = require('diffRawProperties'); var flattenStyle = require('flattenStyle'); +var precomputeStyle = require('precomputeStyle'); var warning = require('warning'); var registrationNames = ReactIOSEventEmitter.registrationNames; @@ -160,7 +161,7 @@ ReactIOSNativeComponent.Mixin = { // before actually doing the expensive flattening operation in order to // compute the diff. if (styleDiffer(nextProps.style, prevProps.style)) { - var nextFlattenedStyle = flattenStyle(nextProps.style); + var nextFlattenedStyle = precomputeStyle(flattenStyle(nextProps.style)); updatePayload = diffRawProperties( updatePayload, this.previousFlattenedStyle, diff --git a/Libraries/StyleSheet/precomputeStyle.js b/Libraries/StyleSheet/precomputeStyle.js new file mode 100644 index 00000000000000..ea2990b94e3049 --- /dev/null +++ b/Libraries/StyleSheet/precomputeStyle.js @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule precomputeStyle + * @flow + */ +'use strict'; + +var MatrixMath = require('MatrixMath'); +var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev'); +var invariant = require('invariant'); + +/** + * This method provides a hook where flattened styles may be precomputed or + * otherwise prepared to become better input data for native code. + */ +function precomputeStyle(style: ?Object): ?Object { + if (!style || !style.transform) { + return style; + } + invariant( + !style.transformMatrix, + 'transformMatrix and transform styles cannot be used on the same component' + ); + var newStyle = _precomputeTransforms({...style}); + deepFreezeAndThrowOnMutationInDev(newStyle); + return newStyle; +} + +/** + * Generate a transform matrix based on the provided transforms, and use that + * within the style object instead. + * + * This allows us to provide an API that is similar to CSS and to have a + * universal, singular interface to native code. + */ +function _precomputeTransforms(style: Object): Object { + var {transform} = style; + var result = MatrixMath.createIdentityMatrix(); + + transform.forEach(transformation => { + var key = Object.keys(transformation)[0]; + var value = transformation[key]; + if (__DEV__) { + _validateTransform(key, value, transformation); + } + + switch (key) { + case 'matrix': + MatrixMath.multiplyInto(result, result, value); + break; + case 'rotate': + _multiplyTransform(result, MatrixMath.reuseRotateZCommand, [_convertToRadians(value)]); + break; + case 'scale': + _multiplyTransform(result, MatrixMath.reuseScaleCommand, [value]); + break; + case 'scaleX': + _multiplyTransform(result, MatrixMath.reuseScaleXCommand, [value]); + break; + case 'scaleY': + _multiplyTransform(result, MatrixMath.reuseScaleYCommand, [value]); + break; + case 'translate': + _multiplyTransform(result, MatrixMath.reuseTranslate3dCommand, [value[0], value[1], value[2] || 0]); + break; + case 'translateX': + _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [value, 0]); + break; + case 'translateY': + _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [0, value]); + break; + default: + throw new Error('Invalid transform name: ' + key); + } + }); + + return { + ...style, + transformMatrix: result, + }; +} + +/** + * Performs a destructive operation on a transform matrix. + */ +function _multiplyTransform( + result: Array, + matrixMathFunction: Function, + args: Array +): void { + var matrixToApply = MatrixMath.createIdentityMatrix(); + var argsWithIdentity = [matrixToApply].concat(args); + matrixMathFunction.apply(this, argsWithIdentity); + MatrixMath.multiplyInto(result, result, matrixToApply); +} + +/** + * Parses a string like '0.5rad' or '60deg' into radians expressed in a float. + * Note that validation on the string is done in `_validateTransform()`. + */ +function _convertToRadians(value: string): number { + var floatValue = parseFloat(value, 10); + return value.indexOf('rad') > -1 ? floatValue : floatValue * Math.PI / 180; +} + +function _validateTransform(key, value, transformation) { + var multivalueTransforms = [ + 'matrix', + 'translate', + ]; + if (multivalueTransforms.indexOf(key) !== -1) { + invariant( + Array.isArray(value), + 'Transform with key of %s must have an array as the value: %s', + key, + JSON.stringify(transformation) + ); + } + switch (key) { + case 'matrix': + invariant( + value.length === 9 || value.length === 16, + 'Matrix transform must have a length of 9 (2d) or 16 (3d). ' + + 'Provided matrix has a length of %s: %s', + value.length, + JSON.stringify(transformation) + ); + break; + case 'translate': + break; + case 'rotate': + invariant( + typeof value === 'string', + 'Transform with key of "%s" must be a string: %s', + key, + JSON.stringify(transformation) + ); + invariant( + value.indexOf('deg') > -1 || value.indexOf('rad') > -1, + 'Rotate transform must be expressed in degrees (deg) or radians ' + + '(rad): %s', + JSON.stringify(transformation) + ); + break; + default: + invariant( + typeof value === 'number', + 'Transform with key of "%s" must be a number: %s', + key, + JSON.stringify(transformation) + ); + } +} + +module.exports = precomputeStyle; diff --git a/Libraries/Utilities/MatrixMath.js b/Libraries/Utilities/MatrixMath.js new file mode 100755 index 00000000000000..7f3d17c461b70a --- /dev/null +++ b/Libraries/Utilities/MatrixMath.js @@ -0,0 +1,131 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule MatrixMath + */ +'use strict'; + +/** + * Memory conservative (mutative) matrix math utilities. Uses "command" + * matrices, which are reusable. + */ +var MatrixMath = { + createIdentityMatrix: function() { + return [ + 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1 + ]; + }, + + createCopy: function(m) { + return [ + m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[12], m[13], m[14], m[15], + ]; + }, + + createTranslate2d: function(x, y) { + var mat = MatrixMath.createIdentityMatrix(); + MatrixMath.reuseTranslate2dCommand(mat, x, y); + return mat; + }, + + reuseTranslate2dCommand: function(matrixCommand, x, y) { + matrixCommand[12] = x; + matrixCommand[13] = y; + }, + + reuseTranslate3dCommand: function(matrixCommand, x, y, z) { + matrixCommand[12] = x; + matrixCommand[13] = y; + matrixCommand[14] = z; + }, + + createScale: function(factor) { + var mat = MatrixMath.createIdentityMatrix(); + MatrixMath.reuseScaleCommand(mat, factor); + return mat; + }, + + reuseScaleCommand: function(matrixCommand, factor) { + matrixCommand[0] = factor; + matrixCommand[5] = factor; + }, + + reuseScale3dCommand: function(matrixCommand, x, y, z) { + matrixCommand[0] = x; + matrixCommand[5] = y; + matrixCommand[10] = z; + }, + + reuseScaleXCommand(matrixCommand, factor) { + matrixCommand[0] = factor; + }, + + reuseScaleYCommand(matrixCommand, factor) { + matrixCommand[5] = factor; + }, + + reuseScaleZCommand(matrixCommand, factor) { + matrixCommand[10] = factor; + }, + + reuseRotateYCommand: function(matrixCommand, amount) { + matrixCommand[0] = Math.cos(amount); + matrixCommand[2] = Math.sin(amount); + matrixCommand[8] = Math.sin(-amount); + matrixCommand[10] = Math.cos(amount); + }, + + createRotateZ: function(radians) { + var mat = MatrixMath.createIdentityMatrix(); + MatrixMath.reuseRotateZCommand(mat, radians); + return mat; + }, + + // http://www.w3.org/TR/css3-transforms/#recomposing-to-a-2d-matrix + reuseRotateZCommand: function(matrixCommand, radians) { + matrixCommand[0] = Math.cos(radians); + matrixCommand[1] = Math.sin(radians); + matrixCommand[4] = -Math.sin(radians); + matrixCommand[5] = Math.cos(radians); + }, + + multiplyInto: function(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + } + +}; + +module.exports = MatrixMath; From 4242bd9c8345e48a062e59f742f9ab4258136437 Mon Sep 17 00:00:00 2001 From: Herman Schaaf Date: Thu, 23 Apr 2015 10:37:09 -0700 Subject: [PATCH 080/250] Replace percent escapes in file URL before using as path Summary: This is to fix a bug that prevents bundling of projects that contain spaces (or other special characters) in their names. #### Reproduction steps before the fix 1. Create a project with a space in the name: ![screen shot 2015-04-16 at 17 23 46](https://cloud.githubusercontent.com/assets/1121616/7176887/63af36de-e45d-11e4-9aa9-40586560b716.png) 2. Follow the steps in `OPTION 2` for running from a bundled file, i.e. create the `main.bundle` file, add it to the project if is not there already, and uncomment `jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];` 3. Run the application. This is what happens: ![screen shot 2015-04-16 at 17 27 48](https://cloud.githubusercontent.com/assets/1121616/7176955/f139764a-e45d-11e4-8dc8-3c13aab70828.png) To prove that it has to do with a space in the name, refactor the project name to not contain a space: ![screen shot 2015-04-16 at 17 28 27](https://cloud.githubusercontent.com/assets/1121616/7176966/056b6c9a Closes https://github.com/facebook/react-native/pull/876 Github Author: Herman Schaaf Test Plan: Imported from GitHub, without a `Test Plan:` line. --- React/Base/RCTJavaScriptLoader.m | 179 ++++++++++++------------------- 1 file changed, 71 insertions(+), 108 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index dd8fab461868fd..e632505a4e2ca8 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -10,47 +10,15 @@ #import "RCTJavaScriptLoader.h" #import "RCTBridge.h" -#import "RCTInvalidating.h" -#import "RCTLog.h" -#import "RCTRedBox.h" +#import "RCTConvert.h" #import "RCTSourceCode.h" #import "RCTUtils.h" -#define NO_REMOTE_MODULE @"Could not fetch module bundle %@. Ensure node server is running.\n\nIf it timed out, try reloading." -#define NO_LOCAL_BUNDLE @"Could not load local bundle %@. Ensure file exists." - -#define CACHE_DIR @"RCTJSBundleCache" - -#pragma mark - Application Engine - -/** - * TODO: - * - Add window resize rotation events matching the DOM API. - * - Device pixel ration hooks. - * - Source maps. - */ @implementation RCTJavaScriptLoader { __weak RCTBridge *_bridge; } -/** - * `CADisplayLink` code copied from Ejecta but we've placed the JavaScriptCore - * engine in its own dedicated thread. - * - * TODO: Try adding to the `RCTJavaScriptExecutor`'s thread runloop. Removes one - * additional GCD dispatch per frame and likely makes it so that other UIThread - * operations don't delay the dispatch (so we can begin working in JS much - * faster.) Event handling must still be sent via a GCD dispatch, of course. - * - * We must add the display link to two runloops in order to get setTimeouts to - * fire during scrolling. (`NSDefaultRunLoopMode` and `UITrackingRunLoopMode`) - * TODO: We can invent a `requestAnimationFrame` and - * `requestAvailableAnimationFrame` to control if callbacks can be fired during - * an animation. - * http://stackoverflow.com/questions/12622800/why-does-uiscrollview-pause-my-cadisplaylink - * - */ - (instancetype)initWithBridge:(RCTBridge *)bridge { if ((self = [super init])) { @@ -61,92 +29,87 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onComplete { + NSURL *originalURL = scriptURL; + if (!scriptURL.scheme || [scriptURL isFileURL]) { + scriptURL = [RCTConvert NSURL:scriptURL.path]; + } + if (scriptURL == nil) { NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{ - NSLocalizedDescriptionKey: @"No script URL provided" + NSLocalizedDescriptionKey: originalURL ? [NSString stringWithFormat:@"Script URL '%@' could not be found.", originalURL] : @"No script URL provided." }]; onComplete(error); return; } - if ([scriptURL isFileURL]) { - NSString *bundlePath = [[NSBundle bundleForClass:[self class]] resourcePath]; - NSString *localPath = [scriptURL.absoluteString substringFromIndex:@"file://".length]; - - if (![localPath hasPrefix:bundlePath]) { - NSString *absolutePath = [NSString stringWithFormat:@"%@/%@", bundlePath, localPath]; - scriptURL = [NSURL fileURLWithPath:absolutePath]; - } - } - NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:scriptURL completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { - // Handle general request errors - if (error) { - if ([[error domain] isEqualToString:NSURLErrorDomain]) { - NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:[scriptURL absoluteString]]; - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: desc, - NSLocalizedFailureReasonErrorKey: [error localizedDescription], - NSUnderlyingErrorKey: error, - }; - error = [NSError errorWithDomain:@"JSServer" - code:error.code - userInfo:userInfo]; - } - onComplete(error); - return; - } - - // Parse response as text - NSStringEncoding encoding = NSUTF8StringEncoding; - if (response.textEncodingName != nil) { - CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); - if (cfEncoding != kCFStringEncodingInvalidId) { - encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); - } - } - NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding]; - - // Handle HTTP errors - if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) { - NSDictionary *userInfo; - NSDictionary *errorDetails = RCTJSONParse(rawText, nil); - if ([errorDetails isKindOfClass:[NSDictionary class]] && - [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) { - NSMutableArray *fakeStack = [[NSMutableArray alloc] init]; - for (NSDictionary *err in errorDetails[@"errors"]) { - [fakeStack addObject: @{ - @"methodName": err[@"description"] ?: @"", - @"file": err[@"filename"] ?: @"", - @"lineNumber": err[@"lineNumber"] ?: @0 - }]; - } - userInfo = @{ - NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided", - @"stack": fakeStack, - }; - } else { - userInfo = @{NSLocalizedDescriptionKey: rawText}; - } - error = [NSError errorWithDomain:@"JSServer" - code:[(NSHTTPURLResponse *)response statusCode] - userInfo:userInfo]; - - onComplete(error); - return; - } - RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; - sourceCodeModule.scriptURL = scriptURL; - sourceCodeModule.scriptText = rawText; + // Handle general request errors + if (error) { + if ([[error domain] isEqualToString:NSURLErrorDomain]) { + NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:[scriptURL absoluteString]]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: [error localizedDescription], + NSUnderlyingErrorKey: error, + }; + error = [NSError errorWithDomain:@"JSServer" + code:error.code + userInfo:userInfo]; + } + onComplete(error); + return; + } - [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(scriptError); - }); - }]; - }]; + // Parse response as text + NSStringEncoding encoding = NSUTF8StringEncoding; + if (response.textEncodingName != nil) { + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (cfEncoding != kCFStringEncodingInvalidId) { + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); + } + } + NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding]; + + // Handle HTTP errors + if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) { + NSDictionary *userInfo; + NSDictionary *errorDetails = RCTJSONParse(rawText, nil); + if ([errorDetails isKindOfClass:[NSDictionary class]] && + [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) { + NSMutableArray *fakeStack = [[NSMutableArray alloc] init]; + for (NSDictionary *err in errorDetails[@"errors"]) { + [fakeStack addObject: @{ + @"methodName": err[@"description"] ?: @"", + @"file": err[@"filename"] ?: @"", + @"lineNumber": err[@"lineNumber"] ?: @0 + }]; + } + userInfo = @{ + NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided", + @"stack": fakeStack, + }; + } else { + userInfo = @{NSLocalizedDescriptionKey: rawText}; + } + error = [NSError errorWithDomain:@"JSServer" + code:[(NSHTTPURLResponse *)response statusCode] + userInfo:userInfo]; + + onComplete(error); + return; + } + RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; + sourceCodeModule.scriptURL = scriptURL; + sourceCodeModule.scriptText = rawText; + + [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { + dispatch_async(dispatch_get_main_queue(), ^{ + onComplete(scriptError); + }); + }]; + }]; [task resume]; } From 4f89d1f76c179bf363b44c6a659331febdd9a6d0 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Thu, 23 Apr 2015 11:30:39 -0700 Subject: [PATCH 081/250] [react-packager] Fix jest tests --- .../AssetServer/__tests__/AssetServer-test.js | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js b/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js index 94dba8cff78ae4..ba804b5f19e004 100644 --- a/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js +++ b/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js @@ -1,16 +1,23 @@ 'use strict'; jest - .autoMockOff() - .mock('../../lib/declareOpts') - .mock('crypto') - .mock('fs'); + .dontMock('path') + .dontMock('../../lib/getAssetDataFromName') + .dontMock('../'); -var fs = require('fs'); -var AssetServer = require('../'); var Promise = require('bluebird'); describe('AssetServer', function() { + var AssetServer; + var crypto; + var fs; + + beforeEach(function() { + AssetServer = require('../'); + crypto = require('crypto'); + fs = require('fs'); + }); + describe('assetServer.get', function() { pit('should work for the simple case', function() { var server = new AssetServer({ @@ -96,7 +103,7 @@ describe('AssetServer', function() { hash.digest.mockImpl(function() { return 'wow such hash'; }); - require('crypto').createHash.mockImpl(function() { + crypto.createHash.mockImpl(function() { return hash; }); From 24095fcc2df06cac0ab606b981c4dcc53bb54299 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Thu, 23 Apr 2015 11:44:02 -0700 Subject: [PATCH 082/250] [react-packager] Change uri to name --- packager/react-packager/src/Packager/__tests__/Packager-test.js | 2 +- packager/react-packager/src/Packager/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index 8e1420a3a6c0dd..3040e098262830 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -132,7 +132,7 @@ describe('Packager', function() { __packager_asset: true, isStatic: true, path: '/root/img/img.png', - uri: 'img', + name: 'img', width: 25, height: 50, deprecated: true, diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index 8563e27280f3c3..79dd6f23d8971a 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -181,7 +181,7 @@ Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) { __packager_asset: true, isStatic: true, path: module.path, - uri: module.id.replace(/^[^!]+!/, ''), + name: module.id.replace(/^[^!]+!/, ''), width: dimensions.width / module.resolution, height: dimensions.height / module.resolution, deprecated: true, From e88ba1a6a319e2b559365b213534f2d4d4926b26 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 23 Apr 2015 11:34:25 -0700 Subject: [PATCH 083/250] [ReactNative] Back out D2014163 entirely --- Libraries/AppRegistry/AppRegistry.js | 2 +- .../Initialization/ExceptionsManager.js | 28 ++++++++----------- .../InitializeJavaScriptAppEngine.js | 3 +- React/Executors/RCTContextExecutor.m | 17 ++++++----- .../haste/polyfills/console.js | 22 ++++----------- 5 files changed, 29 insertions(+), 43 deletions(-) diff --git a/Libraries/AppRegistry/AppRegistry.js b/Libraries/AppRegistry/AppRegistry.js index 63bd1d9a0bd3f4..f36f881323adce 100644 --- a/Libraries/AppRegistry/AppRegistry.js +++ b/Libraries/AppRegistry/AppRegistry.js @@ -69,7 +69,7 @@ var AppRegistry = { 'Running application "' + appKey + '" with appParams: ' + JSON.stringify(appParameters) + '. ' + '__DEV__ === ' + __DEV__ + - ', development-level warnings are ' + (__DEV__ ? 'ON' : 'OFF') + + ', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') + ', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON') ); invariant( diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index fb57a956a60000..c5476eaab5cfe3 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -25,11 +25,17 @@ type Exception = { message: string; } -function reportException(e: Exception, stack?: any) { +function handleException(e: Exception) { + var stack = parseErrorStack(e); + console.error( + 'Err0r: ' + + '\n stack: \n' + stackToString(stack) + + '\n URL: ' + e.sourceURL + + '\n line: ' + e.line + + '\n message: ' + e.message + ); + if (RCTExceptionsManager) { - if (!stack) { - stack = parseErrorStack(e); - } RCTExceptionsManager.reportUnhandledException(e.message, stack); if (__DEV__) { (sourceMapPromise = sourceMapPromise || loadSourceMap()) @@ -44,18 +50,6 @@ function reportException(e: Exception, stack?: any) { } } -function handleException(e: Exception) { - var stack = parseErrorStack(e); - console.log( - 'Error: ' + - '\n stack: \n' + stackToString(stack) + - '\n URL: ' + e.sourceURL + - '\n line: ' + e.line + - '\n message: ' + e.message - ); - reportException(e, stack); -} - function stackToString(stack) { var maxLength = Math.max.apply(null, stack.map(frame => frame.methodName.length)); return stack.map(frame => stackFrameToString(frame, maxLength)).join('\n'); @@ -77,4 +71,4 @@ function fillSpaces(n) { return new Array(n + 1).join(' '); } -module.exports = { handleException, reportException }; +module.exports = { handleException }; diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index bbfee4eb39a37f..51f6809ccd9b28 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -77,7 +77,6 @@ function handleErrorWithRedBox(e) { function setupRedBoxErrorHandler() { var ErrorUtils = require('ErrorUtils'); ErrorUtils.setGlobalHandler(handleErrorWithRedBox); - GLOBAL.reportException = require('ExceptionsManager').reportException; } /** @@ -135,8 +134,8 @@ function setupGeolocation() { } setupDocumentShim(); +setupRedBoxErrorHandler(); setupTimers(); -setupRedBoxErrorHandler(); // needs to happen after setupTimers setupAlert(); setupPromise(); setupXHR(); diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 8f931d96ab3b3f..86444dd2a7f903 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -74,12 +74,12 @@ @implementation RCTContextExecutor static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { if (argumentCount > 0) { - JSStringRef messageRef = JSValueToStringCopy(context, arguments[0], exception); - if (!messageRef) { + JSStringRef string = JSValueToStringCopy(context, arguments[0], exception); + if (!string) { return JSValueMakeUndefined(context); } - NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, messageRef); - JSStringRelease(messageRef); + NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string); + JSStringRelease(string); NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: @"( stack: )?([_a-z0-9]*)@?(http://|file:///)[a-z.0-9:/_-]+/([a-z0-9_]+).includeRequire.runModule.bundle(:[0-9]+:[0-9]+)" options:NSRegularExpressionCaseInsensitive @@ -89,11 +89,14 @@ static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, range:(NSRange){0, message.length} withTemplate:@"[$4$5] \t$2"]; + // TODO: it would be good if log level was sent as a param, instead of this hack RCTLogLevel level = RCTLogLevelInfo; - if (argumentCount > 1) { - level = MAX(level, JSValueToNumber(context, arguments[1], exception) - 1); + if ([message rangeOfString:@"error" options:NSCaseInsensitiveSearch].length) { + level = RCTLogLevelError; + } else if ([message rangeOfString:@"warning" options:NSCaseInsensitiveSearch].length) { + level = RCTLogLevelWarning; } - RCTGetLogFunction()(level, nil, nil, message); + _RCTLogFormat(level, NULL, -1, @"%@", message); } return JSValueMakeUndefined(context); diff --git a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js index 11464cce5c62dd..91fb970f885521 100644 --- a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js +++ b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js @@ -23,7 +23,7 @@ log: 1, info: 2, warn: 3, - error: 4, + error: 4 }; function setupConsole(global) { @@ -35,10 +35,8 @@ function getNativeLogFunction(level) { return function() { var str = Array.prototype.map.call(arguments, function(arg) { - if (arg === undefined) { - return 'undefined'; - } else if (arg === null) { - return 'null'; + if (arg == null) { + return arg === null ? 'null' : 'undefined'; } else if (typeof arg === 'string') { return '"' + arg + '"'; } else { @@ -50,22 +48,14 @@ if (typeof arg.toString === 'function') { try { return arg.toString(); - } catch (E) {} + } catch (E) { + return 'unknown'; + } } - return '["' + typeof arg + '" failed to stringify]'; } } }).join(', '); global.nativeLoggingHook(str, level); - if (global.reportException && level === LOG_LEVELS.error) { - var error = new Error(str); - error.framesToPop = 1; - // TODO(sahrens): re-enable this when we have a way to turn - // it off by default for MAdMan/Android, and/or all - // consumers of console.error() are fixed, including - // CatalystErrorHandlerModuleTestCase - // global.reportException(error); - } }; } From 34a5aa1d0ab1f4c46760a73a81348d1972dcf460 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Thu, 23 Apr 2015 14:39:51 -0700 Subject: [PATCH 084/250] [ReactNative][madman] Revert D2001353 --- React/Base/RCTJavaScriptLoader.m | 179 +++++++++++++++++++------------ 1 file changed, 108 insertions(+), 71 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index e632505a4e2ca8..dd8fab461868fd 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -10,15 +10,47 @@ #import "RCTJavaScriptLoader.h" #import "RCTBridge.h" -#import "RCTConvert.h" +#import "RCTInvalidating.h" +#import "RCTLog.h" +#import "RCTRedBox.h" #import "RCTSourceCode.h" #import "RCTUtils.h" +#define NO_REMOTE_MODULE @"Could not fetch module bundle %@. Ensure node server is running.\n\nIf it timed out, try reloading." +#define NO_LOCAL_BUNDLE @"Could not load local bundle %@. Ensure file exists." + +#define CACHE_DIR @"RCTJSBundleCache" + +#pragma mark - Application Engine + +/** + * TODO: + * - Add window resize rotation events matching the DOM API. + * - Device pixel ration hooks. + * - Source maps. + */ @implementation RCTJavaScriptLoader { __weak RCTBridge *_bridge; } +/** + * `CADisplayLink` code copied from Ejecta but we've placed the JavaScriptCore + * engine in its own dedicated thread. + * + * TODO: Try adding to the `RCTJavaScriptExecutor`'s thread runloop. Removes one + * additional GCD dispatch per frame and likely makes it so that other UIThread + * operations don't delay the dispatch (so we can begin working in JS much + * faster.) Event handling must still be sent via a GCD dispatch, of course. + * + * We must add the display link to two runloops in order to get setTimeouts to + * fire during scrolling. (`NSDefaultRunLoopMode` and `UITrackingRunLoopMode`) + * TODO: We can invent a `requestAnimationFrame` and + * `requestAvailableAnimationFrame` to control if callbacks can be fired during + * an animation. + * http://stackoverflow.com/questions/12622800/why-does-uiscrollview-pause-my-cadisplaylink + * + */ - (instancetype)initWithBridge:(RCTBridge *)bridge { if ((self = [super init])) { @@ -29,87 +61,92 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onComplete { - NSURL *originalURL = scriptURL; - if (!scriptURL.scheme || [scriptURL isFileURL]) { - scriptURL = [RCTConvert NSURL:scriptURL.path]; - } - if (scriptURL == nil) { NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{ - NSLocalizedDescriptionKey: originalURL ? [NSString stringWithFormat:@"Script URL '%@' could not be found.", originalURL] : @"No script URL provided." + NSLocalizedDescriptionKey: @"No script URL provided" }]; onComplete(error); return; } + if ([scriptURL isFileURL]) { + NSString *bundlePath = [[NSBundle bundleForClass:[self class]] resourcePath]; + NSString *localPath = [scriptURL.absoluteString substringFromIndex:@"file://".length]; + + if (![localPath hasPrefix:bundlePath]) { + NSString *absolutePath = [NSString stringWithFormat:@"%@/%@", bundlePath, localPath]; + scriptURL = [NSURL fileURLWithPath:absolutePath]; + } + } + NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:scriptURL completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { - // Handle general request errors - if (error) { - if ([[error domain] isEqualToString:NSURLErrorDomain]) { - NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:[scriptURL absoluteString]]; - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: desc, - NSLocalizedFailureReasonErrorKey: [error localizedDescription], - NSUnderlyingErrorKey: error, - }; - error = [NSError errorWithDomain:@"JSServer" - code:error.code - userInfo:userInfo]; - } - onComplete(error); - return; - } + // Handle general request errors + if (error) { + if ([[error domain] isEqualToString:NSURLErrorDomain]) { + NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:[scriptURL absoluteString]]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: [error localizedDescription], + NSUnderlyingErrorKey: error, + }; + error = [NSError errorWithDomain:@"JSServer" + code:error.code + userInfo:userInfo]; + } + onComplete(error); + return; + } - // Parse response as text - NSStringEncoding encoding = NSUTF8StringEncoding; - if (response.textEncodingName != nil) { - CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); - if (cfEncoding != kCFStringEncodingInvalidId) { - encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); - } - } - NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding]; - - // Handle HTTP errors - if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) { - NSDictionary *userInfo; - NSDictionary *errorDetails = RCTJSONParse(rawText, nil); - if ([errorDetails isKindOfClass:[NSDictionary class]] && - [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) { - NSMutableArray *fakeStack = [[NSMutableArray alloc] init]; - for (NSDictionary *err in errorDetails[@"errors"]) { - [fakeStack addObject: @{ - @"methodName": err[@"description"] ?: @"", - @"file": err[@"filename"] ?: @"", - @"lineNumber": err[@"lineNumber"] ?: @0 - }]; - } - userInfo = @{ - NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided", - @"stack": fakeStack, - }; - } else { - userInfo = @{NSLocalizedDescriptionKey: rawText}; - } - error = [NSError errorWithDomain:@"JSServer" - code:[(NSHTTPURLResponse *)response statusCode] - userInfo:userInfo]; - - onComplete(error); - return; - } - RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; - sourceCodeModule.scriptURL = scriptURL; - sourceCodeModule.scriptText = rawText; - - [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(scriptError); - }); - }]; - }]; + // Parse response as text + NSStringEncoding encoding = NSUTF8StringEncoding; + if (response.textEncodingName != nil) { + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (cfEncoding != kCFStringEncodingInvalidId) { + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); + } + } + NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding]; + + // Handle HTTP errors + if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) { + NSDictionary *userInfo; + NSDictionary *errorDetails = RCTJSONParse(rawText, nil); + if ([errorDetails isKindOfClass:[NSDictionary class]] && + [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) { + NSMutableArray *fakeStack = [[NSMutableArray alloc] init]; + for (NSDictionary *err in errorDetails[@"errors"]) { + [fakeStack addObject: @{ + @"methodName": err[@"description"] ?: @"", + @"file": err[@"filename"] ?: @"", + @"lineNumber": err[@"lineNumber"] ?: @0 + }]; + } + userInfo = @{ + NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided", + @"stack": fakeStack, + }; + } else { + userInfo = @{NSLocalizedDescriptionKey: rawText}; + } + error = [NSError errorWithDomain:@"JSServer" + code:[(NSHTTPURLResponse *)response statusCode] + userInfo:userInfo]; + + onComplete(error); + return; + } + RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; + sourceCodeModule.scriptURL = scriptURL; + sourceCodeModule.scriptText = rawText; + + [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { + dispatch_async(dispatch_get_main_queue(), ^{ + onComplete(scriptError); + }); + }]; + }]; [task resume]; } From 36afc46274157a35bdfa8997e40b20b02ead4cf9 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Thu, 23 Apr 2015 14:16:10 -0700 Subject: [PATCH 085/250] [ReactNative] Fix for Navigator.replacePreviousAndPop --- Libraries/CustomComponents/Navigator/Navigator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index ea8977ff87ca38..da4d2ab10f52ca 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -1079,8 +1079,7 @@ var Navigator = React.createClass({ // To avoid visual glitches, we never re-render scenes during a transition. // We assume that `state.updatingRangeLength` will have a length during the // initial render of any scene - var shouldRenderScenes = !this.state.isAnimating && - this.state.updatingRangeLength !== 0; + var shouldRenderScenes = this.state.updatingRangeLength !== 0; if (shouldRenderScenes) { return ( From ee3986342a1ab780f0c7cfc73dabf6f0b8dec161 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Thu, 23 Apr 2015 16:11:34 -0700 Subject: [PATCH 086/250] [ReactNative][madman] Revert D2011598 temporarily --- Libraries/Utilities/MessageQueue.js | 32 +++++++++++++---------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/Libraries/Utilities/MessageQueue.js b/Libraries/Utilities/MessageQueue.js index 576aaa9917bba2..c047d06deecf93 100644 --- a/Libraries/Utilities/MessageQueue.js +++ b/Libraries/Utilities/MessageQueue.js @@ -10,9 +10,7 @@ * @flow */ 'use strict'; - var ErrorUtils = require('ErrorUtils'); -var ReactUpdates = require('ReactUpdates'); var invariant = require('invariant'); var warning = require('warning'); @@ -309,23 +307,21 @@ var MessageQueueMixin = { ); }, - processBatch: function(batch) { + processBatch: function (batch) { var self = this; - ReactUpdates.batchedUpdates(function() { - batch.forEach(function(call) { - invariant( - call.module === 'BatchedBridge', - 'All the calls should pass through the BatchedBridge module' - ); - if (call.method === 'callFunctionReturnFlushedQueue') { - self.callFunction.apply(self, call.args); - } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { - self.invokeCallback.apply(self, call.args); - } else { - throw new Error( - 'Unrecognized method called on BatchedBridge: ' + call.method); - } - }); + batch.forEach(function (call) { + invariant( + call.module === 'BatchedBridge', + 'All the calls should pass through the BatchedBridge module' + ); + if (call.method === 'callFunctionReturnFlushedQueue') { + self.callFunction.apply(self, call.args); + } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { + self.invokeCallback.apply(self, call.args); + } else { + throw new Error( + 'Unrecognized method called on BatchedBridge: ' + call.method); + } }); return this.flushedQueue(); }, From 1f1c6af9cd195763b4b313884c7c00eb5be30bc6 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Tue, 21 Apr 2015 14:47:40 -0700 Subject: [PATCH 087/250] [ReactNative] Add instructions for new `react-native bundle` --- Examples/SampleApp/iOS/AppDelegate.m | 8 ++++---- Examples/SampleApp/iOS/main.jsbundle | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Examples/SampleApp/iOS/AppDelegate.m b/Examples/SampleApp/iOS/AppDelegate.m index 777072c6c56964..7e8d5fecfb2915 100644 --- a/Examples/SampleApp/iOS/AppDelegate.m +++ b/Examples/SampleApp/iOS/AppDelegate.m @@ -35,12 +35,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( /** * OPTION 2 - * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` - * to your Xcode project folder in the terminal, and run + * Load from pre-bundled file on disk. To re-generate the static bundle + * from the root of your project directory, run * - * $ curl 'http://localhost:8081/Examples/SampleApp/index.ios.bundle?dev=false&minify=true' -o main.jsbundle + * $ react-native bundle --minify * - * then add the `main.jsbundle` file to your project and uncomment this line: + * see http://facebook.github.io/react-native/docs/runningondevice.html */ // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; diff --git a/Examples/SampleApp/iOS/main.jsbundle b/Examples/SampleApp/iOS/main.jsbundle index 7cc6a2adcf6796..b702b30c66dc4c 100644 --- a/Examples/SampleApp/iOS/main.jsbundle +++ b/Examples/SampleApp/iOS/main.jsbundle @@ -1,5 +1,8 @@ // Offline JS -// To re-generate the offline bundle, run this from root of your project -// $ curl 'http://localhost:8081/Examples/SampleApp/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle +// To re-generate the offline bundle, run this from the root of your project: +// +// $ react-native bundle --minify +// +// See http://facebook.github.io/react-native/docs/runningondevice.html for more details. throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions'); From 4c9ed22ff62fcfc118d4089498059db97af833ef Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Thu, 23 Apr 2015 17:04:11 -0700 Subject: [PATCH 088/250] [ReactNative][madman] Reverted D2014357 --- packager/react-packager/src/Packager/__tests__/Packager-test.js | 2 +- packager/react-packager/src/Packager/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index 3040e098262830..8e1420a3a6c0dd 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -132,7 +132,7 @@ describe('Packager', function() { __packager_asset: true, isStatic: true, path: '/root/img/img.png', - name: 'img', + uri: 'img', width: 25, height: 50, deprecated: true, diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index 79dd6f23d8971a..8563e27280f3c3 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -181,7 +181,7 @@ Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) { __packager_asset: true, isStatic: true, path: module.path, - name: module.id.replace(/^[^!]+!/, ''), + uri: module.id.replace(/^[^!]+!/, ''), width: dimensions.width / module.resolution, height: dimensions.height / module.resolution, deprecated: true, From dd6f7743eb626a2b22e1d97130b3fea931f73b55 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Thu, 23 Apr 2015 16:00:34 -0700 Subject: [PATCH 089/250] Fix updating RCTText with new text of the same size Summary: Fixes #979. Previously, a Text whose width is determined automatically (as opposed to set by a container) would position the text incorrectly after an update to the text *if* the text's width did not change (i.e., when changing only digits in a font with tabular numbers). Every time RCTShadowText's RCTMeasure runs, it sets the text container's size to be the maximum allowed size for the text. When RCTText's drawRect is called later, it relied on layoutSubviews having been called to set the text container's size back to the proper width. But if RCTMeasure returned the same dimensions as last time, then RCTText's frame wasn't reset and so layoutSubviews was never re-called. With this change, we set the textContainer's size each time we draw the text. We could also fix this by using a different NSTextContainer instance in RCTMeasure. Not sure what the pros and cons of that are. Closes https://github.com/facebook/react-native/pull/989 Github Author: Ben Alpert Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Text/RCTText.m | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Libraries/Text/RCTText.m b/Libraries/Text/RCTText.m index e51686ac7bcc18..9d2ade8e43082a 100644 --- a/Libraries/Text/RCTText.m +++ b/Libraries/Text/RCTText.m @@ -95,21 +95,18 @@ - (CGRect)textFrame return UIEdgeInsetsInsetRect(self.bounds, _contentInset); } -- (void)layoutSubviews +- (void)drawRect:(CGRect)rect { - [super layoutSubviews]; + CGRect textFrame = [self textFrame]; - // The header comment for `size` says that a height of 0.0 should be enough, - // but it isn't. - _textContainer.size = CGSizeMake([self textFrame].size.width, CGFLOAT_MAX); -} + // We reset the text container size every time because RCTShadowText's + // RCTMeasure overrides it. The header comment for `size` says that a height + // of 0.0 should be enough, but it isn't. + _textContainer.size = CGSizeMake(textFrame.size.width, CGFLOAT_MAX); -- (void)drawRect:(CGRect)rect -{ - CGPoint origin = [self textFrame].origin; NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer]; - [_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:origin]; - [_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:origin]; + [_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin]; + [_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin]; } - (NSNumber *)reactTagAtPoint:(CGPoint)point From d354c7dd91bb64ca1c0b4ca1c0725cae0981a741 Mon Sep 17 00:00:00 2001 From: Felix Oghina Date: Fri, 24 Apr 2015 02:30:24 -0700 Subject: [PATCH 090/250] [react_native] JS files from D2009062: add geolocation support --- Libraries/Geolocation/{Geolocation.ios.js => Geolocation.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Libraries/Geolocation/{Geolocation.ios.js => Geolocation.js} (100%) diff --git a/Libraries/Geolocation/Geolocation.ios.js b/Libraries/Geolocation/Geolocation.js similarity index 100% rename from Libraries/Geolocation/Geolocation.ios.js rename to Libraries/Geolocation/Geolocation.js From d293bed5ab66c002beb5331304c8e94f5de41c49 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Fri, 24 Apr 2015 08:31:07 -0700 Subject: [PATCH 091/250] [ReactNative] Fix analyze errors on oss --- .../RCTActionSheet.xcodeproj/project.pbxproj | 4 ++++ .../RCTAdSupport.xcodeproj/project.pbxproj | 3 +++ .../RCTGeolocation.xcodeproj/project.pbxproj | 3 +++ Libraries/Image/RCTGIFImage.m | 1 - .../Image/RCTImage.xcodeproj/project.pbxproj | 4 ++++ .../Network/RCTNetwork.xcodeproj/project.pbxproj | 3 +++ .../RCTPushNotification.xcodeproj/project.pbxproj | 3 +++ .../RCTPushNotificationManager.m | 2 +- .../RCTTest/RCTTest.xcodeproj/project.pbxproj | 3 +++ .../project.pbxproj | 3 +++ Libraries/Text/RCTText.xcodeproj/project.pbxproj | 3 +++ .../RCTVibration.xcodeproj/project.pbxproj | 3 +++ React/Layout/Layout.c | 15 +-------------- React/React.xcodeproj/project.pbxproj | 4 ++++ 14 files changed, 38 insertions(+), 16 deletions(-) diff --git a/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj b/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj index 7e420235efe607..8434df87d37151 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj +++ b/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj @@ -198,6 +198,7 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -206,6 +207,7 @@ LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTActionSheet; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -213,6 +215,7 @@ 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -221,6 +224,7 @@ LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTActionSheet; + RUN_CLANG_STATIC_ANALYZER = NO; SKIP_INSTALL = YES; }; name = Release; diff --git a/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj b/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj index 811d25e6304d57..1b89d7bfa9d6fc 100644 --- a/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj +++ b/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj @@ -208,6 +208,7 @@ 832C81951AAF6DF0007FA2F7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -215,6 +216,7 @@ ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -222,6 +224,7 @@ 832C81961AAF6DF0007FA2F7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj b/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj index dd17e5808c421c..ee79e7571fd973 100644 --- a/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj +++ b/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj @@ -198,6 +198,7 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -206,6 +207,7 @@ LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTGeolocation; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -213,6 +215,7 @@ 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Libraries/Image/RCTGIFImage.m b/Libraries/Image/RCTGIFImage.m index e0975f4ac25e4f..99704e305dd0b6 100644 --- a/Libraries/Image/RCTGIFImage.m +++ b/Libraries/Image/RCTGIFImage.m @@ -14,7 +14,6 @@ static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSource) { if (!UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)) { - CFRelease(imageSource); return nil; } diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj index 33a5a65a8382c0..3431def50f0532 100644 --- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj +++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj @@ -240,6 +240,7 @@ 58B511721A9E6B3D00147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -251,6 +252,7 @@ ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTImage; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -258,6 +260,7 @@ 58B511731A9E6B3D00147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -269,6 +272,7 @@ ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTImage; + RUN_CLANG_STATIC_ANALYZER = NO; SKIP_INSTALL = YES; }; name = Release; diff --git a/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj index 652952994870b0..1dca7fe6de27bf 100644 --- a/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj +++ b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj @@ -204,6 +204,7 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -212,6 +213,7 @@ LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTNetwork; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -219,6 +221,7 @@ 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj b/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj index 10d8ce16900533..c65b8d3c5b7acc 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj +++ b/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj @@ -198,6 +198,7 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -206,6 +207,7 @@ LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTPushNotification; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -213,6 +215,7 @@ 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index 90f2d878629092..c0bedee5ec8952 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -113,7 +113,7 @@ - (void)handleRemoteNotificationReceived:(NSNotification *)notification #endif - NSUInteger types; + NSUInteger types = 0; if ([UIApplication instancesRespondToSelector:@selector(currentUserNotificationSettings)]) { types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types]; } else { diff --git a/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj b/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj index d4945f460288a3..6ab58a8a2e3e06 100644 --- a/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj +++ b/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj @@ -246,6 +246,7 @@ 580C37841AB104AF0015E709 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -257,6 +258,7 @@ XCTest, ); PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -264,6 +266,7 @@ 580C37851AB104AF0015E709 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj b/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj index d9e3b9f5b695ae..38ac20c73c64d4 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj/project.pbxproj @@ -214,6 +214,7 @@ 832C81951AAF6DF0007FA2F7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -224,6 +225,7 @@ "-llibicucore", ); PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -231,6 +233,7 @@ 832C81961AAF6DF0007FA2F7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Libraries/Text/RCTText.xcodeproj/project.pbxproj b/Libraries/Text/RCTText.xcodeproj/project.pbxproj index 0304ef851ea3cf..3c4bcf5bae846f 100644 --- a/Libraries/Text/RCTText.xcodeproj/project.pbxproj +++ b/Libraries/Text/RCTText.xcodeproj/project.pbxproj @@ -222,6 +222,7 @@ 58B511B01A9E6C1300147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -229,6 +230,7 @@ ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -236,6 +238,7 @@ 58B511B11A9E6C1300147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Libraries/Vibration/RCTVibration.xcodeproj/project.pbxproj b/Libraries/Vibration/RCTVibration.xcodeproj/project.pbxproj index bc8a47cbf7c9ad..f8aec3fedde4d0 100644 --- a/Libraries/Vibration/RCTVibration.xcodeproj/project.pbxproj +++ b/Libraries/Vibration/RCTVibration.xcodeproj/project.pbxproj @@ -208,6 +208,7 @@ 832C81951AAF6DF0007FA2F7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -215,6 +216,7 @@ ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -222,6 +224,7 @@ 832C81961AAF6DF0007FA2F7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c index 21dec570ac3522..2b168e44a29e19 100644 --- a/React/Layout/Layout.c +++ b/React/Layout/Layout.c @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<24fa633b4dd81b7fb40c2b2b0b7c97d0>> * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * !! This file is a check-in from github! !! @@ -642,19 +642,6 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { } } - float containerMainAxis = node->layout.dimensions[dim[mainAxis]]; - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (isUndefined(node->layout.dimensions[dim[mainAxis]])) { - containerMainAxis = fmaxf( - // We're missing the last padding at this point to get the final - // dimension - mainDim + getPaddingAndBorder(node, trailing[mainAxis]), - // We can never assign a width smaller than the padding and borders - getPaddingAndBorderAxis(node, mainAxis) - ); - } - float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { containerCrossAxis = fmaxf( diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 8823e15655c0d3..3364cce76fbfe8 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -625,6 +625,7 @@ 83CBBA401A601D0F00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -636,6 +637,7 @@ ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; }; name = Debug; @@ -643,6 +645,7 @@ 83CBBA411A601D0F00E9B192 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -650,6 +653,7 @@ ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; SKIP_INSTALL = YES; }; name = Release; From a635a73abc352d2ae229d2b0b7569359164e4135 Mon Sep 17 00:00:00 2001 From: Andrei Coman Date: Fri, 24 Apr 2015 09:12:04 -0700 Subject: [PATCH 092/250] [react_native] JS files from D2020585: [react_native] Remove focus for TextInputs when they unmount --- Libraries/Components/TextInput/TextInput.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index fb5f99949fbdcd..dfd3ab1a128469 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -350,6 +350,9 @@ var TextInput = React.createClass({ componentWillUnmount: function() { this._focusSubscription && this._focusSubscription.remove(); + if (this.isFocused()) { + this.blur(); + } }, _bufferTimeout: (undefined: ?number), From 861c66e587ab233a701bc567dd7d1f210a43f2ef Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 24 Apr 2015 10:31:28 -0700 Subject: [PATCH 093/250] [ReactNative] Fix launchEditor script --- packager/launchEditor.js | 45 +++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/packager/launchEditor.js b/packager/launchEditor.js index cf89ed4f04b779..b572b5cbd7c314 100644 --- a/packager/launchEditor.js +++ b/packager/launchEditor.js @@ -8,23 +8,21 @@ */ 'use strict'; +var chalk = require('chalk'); var fs = require('fs'); -var spawn = require('child_process').spawn; - -var firstLaunch = true; - -function guessEditor() { - if (firstLaunch) { - console.log('When you see Red Box with stack trace, you can click any ' + - 'stack frame to jump to the source file. The packager will launch your ' + - 'editor of choice. It will first look at REACT_EDITOR environment ' + - 'variable, then at EDITOR. To set it up, you can add something like ' + - 'REACT_EDITOR=atom to your .bashrc.'); - firstLaunch = false; - } - - var editor = process.env.REACT_EDITOR || process.env.EDITOR || 'subl'; - return editor; +var exec = require('child_process').exec; + +function printInstructions(title) { + console.log([ + '', + chalk.bgBlue.white.bold(' ' + title + ' '), + ' When you see Red Box with stack trace, you can click any ', + ' stack frame to jump to the source file. The packager will launch your ', + ' editor of choice. It will first look at REACT_EDITOR environment ', + ' variable, then at EDITOR. To set it up, you can add something like ', + ' REACT_EDITOR=atom to your .bashrc.', + '' + ].join('\n')); } function launchEditor(fileName, lineNumber) { @@ -37,9 +35,18 @@ function launchEditor(fileName, lineNumber) { argument += ':' + lineNumber; } - var editor = guessEditor(); - console.log('Opening ' + fileName + ' with ' + editor); - spawn(editor, [argument], { stdio: ['pipe', 'pipe', process.stderr] }); + var editor = process.env.REACT_EDITOR || process.env.EDITOR; + if (editor) { + console.log('Opening ' + chalk.underline(fileName) + ' with ' + chalk.bold(editor)); + exec(editor + ' ' + argument, function(error) { + if (error) { + console.log(chalk.red(error.message)); + printInstructions('How to fix'); + } + }); + } else { + printInstructions('PRO TIP'); + } } module.exports = launchEditor; From d094952725b86acd955b8a7f7821c77f3b5877e3 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Fri, 24 Apr 2015 10:23:51 -0700 Subject: [PATCH 094/250] [ReactNative] Re-applied D2011598 --- Libraries/Utilities/MessageQueue.js | 32 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/Libraries/Utilities/MessageQueue.js b/Libraries/Utilities/MessageQueue.js index c047d06deecf93..576aaa9917bba2 100644 --- a/Libraries/Utilities/MessageQueue.js +++ b/Libraries/Utilities/MessageQueue.js @@ -10,7 +10,9 @@ * @flow */ 'use strict'; + var ErrorUtils = require('ErrorUtils'); +var ReactUpdates = require('ReactUpdates'); var invariant = require('invariant'); var warning = require('warning'); @@ -307,21 +309,23 @@ var MessageQueueMixin = { ); }, - processBatch: function (batch) { + processBatch: function(batch) { var self = this; - batch.forEach(function (call) { - invariant( - call.module === 'BatchedBridge', - 'All the calls should pass through the BatchedBridge module' - ); - if (call.method === 'callFunctionReturnFlushedQueue') { - self.callFunction.apply(self, call.args); - } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { - self.invokeCallback.apply(self, call.args); - } else { - throw new Error( - 'Unrecognized method called on BatchedBridge: ' + call.method); - } + ReactUpdates.batchedUpdates(function() { + batch.forEach(function(call) { + invariant( + call.module === 'BatchedBridge', + 'All the calls should pass through the BatchedBridge module' + ); + if (call.method === 'callFunctionReturnFlushedQueue') { + self.callFunction.apply(self, call.args); + } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { + self.invokeCallback.apply(self, call.args); + } else { + throw new Error( + 'Unrecognized method called on BatchedBridge: ' + call.method); + } + }); }); return this.flushedQueue(); }, From 970bff613259c4735729babcc191c2f3c7a4348b Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 24 Apr 2015 10:56:58 -0700 Subject: [PATCH 095/250] [ReactNative] Enable resolveAssetSource for Image.android --- Libraries/Image/__tests__/resolveAssetSource-test.js | 6 ++++++ Libraries/Image/resolveAssetSource.js | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js index 26f3c9ea32e999..632ff96567ee1c 100644 --- a/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -32,6 +32,12 @@ describe('resolveAssetSource', () => { expect(resolveAssetSource(source2)).toBe(source2); }); + it('ignores any weird data', () => { + expect(resolveAssetSource(null)).toBe(null); + expect(resolveAssetSource(42)).toBe(null); + expect(resolveAssetSource('nonsense')).toBe(null); + }); + describe('bundle was loaded from network', () => { beforeEach(() => { SourceCode.scriptURL = 'http://10.0.0.1:8081/main.bundle'; diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js index da136e9a73d583..02189155877006 100644 --- a/Libraries/Image/resolveAssetSource.js +++ b/Libraries/Image/resolveAssetSource.js @@ -43,10 +43,11 @@ function pickScale(scales, deviceScale) { return scales[scales.length - 1] || 1; } -// TODO(frantic): -// * Pick best scale and append @Nx to file path -// * We are currently using httpServerLocation for both http and in-app bundle function resolveAssetSource(source) { + if (!source || typeof source !== 'object') { + return null; + } + if (!source.__packager_asset) { return source; } From 1f98c843b0ada05736fbe76d3f183127e754cdc6 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Fri, 24 Apr 2015 11:02:40 -0700 Subject: [PATCH 096/250] [react-native] Update react-tools to 0.13.2* --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce743e3bf6d1d9..5b7bedeaabafe5 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "optimist": "0.6.1", "promise": "^7.0.0", "react-timer-mixin": "^0.13.1", - "react-tools": "0.13.1", + "react-tools": "0.13.2", "rebound": "^0.0.12", "sane": "1.0.3", "source-map": "0.1.31", From 5225acea01734a81ac3543b91de7d2f565440867 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 24 Apr 2015 12:15:11 -0700 Subject: [PATCH 097/250] Fix tests --- Libraries/Utilities/__mocks__/PixelRatio.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Libraries/Utilities/__mocks__/PixelRatio.js diff --git a/Libraries/Utilities/__mocks__/PixelRatio.js b/Libraries/Utilities/__mocks__/PixelRatio.js new file mode 100644 index 00000000000000..bcf04d7fe48a6f --- /dev/null +++ b/Libraries/Utilities/__mocks__/PixelRatio.js @@ -0,0 +1,20 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + */ +'use strict'; + +var PixelRatio = { + startDetecting: function () { + // noop for our implementation + }, + + get: function() { + return 2; + }, + + getPixelSizeForLayoutSize: function (layoutSize) { + return Math.round(layoutSize * PixelRatio.get()); + } +}; + +module.exports = PixelRatio; From 46db1826c54decb806c8011ba2abe2a62ac0ddac Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Fri, 24 Apr 2015 11:58:31 -0700 Subject: [PATCH 098/250] [react_native] JS files from D2017699: Expose JavaScriptEnabled property for WebView component on android. --- Examples/UIExplorer/WebViewExample.js | 1 + Libraries/Components/WebView/WebView.android.js | 3 +++ Libraries/Components/WebView/WebView.ios.js | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/Examples/UIExplorer/WebViewExample.js b/Examples/UIExplorer/WebViewExample.js index 8813a8afdb1a01..d1e990cb4b4995 100644 --- a/Examples/UIExplorer/WebViewExample.js +++ b/Examples/UIExplorer/WebViewExample.js @@ -94,6 +94,7 @@ var WebViewExample = React.createClass({ automaticallyAdjustContentInsets={false} style={styles.webView} url={this.state.url} + javaScriptEnabledAndroid={true} onNavigationStateChange={this.onNavigationStateChange} startInLoadingState={true} /> diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js index bfc823f9f85928..959422bbc9888a 100644 --- a/Libraries/Components/WebView/WebView.android.js +++ b/Libraries/Components/WebView/WebView.android.js @@ -42,6 +42,7 @@ var WebView = React.createClass({ onNavigationStateChange: PropTypes.func, startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load style: View.propTypes.style, + javaScriptEnabledAndroid: PropTypes.bool, /** * Used to locate this view in end-to-end tests. */ @@ -90,6 +91,7 @@ var WebView = React.createClass({ key="webViewKey" style={webViewStyles} url={this.props.url} + javaScriptEnabledAndroid={this.props.javaScriptEnabledAndroid} contentInset={this.props.contentInset} automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets} onLoadingStart={this.onLoadingStart} @@ -157,6 +159,7 @@ var WebView = React.createClass({ var RCTWebView = createReactIOSNativeComponentClass({ validAttributes: merge(ReactIOSViewAttributes.UIView, { url: true, + javaScriptEnabledAndroid: true, }), uiViewClassName: 'RCTWebView', }); diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index c4e4fbcd3299c8..ed2c98fae02f5a 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -92,6 +92,10 @@ var WebView = React.createClass({ onNavigationStateChange: PropTypes.func, startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load style: View.propTypes.style, + /** + * Used for android only, JS is enabled by default for WebView on iOS + */ + javaScriptEnabledAndroid: PropTypes.bool, }, getInitialState: function() { From 846585941159785a3a685db152001d849d60bc75 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 24 Apr 2015 13:14:24 -0700 Subject: [PATCH 099/250] Fix doc generation for Geolocation --- Libraries/Utilities/__mocks__/ErrorUtils.js | 23 +++++++++++++++++++++ website/server/extractDocs.js | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Libraries/Utilities/__mocks__/ErrorUtils.js diff --git a/Libraries/Utilities/__mocks__/ErrorUtils.js b/Libraries/Utilities/__mocks__/ErrorUtils.js new file mode 100644 index 00000000000000..99db791774a441 --- /dev/null +++ b/Libraries/Utilities/__mocks__/ErrorUtils.js @@ -0,0 +1,23 @@ +// This mock only provides short-circuited methods of applyWithGuard and guard. +// A lot of modules rely on these two functions. This mock relieves their tests +// from depending on the real ErrorUtils module. If you need real error handling +// don't use this mock. +'use strict'; + +function execute(fun, context, args) { + return fun.apply(context, args); +}; + +function reportError(error) { + throw error; +} + +var ErrorUtils = { + apply: jest.genMockFunction().mockImplementation(execute), + applyWithGuard: jest.genMockFunction().mockImplementation(execute), + inGuard: jest.genMockFunction().mockReturnValue(true), + reportError: jest.genMockFunction().mockImplementation(reportError), + setGlobalHandler: jest.genMockFunction(), +}; + +module.exports = ErrorUtils; diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js index 54252af5b5e11e..d96514226d1f10 100644 --- a/website/server/extractDocs.js +++ b/website/server/extractDocs.js @@ -154,7 +154,7 @@ var styles = [ ]; var polyfills = [ - '../Libraries/GeoLocation/Geolocation.ios.js', + '../Libraries/GeoLocation/Geolocation.js', ]; var all = components From 5db06ac511f7d416e885fe1bfd609f2ea7d165e2 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Fri, 24 Apr 2015 21:16:12 +0100 Subject: [PATCH 100/250] Add some common objc nits to the style guide @nicklockwood @a2 @vjeux Want to add something else? (or remove something) --- CONTRIBUTING.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32b4d2f93290b1..6bb19e007ba308 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,14 +52,27 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe ### Code -* Use semicolons; +#### General + * Add trailing commas, * 2 spaces for indentation (no tabs) -* Prefer `'` over `"` -* `'use strict';` -* 80 character line length * "Attractive" + +#### JavaScript + +* Use semicolons; +* `'use strict';` +* Prefer `'` over `"` * Do not use the optional parameters of `setTimeout` and `setInterval` +* 80 character line length + +#### Objective-C + +* Space after `@property` declarations +* Brackets on *every* `if`, on the *same* line +* `- method`, `@interface`, and `@implementation` brackets on the following line +* *Try* to keep it around 80 characters line length (sometimes it's just not possible...) +* `*` operator goes with the variable name (e.g. `NSObject *variableName;`) ### Documentation From a2dd7fc69fd869b19cf7dff5585de00681b6d756 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 24 Apr 2015 14:43:14 -0700 Subject: [PATCH 101/250] [ReactNative] Fix dimensions in jest tests --- .../__mocks__/NativeModules.js | 1 + Libraries/Utilities/Dimensions.js | 2 +- Libraries/Utilities/__mocks__/ErrorUtils.js | 23 +++++++++++++++++++ Libraries/Utilities/__mocks__/PixelRatio.js | 20 ++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 Libraries/Utilities/__mocks__/ErrorUtils.js create mode 100644 Libraries/Utilities/__mocks__/PixelRatio.js diff --git a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js index 28da1bc321fe53..7eed36e01b1d87 100644 --- a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js +++ b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js @@ -29,6 +29,7 @@ var NativeModules = { UIManager: { customBubblingEventTypes: {}, customDirectEventTypes: {}, + Dimensions: {}, }, AsyncLocalStorage: { getItem: jest.genMockFunction(), diff --git a/Libraries/Utilities/Dimensions.js b/Libraries/Utilities/Dimensions.js index fe28a7684c9d46..b93000a33a8f50 100644 --- a/Libraries/Utilities/Dimensions.js +++ b/Libraries/Utilities/Dimensions.js @@ -20,7 +20,7 @@ var dimensions = NativeModules.UIManager.Dimensions; // We calculate the window dimensions in JS so that we don't encounter loss of // precision in transferring the dimensions (which could be non-integers) over // the bridge. -if (dimensions.windowPhysicalPixels) { +if (dimensions && dimensions.windowPhysicalPixels) { // parse/stringify => Clone hack dimensions = JSON.parse(JSON.stringify(dimensions)); diff --git a/Libraries/Utilities/__mocks__/ErrorUtils.js b/Libraries/Utilities/__mocks__/ErrorUtils.js new file mode 100644 index 00000000000000..99db791774a441 --- /dev/null +++ b/Libraries/Utilities/__mocks__/ErrorUtils.js @@ -0,0 +1,23 @@ +// This mock only provides short-circuited methods of applyWithGuard and guard. +// A lot of modules rely on these two functions. This mock relieves their tests +// from depending on the real ErrorUtils module. If you need real error handling +// don't use this mock. +'use strict'; + +function execute(fun, context, args) { + return fun.apply(context, args); +}; + +function reportError(error) { + throw error; +} + +var ErrorUtils = { + apply: jest.genMockFunction().mockImplementation(execute), + applyWithGuard: jest.genMockFunction().mockImplementation(execute), + inGuard: jest.genMockFunction().mockReturnValue(true), + reportError: jest.genMockFunction().mockImplementation(reportError), + setGlobalHandler: jest.genMockFunction(), +}; + +module.exports = ErrorUtils; diff --git a/Libraries/Utilities/__mocks__/PixelRatio.js b/Libraries/Utilities/__mocks__/PixelRatio.js new file mode 100644 index 00000000000000..bcf04d7fe48a6f --- /dev/null +++ b/Libraries/Utilities/__mocks__/PixelRatio.js @@ -0,0 +1,20 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + */ +'use strict'; + +var PixelRatio = { + startDetecting: function () { + // noop for our implementation + }, + + get: function() { + return 2; + }, + + getPixelSizeForLayoutSize: function (layoutSize) { + return Math.round(layoutSize * PixelRatio.get()); + } +}; + +module.exports = PixelRatio; From a1a15bda062d3e51e4c8fcfc00ab78cd27bd4fc9 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 24 Apr 2015 14:57:24 -0700 Subject: [PATCH 102/250] [ReactNative] Fix reloading in debug mode sometimes crashes packager --- packager/webSocketProxy.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packager/webSocketProxy.js b/packager/webSocketProxy.js index 8223bbf24b0e78..f863621362e421 100644 --- a/packager/webSocketProxy.js +++ b/packager/webSocketProxy.js @@ -34,7 +34,12 @@ function attachToServer(server, path) { ws.on('message', function(message) { allClientsExcept(ws).forEach(function(cn) { - cn.send(message); + try { + // Sometimes this call throws 'not opened' + cn.send(message); + } catch(e) { + console.warn('WARN: ' + e.message); + } }); }); }); From 68eb3e49067d10ccc11bdf7c2839406cb6ce0ac4 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 24 Apr 2015 16:10:46 -0700 Subject: [PATCH 103/250] [ReactNative] geolocation method docs --- Examples/UIExplorer/GeolocationExample.js | 3 ++- Libraries/Geolocation/Geolocation.js | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Examples/UIExplorer/GeolocationExample.js b/Examples/UIExplorer/GeolocationExample.js index c55bd351b09ce4..9bd744678df243 100644 --- a/Examples/UIExplorer/GeolocationExample.js +++ b/Examples/UIExplorer/GeolocationExample.js @@ -50,7 +50,8 @@ var GeolocationExample = React.createClass({ componentDidMount: function() { navigator.geolocation.getCurrentPosition( (initialPosition) => this.setState({initialPosition}), - (error) => console.error(error) + (error) => console.error(error), + {enableHighAccuracy: true, timeout: 100, maximumAge: 1000} ); this.watchID = navigator.geolocation.watchPosition((lastPosition) => { this.setState({lastPosition}); diff --git a/Libraries/Geolocation/Geolocation.js b/Libraries/Geolocation/Geolocation.js index 13fe40a2364447..fae309aef5fe68 100644 --- a/Libraries/Geolocation/Geolocation.js +++ b/Libraries/Geolocation/Geolocation.js @@ -22,6 +22,12 @@ var subscriptions = []; var updatesEnabled = false; +type GeoOptions = { + timeout: number; + maximumAge: number; + enableHighAccuracy: bool; +} + /** * You need to include the `NSLocationWhenInUseUsageDescription` key * in Info.plist to enable geolocation. Geolocation is enabled by default @@ -32,10 +38,14 @@ var updatesEnabled = false; */ var Geolocation = { + /* + * Invokes the success callback once with the latest location info. Supported + * options: timeout (ms), maximumAge (ms), enableHighAccuracy (bool) + */ getCurrentPosition: function( geo_success: Function, geo_error?: Function, - geo_options?: Object + geo_options?: GeoOptions ) { invariant( typeof geo_success === 'function', @@ -48,7 +58,11 @@ var Geolocation = { ); }, - watchPosition: function(success: Function, error?: Function, options?: Object): number { + /* + * Invokes the success callback whenever the location changes. Supported + * options: timeout (ms), maximumAge (ms), enableHighAccuracy (bool) + */ + watchPosition: function(success: Function, error?: Function, options?: GeoOptions): number { if (!updatesEnabled) { RCTLocationObserver.startObserving(options || {}); updatesEnabled = true; From 1cc0c965088354bc9417dc1f97b049d8e70984db Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 24 Apr 2015 18:00:20 -0700 Subject: [PATCH 104/250] [React Native] Bring back console.error redboxes with disable flag --- Libraries/Utilities/stringifySafe.js | 42 ++++++++++++++++++++++++++++ React/Executors/RCTContextExecutor.m | 17 +++++------ 2 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 Libraries/Utilities/stringifySafe.js diff --git a/Libraries/Utilities/stringifySafe.js b/Libraries/Utilities/stringifySafe.js new file mode 100644 index 00000000000000..053ea69849e821 --- /dev/null +++ b/Libraries/Utilities/stringifySafe.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule stringifySafe + * @flow + */ +'use strict'; + +/** + * Tries to stringify with JSON.stringify and toString, but catches exceptions + * (e.g. from circular objects) and always returns a string and never throws. + */ +function stringifySafe(arg: any): string { + var ret; + if (arg === undefined) { + ret = 'undefined'; + } else if (arg === null) { + ret = 'null'; + } else if (typeof arg === 'string') { + ret = '"' + arg + '"'; + } else { + // Perform a try catch, just in case the object has a circular + // reference or stringify throws for some other reason. + try { + ret = JSON.stringify(arg); + } catch (e) { + if (typeof arg.toString === 'function') { + try { + ret = arg.toString(); + } catch (E) {} + } + } + } + return ret || '["' + typeof arg + '" failed to stringify]'; +} + +module.exports = stringifySafe; diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 86444dd2a7f903..8f931d96ab3b3f 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -74,12 +74,12 @@ @implementation RCTContextExecutor static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { if (argumentCount > 0) { - JSStringRef string = JSValueToStringCopy(context, arguments[0], exception); - if (!string) { + JSStringRef messageRef = JSValueToStringCopy(context, arguments[0], exception); + if (!messageRef) { return JSValueMakeUndefined(context); } - NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string); - JSStringRelease(string); + NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, messageRef); + JSStringRelease(messageRef); NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: @"( stack: )?([_a-z0-9]*)@?(http://|file:///)[a-z.0-9:/_-]+/([a-z0-9_]+).includeRequire.runModule.bundle(:[0-9]+:[0-9]+)" options:NSRegularExpressionCaseInsensitive @@ -89,14 +89,11 @@ static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, range:(NSRange){0, message.length} withTemplate:@"[$4$5] \t$2"]; - // TODO: it would be good if log level was sent as a param, instead of this hack RCTLogLevel level = RCTLogLevelInfo; - if ([message rangeOfString:@"error" options:NSCaseInsensitiveSearch].length) { - level = RCTLogLevelError; - } else if ([message rangeOfString:@"warning" options:NSCaseInsensitiveSearch].length) { - level = RCTLogLevelWarning; + if (argumentCount > 1) { + level = MAX(level, JSValueToNumber(context, arguments[1], exception) - 1); } - _RCTLogFormat(level, NULL, -1, @"%@", message); + RCTGetLogFunction()(level, nil, nil, message); } return JSValueMakeUndefined(context); From 4cbd7ceb8173500fe3f6ffe5c777115585dfeb1d Mon Sep 17 00:00:00 2001 From: Gabe Levi Date: Fri, 24 Apr 2015 22:43:01 -0700 Subject: [PATCH 105/250] [Flow] Add versions to fbobjc .flowconfigs --- .flowconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.flowconfig b/.flowconfig index 4f9e8498e5c341..054b0d01d80f30 100644 --- a/.flowconfig +++ b/.flowconfig @@ -29,3 +29,6 @@ Examples/UIExplorer/ImageMocks.js [options] module.system=haste + +[version] +0.10.0 From bd5736414ad0e60507bc0d823e9ff1e934912bf8 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Sat, 25 Apr 2015 13:15:10 -0700 Subject: [PATCH 106/250] [ReactNative] Fix error when reload during profile --- React/Executors/RCTContextExecutor.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 8f931d96ab3b3f..48d371b7e63cfd 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -302,7 +302,7 @@ - (void)executeApplicationScript:(NSString *)script } onComplete(error); } - }), @"js_call", (@{ @"url": sourceURL }))]; + }), @"js_call", (@{ @"url": sourceURL.absoluteString }))]; } - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block From 8a3b0fa9e81f74d25816e2fe17268a1ee829d70a Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sat, 25 Apr 2015 14:52:18 -0700 Subject: [PATCH 107/250] Fix edge cases and add tests for +[RCTConvert NSURL:] --- React/Base/RCTConvert.m | 51 +++++---- React/Base/RCTJavaScriptLoader.m | 180 ++++++++++++------------------- React/Base/RCTRootView.m | 10 -- 3 files changed, 101 insertions(+), 140 deletions(-) diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index f1ed77298dafdb..eacb03b857a91d 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -49,11 +49,11 @@ + (NSNumber *)NSNumber:(id)json }); NSNumber *number = [formatter numberFromString:json]; if (!number) { - RCTLogError(@"JSON String '%@' could not be interpreted as a number", json); + RCTLogConvertError(json, "a number"); } return number; } else if (json && json != [NSNull null]) { - RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a number", json, [json classForCoder]); + RCTLogConvertError(json, "a number"); } return nil; } @@ -66,30 +66,38 @@ + (NSData *)NSData:(id)json + (NSURL *)NSURL:(id)json { - if (!json || json == (id)kCFNull) { + NSString *path = [self NSString:json]; + if (!path.length) { return nil; } - if (![json isKindOfClass:[NSString class]]) { - RCTLogError(@"Expected NSString for NSURL, received %@: %@", [json classForCoder], json); - return nil; - } + @try { // NSURL has a history of crashing with bad input, so let's be safe - NSString *path = json; - if ([path isAbsolutePath]) - { + NSURL *URL = [NSURL URLWithString:path]; + if (URL.scheme) { // Was a well-formed absolute URL + return URL; + } + + // Check if it has a scheme + if ([path rangeOfString:@"[a-zA-Z][a-zA-Z._-]+:" options:NSRegularExpressionSearch].location == 0) { + path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + URL = [NSURL URLWithString:path]; + if (URL) { + return URL; + } + } + + // Assume that it's a local path + path = [path stringByRemovingPercentEncoding]; + if (![path isAbsolutePath]) { + path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:path]; + } return [NSURL fileURLWithPath:path]; } - else if ([path length]) - { - NSURL *URL = [NSURL URLWithString:path relativeToURL:[[NSBundle mainBundle] resourceURL]]; - if ([URL isFileURL] && ![[NSFileManager defaultManager] fileExistsAtPath:[URL path]]) { - RCTLogWarn(@"The file '%@' does not exist", URL); - return nil; - } - return URL; + @catch (__unused NSException *e) { + RCTLogConvertError(json, "a valid URL"); + return nil; } - return nil; } + (NSURLRequest *)NSURLRequest:(id)json @@ -112,11 +120,12 @@ + (NSDate *)NSDate:(id)json }); NSDate *date = [formatter dateFromString:json]; if (!date) { - RCTLogError(@"JSON String '%@' could not be interpreted as a date. Expected format: YYYY-MM-DD'T'HH:mm:ss.sssZ", json); + RCTLogError(@"JSON String '%@' could not be interpreted as a date. " + "Expected format: YYYY-MM-DD'T'HH:mm:ss.sssZ", json); } return date; } else if (json && json != [NSNull null]) { - RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a date", json, [json classForCoder]); + RCTLogConvertError(json, "a date"); } return nil; } diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index dd8fab461868fd..2e7d21b9442fe7 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -10,47 +10,15 @@ #import "RCTJavaScriptLoader.h" #import "RCTBridge.h" -#import "RCTInvalidating.h" -#import "RCTLog.h" -#import "RCTRedBox.h" +#import "RCTConvert.h" #import "RCTSourceCode.h" #import "RCTUtils.h" -#define NO_REMOTE_MODULE @"Could not fetch module bundle %@. Ensure node server is running.\n\nIf it timed out, try reloading." -#define NO_LOCAL_BUNDLE @"Could not load local bundle %@. Ensure file exists." - -#define CACHE_DIR @"RCTJSBundleCache" - -#pragma mark - Application Engine - -/** - * TODO: - * - Add window resize rotation events matching the DOM API. - * - Device pixel ration hooks. - * - Source maps. - */ @implementation RCTJavaScriptLoader { __weak RCTBridge *_bridge; } -/** - * `CADisplayLink` code copied from Ejecta but we've placed the JavaScriptCore - * engine in its own dedicated thread. - * - * TODO: Try adding to the `RCTJavaScriptExecutor`'s thread runloop. Removes one - * additional GCD dispatch per frame and likely makes it so that other UIThread - * operations don't delay the dispatch (so we can begin working in JS much - * faster.) Event handling must still be sent via a GCD dispatch, of course. - * - * We must add the display link to two runloops in order to get setTimeouts to - * fire during scrolling. (`NSDefaultRunLoopMode` and `UITrackingRunLoopMode`) - * TODO: We can invent a `requestAnimationFrame` and - * `requestAvailableAnimationFrame` to control if callbacks can be fired during - * an animation. - * http://stackoverflow.com/questions/12622800/why-does-uiscrollview-pause-my-cadisplaylink - * - */ - (instancetype)initWithBridge:(RCTBridge *)bridge { if ((self = [super init])) { @@ -61,92 +29,86 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onComplete { - if (scriptURL == nil) { + // Sanitize the script URL + scriptURL = [RCTConvert NSURL:scriptURL.absoluteString]; + + if (!scriptURL || + ([scriptURL isFileURL] && ![[NSFileManager defaultManager] fileExistsAtPath:scriptURL.path])) { NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{ - NSLocalizedDescriptionKey: @"No script URL provided" + NSLocalizedDescriptionKey: scriptURL ? [NSString stringWithFormat:@"Script at '%@' could not be found.", scriptURL] : @"No script URL provided" }]; onComplete(error); return; } - if ([scriptURL isFileURL]) { - NSString *bundlePath = [[NSBundle bundleForClass:[self class]] resourcePath]; - NSString *localPath = [scriptURL.absoluteString substringFromIndex:@"file://".length]; - - if (![localPath hasPrefix:bundlePath]) { - NSString *absolutePath = [NSString stringWithFormat:@"%@/%@", bundlePath, localPath]; - scriptURL = [NSURL fileURLWithPath:absolutePath]; - } - } - NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:scriptURL completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { - // Handle general request errors - if (error) { - if ([[error domain] isEqualToString:NSURLErrorDomain]) { - NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:[scriptURL absoluteString]]; - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: desc, - NSLocalizedFailureReasonErrorKey: [error localizedDescription], - NSUnderlyingErrorKey: error, - }; - error = [NSError errorWithDomain:@"JSServer" - code:error.code - userInfo:userInfo]; - } - onComplete(error); - return; - } - - // Parse response as text - NSStringEncoding encoding = NSUTF8StringEncoding; - if (response.textEncodingName != nil) { - CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); - if (cfEncoding != kCFStringEncodingInvalidId) { - encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); - } - } - NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding]; - - // Handle HTTP errors - if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) { - NSDictionary *userInfo; - NSDictionary *errorDetails = RCTJSONParse(rawText, nil); - if ([errorDetails isKindOfClass:[NSDictionary class]] && - [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) { - NSMutableArray *fakeStack = [[NSMutableArray alloc] init]; - for (NSDictionary *err in errorDetails[@"errors"]) { - [fakeStack addObject: @{ - @"methodName": err[@"description"] ?: @"", - @"file": err[@"filename"] ?: @"", - @"lineNumber": err[@"lineNumber"] ?: @0 - }]; - } - userInfo = @{ - NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided", - @"stack": fakeStack, - }; - } else { - userInfo = @{NSLocalizedDescriptionKey: rawText}; - } - error = [NSError errorWithDomain:@"JSServer" - code:[(NSHTTPURLResponse *)response statusCode] - userInfo:userInfo]; - - onComplete(error); - return; - } - RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; - sourceCodeModule.scriptURL = scriptURL; - sourceCodeModule.scriptText = rawText; + // Handle general request errors + if (error) { + if ([[error domain] isEqualToString:NSURLErrorDomain]) { + NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:[scriptURL absoluteString]]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: [error localizedDescription], + NSUnderlyingErrorKey: error, + }; + error = [NSError errorWithDomain:@"JSServer" + code:error.code + userInfo:userInfo]; + } + onComplete(error); + return; + } - [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(scriptError); - }); - }]; - }]; + // Parse response as text + NSStringEncoding encoding = NSUTF8StringEncoding; + if (response.textEncodingName != nil) { + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (cfEncoding != kCFStringEncodingInvalidId) { + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); + } + } + NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding]; + + // Handle HTTP errors + if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) { + NSDictionary *userInfo; + NSDictionary *errorDetails = RCTJSONParse(rawText, nil); + if ([errorDetails isKindOfClass:[NSDictionary class]] && + [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) { + NSMutableArray *fakeStack = [[NSMutableArray alloc] init]; + for (NSDictionary *err in errorDetails[@"errors"]) { + [fakeStack addObject: @{ + @"methodName": err[@"description"] ?: @"", + @"file": err[@"filename"] ?: @"", + @"lineNumber": err[@"lineNumber"] ?: @0 + }]; + } + userInfo = @{ + NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided", + @"stack": fakeStack, + }; + } else { + userInfo = @{NSLocalizedDescriptionKey: rawText}; + } + error = [NSError errorWithDomain:@"JSServer" + code:[(NSHTTPURLResponse *)response statusCode] + userInfo:userInfo]; + + onComplete(error); + return; + } + RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; + sourceCodeModule.scriptURL = scriptURL; + sourceCodeModule.scriptText = rawText; + + [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { + dispatch_async(dispatch_get_main_queue(), ^{ + onComplete(scriptError); + }); + }]; + }]; [task resume]; } diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 45624efdcc1e8a..0c171951e28467 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -23,16 +23,6 @@ #import "RCTWebViewExecutor.h" #import "UIView+React.h" -/** - * HACK(t6568049) This should be removed soon, hiding to prevent people from - * relying on it - */ -@interface RCTBridge (RCTRootView) - -- (void)setJavaScriptExecutor:(id)executor; - -@end - @interface RCTUIManager (RCTRootView) - (NSNumber *)allocateRootTag; From dd6bce78e1879ff35946411b4499cf47fdb02e8f Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Sat, 25 Apr 2015 19:18:39 -0700 Subject: [PATCH 108/250] [ReactNative] Allow bridge modules to run on the JavaScript thread --- React/Base/RCTBridge.m | 25 +++++++++++++++++++------ React/Base/RCTBridgeModule.h | 10 ++++++++++ React/Executors/RCTContextExecutor.m | 17 +++++++++++------ React/Modules/RCTTiming.m | 4 +--- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 48fd672a3926d4..67c86fb1226656 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -31,6 +31,8 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification"; NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification"; +dispatch_queue_t const RCTJSThread = nil; + /** * Must be kept in sync with `MessageQueue.js`. */ @@ -795,6 +797,7 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL _bundleURL = bundleURL; _moduleProvider = block; _launchOptions = [launchOptions copy]; + [self setUp]; [self bindKeys]; } @@ -872,6 +875,8 @@ - (void)setUp dispatch_queue_t queue = [module methodQueue]; if (queue) { _queuesByID[moduleID] = queue; + } else { + _queuesByID[moduleID] = [NSNull null]; } } }]; @@ -1128,6 +1133,16 @@ - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete: #pragma mark - Payload Generation +- (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID +{ + id queue = _queuesByID[moduleID]; + if (queue == [NSNull null]) { + [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; + } else { + dispatch_async(queue ?: _methodQueue, block); + } +} + - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context { #if BATCHED_BRIDGE @@ -1235,10 +1250,9 @@ - (void)_handleBuffer:(id)buffer context:(NSNumber *)context // TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case? [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { if ([module respondsToSelector:@selector(batchDidComplete)]) { - dispatch_queue_t queue = _queuesByID[moduleID]; - dispatch_async(queue ?: _methodQueue, ^{ + [self dispatchBlock:^{ [module batchDidComplete]; - }); + } forModule:moduleID]; } }]; } @@ -1273,8 +1287,7 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i } __weak RCTBridge *weakSelf = self; - dispatch_queue_t queue = _queuesByID[moduleID]; - dispatch_async(queue ?: _methodQueue, ^{ + [self dispatchBlock:^{ RCTProfileBeginEvent(); __strong RCTBridge *strongSelf = weakSelf; @@ -1303,7 +1316,7 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i @"method": method.JSMethodName, @"selector": NSStringFromSelector(method.selector), }); - }); + } forModule:@(moduleID)]; return YES; } diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index dd6f61e235e279..34b861ff3f8f10 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -17,6 +17,16 @@ */ typedef void (^RCTResponseSenderBlock)(NSArray *response); +/** + * This constant can be returned from +methodQueue to force module + * methods to be called on the JavaScript thread. This can have serious + * implications for performance, so only use this if you're sure it's what + * you need. + * + * NOTE: RCTJSThread is not a real libdispatch queue + */ +extern const dispatch_queue_t RCTJSThread; + /** * Provides the interface needed to register a bridge module. */ diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 48d371b7e63cfd..edcd2aebd880c7 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -307,12 +307,17 @@ - (void)executeApplicationScript:(NSString *)script - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block { - if ([NSThread currentThread] != _javaScriptThread) { - [self performSelector:@selector(executeBlockOnJavaScriptQueue:) - onThread:_javaScriptThread withObject:block waitUntilDone:NO]; - } else { - block(); - } + /** + * Always dispatch async, ensure there are no sync calls on the JS thread + * otherwise timers can cause a deadlock + */ + [self performSelector:@selector(_runBlock:) + onThread:_javaScriptThread withObject:block waitUntilDone:NO]; +} + +- (void)_runBlock:(dispatch_block_t)block +{ + block(); } - (void)injectJSONText:(NSString *)script diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index 1d99c1a2d42663..e2df5befca18eb 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -110,7 +110,7 @@ - (void)dealloc - (dispatch_queue_t)methodQueue { - return dispatch_get_main_queue(); + return RCTJSThread; } - (BOOL)isValid @@ -131,8 +131,6 @@ - (void)stopTimers - (void)startTimers { - RCTAssertMainThread(); - if (![self isValid] || _timers.count == 0) { return; } From 8c8b28969edd9807f4797b95f8fb5ea8e8bef17d Mon Sep 17 00:00:00 2001 From: Robert Payne Date: Sun, 26 Apr 2015 18:06:43 +1200 Subject: [PATCH 109/250] Documentation for exporting Swift Native Modules --- docs/NativeModulesIOS.md | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/NativeModulesIOS.md b/docs/NativeModulesIOS.md index 51fbb22a30403b..5fec7ea183e920 100644 --- a/docs/NativeModulesIOS.md +++ b/docs/NativeModulesIOS.md @@ -7,11 +7,11 @@ permalink: docs/nativemodulesios.html next: nativecomponentsios --- -Sometimes an app needs access to platform API, and React Native doesn't have a corresponding module yet. Maybe you want to reuse some existing Objective-C or C++ code without having to reimplement it in JavaScript, or write some high performance, multi-threaded code such as for image processing, a database, or any number of advanced extensions. +Sometimes an app needs access to platform API, and React Native doesn't have a corresponding module yet. Maybe you want to reuse some existing Objective-C, Swift or C++ code without having to reimplement it in JavaScript, or write some high performance, multi-threaded code such as for image processing, a database, or any number of advanced extensions. We designed React Native such that it is possible for you to write real native code and have access to the full power of the platform. This is a more advanced feature and we don't expect it to be part of the usual development process, however it is essential that it exists. If React Native doesn't support a native feature that you need, you should be able to build it yourself. -This is a more advanced guide that shows how to build a native module. It assumes the reader knows Objective-C (Swift is not supported yet) and core libraries (Foundation, UIKit). +This is a more advanced guide that shows how to build a native module. It assumes the reader knows Objective-C or Swift and core libraries (Foundation, UIKit). ## iOS Calendar Module Example @@ -248,3 +248,39 @@ var subscription = DeviceEventEmitter.addListener( subscription.remove(); ``` For more examples of sending events to JavaScript, see [`RCTLocationObserver`](https://github.com/facebook/react-native/blob/master/Libraries/Geolocation/RCTLocationObserver.m). + +## Exporting Swift + +Swift doesn't have support for macros so exposing it to React Native requires a bit more setup but works relatively the same. + +Let's say we have the same `CalendarManager` but as a Swift class: + +```swift +// CalendarManager.swift + +@objc(CalendarManager) +class CalendarManager: NSObject { + + @objc func addEvent(name: String, location: String, date: NSNumber) -> Void { + // Date is ready to use! + } + +} +``` + +> **NOTE** It is important to use the @objc modifiers to ensure the class and functions are exported properly to the Objective-C runtime. + +Then create a private implementation file that will register the required information with the React Native bridge: + +```objc +// CalendarManagerBridge.m +#import "RCTBridgeModule.h" + +@interface RCT_EXTERN_MODULE(CalendarManager, NSObject) + +RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSNumber *)date) + +@end +``` + +You can also use `RCT_EXTERN_REMAP_MODULE` and `RCT_EXTERN_REMAP_METHOD` to alter the JavaScript name of the module or methods you are exporting. For more information see [`RCTBridgeModule`](https://github.com/facebook/react-native/blob/master/React/Base/RCTBridgeModule.h). \ No newline at end of file From 77e38b26c56450a085b2b6215d2fc07ee75f10e8 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sun, 26 Apr 2015 02:22:34 -0700 Subject: [PATCH 110/250] Improved RCTActivityIndicatorView and fixed some flow errors --- Examples/UIExplorer/UIExplorerList.js | 2 +- .../ActivityIndicatorIOS.ios.js | 41 +++++++------------ .../Components/TabBarIOS/TabBarItemIOS.ios.js | 5 ++- Libraries/ReactIOS/ReactIOSMount.js | 4 +- React/React.xcodeproj/project.pbxproj | 12 +++--- ...er.h => RCTActivityIndicatorViewManager.h} | 8 +++- ...er.m => RCTActivityIndicatorViewManager.m} | 30 +++++--------- 7 files changed, 46 insertions(+), 56 deletions(-) rename React/Views/{RCTUIActivityIndicatorViewManager.h => RCTActivityIndicatorViewManager.h} (64%) rename React/Views/{RCTUIActivityIndicatorViewManager.m => RCTActivityIndicatorViewManager.m} (52%) diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index 73010617732abd..df3c9bc7decf34 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -11,7 +11,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * @flow + * --flow disabled-- */ 'use strict'; diff --git a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js index 3a44020a663590..a3f1fe6be8c3d5 100644 --- a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js +++ b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js @@ -18,21 +18,16 @@ var React = require('React'); var StyleSheet = require('StyleSheet'); var View = require('View'); -var keyMirror = require('keyMirror'); var requireNativeComponent = require('requireNativeComponent'); var verifyPropTypes = require('verifyPropTypes'); -var SpinnerSize = keyMirror({ - large: null, - small: null, -}); - var GRAY = '#999999'; type DefaultProps = { animating: boolean; - size: 'small' | 'large'; color: string; + hidesWhenStopped: boolean; + size: 'small' | 'large'; }; var ActivityIndicatorIOS = React.createClass({ @@ -47,7 +42,10 @@ var ActivityIndicatorIOS = React.createClass({ * The foreground color of the spinner (default is gray). */ color: PropTypes.string, - + /** + * Whether the indicator should hide when not animating (true by default). + */ + hidesWhenStopped: PropTypes.bool, /** * Size of the indicator. Small has a height of 20, large has a height of 36. */ @@ -60,27 +58,18 @@ var ActivityIndicatorIOS = React.createClass({ getDefaultProps: function(): DefaultProps { return { animating: true, - size: SpinnerSize.small, color: GRAY, + hidesWhenStopped: true, + size: 'small', }; }, render: function() { - var style = styles.sizeSmall; - var NativeConstants = NativeModules.UIManager.UIActivityIndicatorView.Constants; - var activityIndicatorViewStyle = NativeConstants.StyleWhite; - if (this.props.size === 'large') { - style = styles.sizeLarge; - activityIndicatorViewStyle = NativeConstants.StyleWhiteLarge; - } + var {style, ...props} = this.props; + var sizeStyle = (this.props.size === 'large') ? styles.sizeLarge : styles.sizeSmall; return ( - - + + ); } @@ -99,15 +88,15 @@ var styles = StyleSheet.create({ } }); -var UIActivityIndicatorView = requireNativeComponent( - 'UIActivityIndicatorView', +var RCTActivityIndicatorView = requireNativeComponent( + 'RCTActivityIndicatorView', null ); if (__DEV__) { var nativeOnlyProps = {activityIndicatorViewStyle: true}; verifyPropTypes( ActivityIndicatorIOS, - UIActivityIndicatorView.viewConfig, + RCTActivityIndicatorView.viewConfig, nativeOnlyProps ); } diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js index 32945c4343474e..bb2a8e991b7ced 100644 --- a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -66,6 +66,9 @@ var TabBarItemIOS = React.createClass({ * blank content, you probably forgot to add a selected one. */ selected: React.PropTypes.bool, + /** + * React style object. + */ style: View.propTypes.style, /** * Text that appears under the icon. It is ignored when a system icon @@ -86,7 +89,7 @@ var TabBarItemIOS = React.createClass({ } }, - componentWillReceiveProps: function(nextProps: { selected: boolean }) { + componentWillReceiveProps: function(nextProps: { selected: any /* workaround for flow bug */ }) { if (this.state.hasBeenSelected || nextProps.selected) { this.setState({hasBeenSelected: true}); } diff --git a/Libraries/ReactIOS/ReactIOSMount.js b/Libraries/ReactIOS/ReactIOSMount.js index 730031672f253d..9b1428fdd6d27a 100644 --- a/Libraries/ReactIOS/ReactIOSMount.js +++ b/Libraries/ReactIOS/ReactIOSMount.js @@ -191,10 +191,10 @@ var ReactIOSMount = { * that has been rendered and unmounting it. There should just be one child * component at this time. */ - unmountComponentAtNode: function(containerTag: number): bool { + unmountComponentAtNode: function(containerTag: number): boolean { if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) { console.error('You cannot render into anything but a top root'); - return; + return false; } var containerID = ReactIOSTagHandles.tagToRootNodeID[containerTag]; diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 3364cce76fbfe8..fce2aae428aa55 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -33,7 +33,7 @@ 13B0801D1A69489C00A75B9A /* RCTNavItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080131A69489C00A75B9A /* RCTNavItemManager.m */; }; 13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080151A69489C00A75B9A /* RCTTextField.m */; }; 13B0801F1A69489C00A75B9A /* RCTTextFieldManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080171A69489C00A75B9A /* RCTTextFieldManager.m */; }; - 13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */; }; + 13B080201A69489C00A75B9A /* RCTActivityIndicatorViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080191A69489C00A75B9A /* RCTActivityIndicatorViewManager.m */; }; 13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */; }; 13C156051AB1A2840079392D /* RCTWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156021AB1A2840079392D /* RCTWebView.m */; }; 13C156061AB1A2840079392D /* RCTWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156041AB1A2840079392D /* RCTWebViewManager.m */; }; @@ -136,8 +136,8 @@ 13B080151A69489C00A75B9A /* RCTTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextField.m; sourceTree = ""; }; 13B080161A69489C00A75B9A /* RCTTextFieldManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextFieldManager.h; sourceTree = ""; }; 13B080171A69489C00A75B9A /* RCTTextFieldManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextFieldManager.m; sourceTree = ""; }; - 13B080181A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIActivityIndicatorViewManager.h; sourceTree = ""; }; - 13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIActivityIndicatorViewManager.m; sourceTree = ""; }; + 13B080181A69489C00A75B9A /* RCTActivityIndicatorViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTActivityIndicatorViewManager.h; sourceTree = ""; }; + 13B080191A69489C00A75B9A /* RCTActivityIndicatorViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActivityIndicatorViewManager.m; sourceTree = ""; }; 13B080231A694A8400A75B9A /* RCTWrapperViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWrapperViewController.h; sourceTree = ""; }; 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWrapperViewController.m; sourceTree = ""; }; 13C156011AB1A2840079392D /* RCTWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebView.h; sourceTree = ""; }; @@ -317,8 +317,8 @@ 13B080151A69489C00A75B9A /* RCTTextField.m */, 13B080161A69489C00A75B9A /* RCTTextFieldManager.h */, 13B080171A69489C00A75B9A /* RCTTextFieldManager.m */, - 13B080181A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.h */, - 13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */, + 13B080181A69489C00A75B9A /* RCTActivityIndicatorViewManager.h */, + 13B080191A69489C00A75B9A /* RCTActivityIndicatorViewManager.m */, 13E0674F1A70F44B002CDEE1 /* RCTView.h */, 13E067501A70F44B002CDEE1 /* RCTView.m */, 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */, @@ -499,7 +499,7 @@ 14F4D38B1AE1B7E40049C042 /* RCTProfile.m in Sources */, 14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */, 14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */, - 13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */, + 13B080201A69489C00A75B9A /* RCTActivityIndicatorViewManager.m in Sources */, 13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */, 58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */, 13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */, diff --git a/React/Views/RCTUIActivityIndicatorViewManager.h b/React/Views/RCTActivityIndicatorViewManager.h similarity index 64% rename from React/Views/RCTUIActivityIndicatorViewManager.h rename to React/Views/RCTActivityIndicatorViewManager.h index e5a10fdd75c746..cbd6816ae4cddf 100644 --- a/React/Views/RCTUIActivityIndicatorViewManager.h +++ b/React/Views/RCTActivityIndicatorViewManager.h @@ -9,6 +9,12 @@ #import "RCTViewManager.h" -@interface RCTUIActivityIndicatorViewManager : RCTViewManager +@interface RCTConvert (UIActivityIndicatorView) + ++ (UIActivityIndicatorViewStyle)UIActivityIndicatorViewStyle:(id)json; + +@end + +@interface RCTActivityIndicatorViewManager : RCTViewManager @end diff --git a/React/Views/RCTUIActivityIndicatorViewManager.m b/React/Views/RCTActivityIndicatorViewManager.m similarity index 52% rename from React/Views/RCTUIActivityIndicatorViewManager.m rename to React/Views/RCTActivityIndicatorViewManager.m index e2c9b3d353bf06..3876400dff3714 100644 --- a/React/Views/RCTUIActivityIndicatorViewManager.m +++ b/React/Views/RCTActivityIndicatorViewManager.m @@ -7,35 +7,37 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTUIActivityIndicatorViewManager.h" +#import "RCTActivityIndicatorViewManager.h" #import "RCTConvert.h" @implementation RCTConvert (UIActivityIndicatorView) +// NOTE: It's pointless to support UIActivityIndicatorViewStyleGray +// as we can set the color to any arbitrary value that we want to + RCT_ENUM_CONVERTER(UIActivityIndicatorViewStyle, (@{ - @"white-large": @(UIActivityIndicatorViewStyleWhiteLarge), - @"large-white": @(UIActivityIndicatorViewStyleWhiteLarge), - @"white": @(UIActivityIndicatorViewStyleWhite), - @"gray": @(UIActivityIndicatorViewStyleGray), + @"large": @(UIActivityIndicatorViewStyleWhiteLarge), + @"small": @(UIActivityIndicatorViewStyleWhite), }), UIActivityIndicatorViewStyleWhiteLarge, integerValue) @end -@implementation RCTUIActivityIndicatorViewManager +@implementation RCTActivityIndicatorViewManager -RCT_EXPORT_MODULE(UIActivityIndicatorViewManager) +RCT_EXPORT_MODULE() - (UIView *)view { return [[UIActivityIndicatorView alloc] init]; } -RCT_EXPORT_VIEW_PROPERTY(activityIndicatorViewStyle, UIActivityIndicatorViewStyle) RCT_EXPORT_VIEW_PROPERTY(color, UIColor) +RCT_EXPORT_VIEW_PROPERTY(hidesWhenStopped, BOOL) +RCT_REMAP_VIEW_PROPERTY(size, activityIndicatorViewStyle, UIActivityIndicatorViewStyle) RCT_CUSTOM_VIEW_PROPERTY(animating, BOOL, UIActivityIndicatorView) { - BOOL animating = json ? [json boolValue] : [defaultView isAnimating]; + BOOL animating = json ? [RCTConvert BOOL:json] : [defaultView isAnimating]; if (animating != [view isAnimating]) { if (animating) { [view startAnimating]; @@ -45,14 +47,4 @@ - (UIView *)view } } -- (NSDictionary *)constantsToExport -{ - return - @{ - @"StyleWhite": @(UIActivityIndicatorViewStyleWhite), - @"StyleWhiteLarge": @(UIActivityIndicatorViewStyleWhiteLarge), - @"StyleGray": @(UIActivityIndicatorViewStyleGray), - }; -} - @end From 89495635da993bed14456f4955c8b10c8e7372bd Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sun, 26 Apr 2015 19:44:55 +0100 Subject: [PATCH 111/250] Update NativeModulesIOS.md --- docs/NativeModulesIOS.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/NativeModulesIOS.md b/docs/NativeModulesIOS.md index 51fbb22a30403b..aaf18ebd7ac671 100644 --- a/docs/NativeModulesIOS.md +++ b/docs/NativeModulesIOS.md @@ -177,7 +177,6 @@ If you want to pass error-like objects to JavaScript, use `RCTMakeError` from [` The native module should not have any assumptions about what thread it is being called on. React Native invokes native modules methods on a separate serial GCD queue, but this is an implementation detail and might change. The `- (dispatch_queue_t)methodQueue` method allows the native module to specify which queue its methods should be run on. For example, if it needs to use a main-thread-only iOS API, it should specify this via: - ```objective-c - (dispatch_queue_t)methodQueue { @@ -194,6 +193,24 @@ Similarly, if an operation may take a long time to complete, the native module s } ``` +The specified `methodQueue` will be shared by all of the methods in your module. If *just one* of your methods is long-running (or needs to be run on a different queue than the others for some reason), you can use `dispatch_async` inside the method to perform that particular method's code on another queue, without affecting the others: + +```objective-c +RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback) +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + // Call long-running code on background thread + ... + // You can invoke callback from any thread/queue + callback(@[...]); + }); +} +``` + +> **NOTE**: Sharing dispatch queues between modules +> +> The `methodQueue` method will be called once when the module is initialized, and then retained by the bridge, so there is no need to retain the queue yourself, unless you wish to make use of it within your module. However, if you wish to share the same queue between multiple modules then you will need to ensure that you retain and return the same queue instance for each of them; merely returning a queue of the same name for each won't work. + ## Exporting Constants A native module can export constants that are immediately available to JavaScript at runtime. This is useful for communicating static data that would otherwise require a round-trip through the bridge. From 70c9f5140d6e82f58e26b9e529de49c2cd5a0e2b Mon Sep 17 00:00:00 2001 From: Gabe Levi Date: Sun, 26 Apr 2015 18:23:40 -0400 Subject: [PATCH 112/250] Clean the errors that Flow v0.10.0 finds --- Examples/UIExplorer/ActivityIndicatorIOSExample.js | 1 + Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js | 2 +- Libraries/ReactIOS/ReactIOSMount.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Examples/UIExplorer/ActivityIndicatorIOSExample.js b/Examples/UIExplorer/ActivityIndicatorIOSExample.js index 4151609e068de3..9655d680c71b63 100644 --- a/Examples/UIExplorer/ActivityIndicatorIOSExample.js +++ b/Examples/UIExplorer/ActivityIndicatorIOSExample.js @@ -57,6 +57,7 @@ var ToggleAnimatingActivityIndicator = React.createClass({ } }); +exports.displayName = (undefined: ?string); exports.framework = 'React'; exports.title = ''; exports.description = 'Animated loading indicators.'; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js index 32945c4343474e..2baaee21b416c6 100644 --- a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -86,7 +86,7 @@ var TabBarItemIOS = React.createClass({ } }, - componentWillReceiveProps: function(nextProps: { selected: boolean }) { + componentWillReceiveProps: function(nextProps: { selected?: boolean }) { if (this.state.hasBeenSelected || nextProps.selected) { this.setState({hasBeenSelected: true}); } diff --git a/Libraries/ReactIOS/ReactIOSMount.js b/Libraries/ReactIOS/ReactIOSMount.js index 730031672f253d..ab46d6fe9d3144 100644 --- a/Libraries/ReactIOS/ReactIOSMount.js +++ b/Libraries/ReactIOS/ReactIOSMount.js @@ -194,7 +194,7 @@ var ReactIOSMount = { unmountComponentAtNode: function(containerTag: number): bool { if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) { console.error('You cannot render into anything but a top root'); - return; + return false; } var containerID = ReactIOSTagHandles.tagToRootNodeID[containerTag]; From 8aeb33d44c8912af0ded877d36f0241b3c924556 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 27 Apr 2015 03:47:48 -0700 Subject: [PATCH 113/250] [ReactNative] Make so each RCTContextExecutor has its own thread --- React/Executors/RCTContextExecutor.h | 2 +- React/Executors/RCTContextExecutor.m | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/React/Executors/RCTContextExecutor.h b/React/Executors/RCTContextExecutor.h index 159965a2fbed01..a41fcf31419a43 100644 --- a/React/Executors/RCTContextExecutor.h +++ b/React/Executors/RCTContextExecutor.h @@ -23,6 +23,6 @@ * You probably don't want to use this; use -init instead. */ - (instancetype)initWithJavaScriptThread:(NSThread *)javaScriptThread - globalContextRef:(JSGlobalContextRef)context; + globalContextRef:(JSGlobalContextRef)context NS_DESIGNATED_INITIALIZER; @end diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index edcd2aebd880c7..f5089a9d6a76ca 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -55,6 +55,11 @@ - (void)invalidate } } +- (void)dealloc +{ + CFRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]); +} + @end @implementation RCTContextExecutor @@ -153,15 +158,12 @@ + (void)runRunLoopThread - (instancetype)init { - static NSThread *javaScriptThread; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // All JS is single threaded, so a serial queue is our only option. - javaScriptThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoopThread) object:nil]; - [javaScriptThread setName:@"com.facebook.React.JavaScript"]; - [javaScriptThread setThreadPriority:[[NSThread mainThread] threadPriority]]; - [javaScriptThread start]; - }); + NSThread *javaScriptThread = [[NSThread alloc] initWithTarget:[self class] + selector:@selector(runRunLoopThread) + object:nil]; + [javaScriptThread setName:@"com.facebook.React.JavaScript"]; + [javaScriptThread setThreadPriority:[[NSThread mainThread] threadPriority]]; + [javaScriptThread start]; return [self initWithJavaScriptThread:javaScriptThread globalContextRef:NULL]; } @@ -169,6 +171,9 @@ - (instancetype)init - (instancetype)initWithJavaScriptThread:(NSThread *)javaScriptThread globalContextRef:(JSGlobalContextRef)context { + RCTAssert(javaScriptThread != nil, + @"Can't initialize RCTContextExecutor without a javaScriptThread"); + if ((self = [super init])) { _javaScriptThread = javaScriptThread; __weak RCTContextExecutor *weakSelf = self; From 136431cc2f5bb9186c73e8587a02f03879a15ad3 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 27 Apr 2015 03:58:30 -0700 Subject: [PATCH 114/250] [ReactNative] Fix struct args on exported methods --- Libraries/Geolocation/RCTLocationObserver.m | 7 ++--- React/Base/RCTBridge.m | 33 ++++++++++++++++----- React/Modules/RCTUIManager.m | 6 ++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index 5d56caccbe314b..3e864657b82c5d 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -163,12 +163,12 @@ - (void)timeout:(NSTimer *)timer #pragma mark - Public API -RCT_EXPORT_METHOD(startObserving:(NSDictionary *)optionsJSON) +RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options) { [self checkLocationConfig]; // Select best options - _observerOptions = [RCTConvert RCTLocationOptions:optionsJSON]; + _observerOptions = options; for (RCTLocationRequest *request in _pendingRequests) { _observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy); } @@ -189,7 +189,7 @@ - (void)timeout:(NSTimer *)timer } } -RCT_EXPORT_METHOD(getCurrentPosition:(NSDictionary *)optionsJSON +RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options withSuccessCallback:(RCTResponseSenderBlock)successBlock errorCallback:(RCTResponseSenderBlock)errorBlock) { @@ -219,7 +219,6 @@ - (void)timeout:(NSTimer *)timer } // Check if previous recorded location exists and is good enough - RCTLocationOptions options = [RCTConvert RCTLocationOptions:optionsJSON]; if (_lastLocationEvent && CFAbsoluteTimeGetCurrent() - [RCTConvert NSTimeInterval:_lastLocationEvent[@"timestamp"]] < options.maximumAge && [_lastLocationEvent[@"coords"][@"accuracy"] doubleValue] >= options.accuracy) { diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 67c86fb1226656..7dc2322f0c0813 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -355,7 +355,7 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName #define RCT_CONVERT_CASE(_value, _type) \ case _value: { \ - _type (*convert)(id, SEL, id) = (typeof(convert))[RCTConvert methodForSelector:selector]; \ + _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \ break; \ } @@ -377,12 +377,27 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName RCT_CONVERT_CASE('B', BOOL) RCT_CONVERT_CASE('@', id) RCT_CONVERT_CASE('^', void *) - case '{': - RCTAssert(NO, @"Argument %zd of %C[%@ %@] is defined as %@, however RCT_EXPORT_METHOD() " - "does not currently support struct-type arguments.", i - 2, - [reactMethodName characterAtIndex:0], _moduleClassName, - objCMethodName, argumentName); - break; + + case '{': { + [argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) { + NSUInteger size; + NSGetSizeAndAlignment(argumentType, &size, NULL); + void *returnValue = malloc(size); + NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector]; + NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + [_invocation setTarget:[RCTConvert class]]; + [_invocation setSelector:selector]; + [_invocation setArgument:&json atIndex:2]; + [_invocation invoke]; + [_invocation getReturnValue:returnValue]; + + [invocation setArgument:returnValue atIndex:index]; + + free(returnValue); + }]; + break; + } + default: defaultCase(argumentType); } @@ -438,6 +453,10 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName RCT_SIMPLE_CASE('d', double, doubleValue) RCT_SIMPLE_CASE('B', BOOL, boolValue) + case '{': + RCTLogMustFix(@"Cannot convert JSON to struct %s", argumentType); + break; + default: defaultCase(argumentType); } diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 451a343d04960d..b7ae182bad84f9 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1001,12 +1001,11 @@ static void RCTMeasureLayout(RCTShadowView *view, * Only layouts for views that are within the rect passed in are returned. Invokes the error callback if the * passed in parent view does not exist. Invokes the supplied callback with the array of computed layouts. */ -RCT_EXPORT_METHOD(measureViewsInRect:(id)rectJSON +RCT_EXPORT_METHOD(measureViewsInRect:(CGRect)rect parentView:(NSNumber *)reactTag errorCallback:(RCTResponseSenderBlock)errorCallback callback:(RCTResponseSenderBlock)callback) { - CGRect rect = [RCTConvert CGRect:rectJSON]; RCTShadowView *shadowView = _shadowViewRegistry[reactTag]; if (!shadowView) { RCTLogError(@"Attempting to measure view that does not exist (tag #%@)", reactTag); @@ -1102,9 +1101,8 @@ static void RCTMeasureLayout(RCTShadowView *view, } RCT_EXPORT_METHOD(zoomToRect:(NSNumber *)reactTag - withRect:(id)rectJSON) + withRect:(CGRect)rect) { - CGRect rect = [RCTConvert CGRect:rectJSON]; [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ UIView *view = viewRegistry[reactTag]; if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) { From 826b56a41be62ab8a558a2a31b161ecc7577e1fe Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Mon, 27 Apr 2015 03:50:07 -0700 Subject: [PATCH 115/250] [React Native] Update RCTView border implementation --- Examples/UIExplorer/BorderExample.js | 20 +- Examples/UIExplorer/UIExplorerBlock.js | 4 + React/Base/RCTUtils.h | 3 + React/Base/RCTUtils.m | 10 + React/Views/RCTView.h | 7 - React/Views/RCTView.m | 480 +++++++++++++++++-------- 6 files changed, 376 insertions(+), 148 deletions(-) diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js index d9c2acf9a654dc..1790dc49164d46 100644 --- a/Examples/UIExplorer/BorderExample.js +++ b/Examples/UIExplorer/BorderExample.js @@ -57,6 +57,17 @@ var styles = StyleSheet.create({ borderLeftWidth: 40, borderLeftColor: 'blue', }, + border5: { + borderRadius: 50, + borderTopWidth: 10, + borderTopColor: 'red', + borderRightWidth: 20, + borderRightColor: 'yellow', + borderBottomWidth: 30, + borderBottomColor: 'green', + borderLeftWidth: 40, + borderLeftColor: 'blue', + }, }); exports.title = 'Border'; @@ -71,7 +82,7 @@ exports.examples = [ }, { title: 'Equal-Width / Same-Color', - description: 'borderWidth & borderColor', + description: 'borderWidth & borderColor & borderRadius', render() { return ; } @@ -97,4 +108,11 @@ exports.examples = [ return ; } }, + { + title: 'Custom Borders', + description: 'border*Width & border*Color', + render() { + return ; + } + }, ]; diff --git a/Examples/UIExplorer/UIExplorerBlock.js b/Examples/UIExplorer/UIExplorerBlock.js index 924415e013e211..e7c2a2a8ea9d16 100644 --- a/Examples/UIExplorer/UIExplorerBlock.js +++ b/Examples/UIExplorer/UIExplorerBlock.js @@ -70,6 +70,7 @@ var styles = StyleSheet.create({ }, titleContainer: { borderWidth: 0.5, + borderRadius: 2.5, borderColor: '#d6d7da', backgroundColor: '#f6f7f8', paddingHorizontal: 10, @@ -78,8 +79,10 @@ var styles = StyleSheet.create({ titleRow: { flexDirection: 'row', justifyContent: 'space-between', + backgroundColor: 'transparent', }, titleText: { + backgroundColor: 'transparent', fontSize: 14, fontWeight: '500', }, @@ -97,6 +100,7 @@ var styles = StyleSheet.create({ height: 8, }, children: { + backgroundColor: 'transparent', padding: 10, } }); diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h index 812a651222a85c..1c04125684fd11 100644 --- a/React/Base/RCTUtils.h +++ b/React/Base/RCTUtils.h @@ -46,3 +46,6 @@ RCT_EXTERN BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector); // TODO(#6472857): create NSErrors and automatically convert them over the bridge. RCT_EXTERN NSDictionary *RCTMakeError(NSString *message, id toStringify, NSDictionary *extraData); RCT_EXTERN NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary *extraData); + +// Returns YES if React is running in a test environment +RCT_EXTERN BOOL RCTRunningInTestEnvironment(void); diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index cea45c324c63e7..0b4a3873c8c0cc 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -201,3 +201,13 @@ BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector) RCTLogError(@"\nError: %@", error); return error; } + +BOOL RCTRunningInTestEnvironment(void) +{ + static BOOL _isTestEnvironment = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _isTestEnvironment = (NSClassFromString(@"SenTestCase") != nil || NSClassFromString(@"XCTest") != nil); + }); + return _isTestEnvironment; +} diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index 73fe2c7cbb0338..1a4bcb40007e75 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -13,13 +13,6 @@ #import "RCTPointerEvents.h" -typedef NS_ENUM(NSInteger, RCTBorderSide) { - RCTBorderSideTop, - RCTBorderSideRight, - RCTBorderSideBottom, - RCTBorderSideLeft -}; - @protocol RCTAutoInsetsProtocol; @interface RCTView : UIView diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index d40798302b2a94..c0786b5abfa863 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -12,9 +12,10 @@ #import "RCTAutoInsetsProtocol.h" #import "RCTConvert.h" #import "RCTLog.h" +#import "RCTUtils.h" #import "UIView+React.h" -static const RCTBorderSide RCTBorderSideCount = 4; +static void *RCTViewCornerRadiusKVOContext = &RCTViewCornerRadiusKVOContext; static UIView *RCTViewHitTest(UIView *view, CGPoint point, UIEvent *event) { @@ -30,6 +31,10 @@ return nil; } +static BOOL RCTEllipseGetIntersectionsWithLine(CGRect ellipseBoundingRect, CGPoint p1, CGPoint p2, CGPoint intersections[2]); +static CGPathRef RCTPathCreateWithRoundedRect(CGRect rect, CGFloat topLeftRadiusX, CGFloat topLeftRadiusY, CGFloat topRightRadiusX, CGFloat topRightRadiusY, CGFloat bottomLeftRadiusX, CGFloat bottomLeftRadiusY, CGFloat bottomRightRadiusX, CGFloat bottomRightRadiusY, const CGAffineTransform *transform); +static void RCTPathAddEllipticArc(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y, CGFloat xRadius, CGFloat yRadius, CGFloat startAngle, CGFloat endAngle, bool clockwise); + @implementation UIView (RCTViewUnmounting) - (void)react_remountAllSubviews @@ -107,8 +112,39 @@ - (UIView *)react_findClipView @implementation RCTView { NSMutableArray *_reactSubviews; - CAShapeLayer *_borderLayers[RCTBorderSideCount]; - CGFloat _borderWidths[RCTBorderSideCount]; + UIColor *_backgroundColor; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + _borderWidth = -1; + _borderTopWidth = -1; + _borderRightWidth = -1; + _borderBottomWidth = -1; + _borderLeftWidth = -1; + + _backgroundColor = [super backgroundColor]; + [super setBackgroundColor:[UIColor clearColor]]; + + [self.layer addObserver:self forKeyPath:@"cornerRadius" options:0 context:RCTViewCornerRadiusKVOContext]; + } + + return self; +} + +- (void)dealloc +{ + [self.layer removeObserver:self forKeyPath:@"cornerRadius" context:RCTViewCornerRadiusKVOContext]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (context == RCTViewCornerRadiusKVOContext) { + [self.layer setNeedsDisplay]; + } else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } } - (NSString *)accessibilityLabel @@ -381,189 +417,353 @@ - (void)layoutSubviews if (_reactSubviews) { [self updateClippedSubviews]; } - - for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) { - if (_borderLayers[side]) [self updatePathForShapeLayerForSide:side]; - } } -- (void)layoutSublayersOfLayer:(CALayer *)layer +#pragma mark - Borders + +- (UIColor *)backgroundColor { - [super layoutSublayersOfLayer:layer]; + return _backgroundColor; +} - const CGRect bounds = layer.bounds; - for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) { - _borderLayers[side].frame = bounds; +- (void)setBackgroundColor:(UIColor *)backgroundColor +{ + if ([_backgroundColor isEqual:backgroundColor]) { + return; } + _backgroundColor = backgroundColor; + [self.layer setNeedsDisplay]; } -- (BOOL)getTrapezoidPoints:(CGPoint[4])outPoints forSide:(RCTBorderSide)side +- (UIImage *)generateBorderImage:(out CGRect *)contentsCenter { - const CGRect bounds = self.layer.bounds; - const CGFloat minX = CGRectGetMinX(bounds); - const CGFloat maxX = CGRectGetMaxX(bounds); - const CGFloat minY = CGRectGetMinY(bounds); - const CGFloat maxY = CGRectGetMaxY(bounds); + const CGFloat maxRadius = MIN(self.bounds.size.height, self.bounds.size.width) / 2.0; + const CGFloat radius = MAX(0, MIN(self.layer.cornerRadius, maxRadius)); -#define BW(SIDE) [self borderWidthForSide:RCTBorderSide##SIDE] + const CGFloat borderWidth = MAX(0, _borderWidth); + const CGFloat topWidth = _borderTopWidth >= 0 ? _borderTopWidth : borderWidth; + const CGFloat rightWidth = _borderRightWidth >= 0 ? _borderRightWidth : borderWidth; + const CGFloat bottomWidth = _borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth; + const CGFloat leftWidth = _borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth; - switch (side) { - case RCTBorderSideRight: - outPoints[0] = CGPointMake(maxX - BW(Right), maxY - BW(Bottom)); - outPoints[1] = CGPointMake(maxX - BW(Right), minY + BW(Top)); - outPoints[2] = CGPointMake(maxX, minY); - outPoints[3] = CGPointMake(maxX, maxY); - break; - case RCTBorderSideBottom: - outPoints[0] = CGPointMake(minX + BW(Left), maxY - BW(Bottom)); - outPoints[1] = CGPointMake(maxX - BW(Right), maxY - BW(Bottom)); - outPoints[2] = CGPointMake(maxX, maxY); - outPoints[3] = CGPointMake(minX, maxY); - break; - case RCTBorderSideLeft: - outPoints[0] = CGPointMake(minX + BW(Left), minY + BW(Top)); - outPoints[1] = CGPointMake(minX + BW(Left), maxY - BW(Bottom)); - outPoints[2] = CGPointMake(minX, maxY); - outPoints[3] = CGPointMake(minX, minY); - break; - case RCTBorderSideTop: - outPoints[0] = CGPointMake(maxX - BW(Right), minY + BW(Top)); - outPoints[1] = CGPointMake(minX + BW(Left), minY + BW(Top)); - outPoints[2] = CGPointMake(minX, minY); - outPoints[3] = CGPointMake(maxX, minY); - break; - } + const CGFloat topRadius = MAX(0, radius - topWidth); + const CGFloat rightRadius = MAX(0, radius - rightWidth); + const CGFloat bottomRadius = MAX(0, radius - bottomWidth); + const CGFloat leftRadius = MAX(0, radius - leftWidth); - return YES; -} + const UIEdgeInsets edgeInsets = UIEdgeInsetsMake(topWidth + topRadius, leftWidth + leftRadius, bottomWidth + bottomRadius, rightWidth + rightRadius); + const CGSize size = CGSizeMake(edgeInsets.left + 1 + edgeInsets.right, edgeInsets.top + 1 + edgeInsets.bottom); -- (CAShapeLayer *)createShapeLayerIfNotExistsForSide:(RCTBorderSide)side -{ - CAShapeLayer *borderLayer = _borderLayers[side]; - if (!borderLayer) { - borderLayer = [CAShapeLayer layer]; - borderLayer.fillColor = self.layer.borderColor; - [self.layer addSublayer:borderLayer]; - _borderLayers[side] = borderLayer; - } - return borderLayer; -} + UIScreen *screen = self.window.screen ?: [UIScreen mainScreen]; + UIGraphicsBeginImageContextWithOptions(size, NO, screen.scale * 2); -- (void)updatePathForShapeLayerForSide:(RCTBorderSide)side -{ - CAShapeLayer *borderLayer = [self createShapeLayerIfNotExistsForSide:side]; + CGContextRef ctx = UIGraphicsGetCurrentContext(); + const CGRect rect = {CGPointZero, size}; + CGPathRef path = CGPathCreateWithRoundedRect(rect, radius, radius, NULL); - CGPoint trapezoidPoints[4]; - [self getTrapezoidPoints:trapezoidPoints forSide:side]; + if (_backgroundColor) { + CGContextSaveGState(ctx); - CGMutablePathRef path = CGPathCreateMutable(); - CGPathAddLines(path, NULL, trapezoidPoints, 4); - CGPathCloseSubpath(path); - borderLayer.path = path; + CGContextAddPath(ctx, path); + CGContextSetFillColorWithColor(ctx, _backgroundColor.CGColor); + CGContextFillPath(ctx); + + CGContextRestoreGState(ctx); + } + + CGContextAddPath(ctx, path); CGPathRelease(path); -} -- (void)updateBorderLayers -{ - BOOL widthsAndColorsSame = YES; - CGFloat width = _borderWidths[0]; - CGColorRef color = _borderLayers[0].fillColor; - for (RCTBorderSide side = 1; side < RCTBorderSideCount; side++) { - CAShapeLayer *layer = _borderLayers[side]; - if (_borderWidths[side] != width || (layer && !CGColorEqualToColor(layer.fillColor, color))) { - widthsAndColorsSame = NO; - break; + if (radius > 0 && topWidth > 0 && rightWidth > 0 && bottomWidth > 0 && leftWidth > 0) { + const UIEdgeInsets insetEdgeInsets = UIEdgeInsetsMake(topWidth, leftWidth, bottomWidth, rightWidth); + const CGRect insetRect = UIEdgeInsetsInsetRect(rect, insetEdgeInsets); + CGPathRef insetPath = RCTPathCreateWithRoundedRect(insetRect, leftRadius, topRadius, rightRadius, topRadius, leftRadius, bottomRadius, rightRadius, bottomRadius, NULL); + CGContextAddPath(ctx, insetPath); + CGPathRelease(insetPath); + } + + CGContextEOClip(ctx); + + BOOL hasEqualColor = !_borderTopColor && !_borderRightColor && !_borderBottomColor && !_borderLeftColor; + BOOL hasEqualBorder = _borderWidth >= 0 && _borderTopWidth < 0 && _borderRightWidth < 0 && _borderBottomWidth < 0 && _borderLeftWidth < 0; + if (radius <= 0 && hasEqualBorder && hasEqualColor) { + CGContextSetStrokeColorWithColor(ctx, _borderColor); + CGContextSetLineWidth(ctx, 2 * _borderWidth); + CGContextClipToRect(ctx, rect); + CGContextStrokeRect(ctx, rect); + } else if (radius <= 0 && hasEqualColor) { + CGContextSetFillColorWithColor(ctx, _borderColor); + CGContextAddRect(ctx, rect); + const CGRect insetRect = UIEdgeInsetsInsetRect(rect, edgeInsets); + CGContextAddRect(ctx, insetRect); + CGContextEOFillPath(ctx); + } else { + BOOL didSet = NO; + CGPoint topLeft; + if (topRadius > 0 && leftRadius > 0) { + CGPoint points[2]; + RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, topWidth, 2 * leftRadius, 2 * topRadius), CGPointMake(0, 0), CGPointMake(leftWidth, topWidth), points); + if (!isnan(points[1].x) && !isnan(points[1].y)) { + topLeft = points[1]; + didSet = YES; + } } - } - if (widthsAndColorsSame) { - // Set main layer border - if (width) { - _borderWidth = self.layer.borderWidth = width; + if (!didSet) { + topLeft = CGPointMake(leftWidth, topWidth); } - if (color) { - self.layer.borderColor = color; + + didSet = NO; + CGPoint bottomLeft; + if (bottomRadius > 0 && leftRadius > 0) { + CGPoint points[2]; + RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, (size.height - bottomWidth) - 2 * bottomRadius, 2 * leftRadius, 2 * bottomRadius), CGPointMake(0, size.height), CGPointMake(leftWidth, size.height - bottomWidth), points); + if (!isnan(points[1].x) && !isnan(points[1].y)) { + bottomLeft = points[1]; + didSet = YES; + } } - // Remove border layers - for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) { - [_borderLayers[side] removeFromSuperlayer]; - _borderLayers[side] = nil; + if (!didSet) { + bottomLeft = CGPointMake(leftWidth, size.height - bottomWidth); } - } else { + didSet = NO; + CGPoint topRight; + if (topRadius > 0 && rightRadius > 0) { + CGPoint points[2]; + RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * rightRadius, topWidth, 2 * rightRadius, 2 * topRadius), CGPointMake(size.width, 0), CGPointMake(size.width - rightWidth, topWidth), points); + if (!isnan(points[0].x) && !isnan(points[0].y)) { + topRight = points[0]; + didSet = YES; + } + } + + if (!didSet) { + topRight = CGPointMake(size.width - rightWidth, topWidth); + } + + didSet = NO; + CGPoint bottomRight; + if (bottomRadius > 0 && rightRadius > 0) { + CGPoint points[2]; + RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * rightRadius, (size.height - bottomWidth) - 2 * bottomRadius, 2 * rightRadius, 2 * bottomRadius), CGPointMake(size.width, size.height), CGPointMake(size.width - rightWidth, size.height - bottomWidth), points); + if (!isnan(points[0].x) && !isnan(points[0].y)) { + bottomRight = points[0]; + didSet = YES; + } + } - // Clear main layer border - self.layer.borderWidth = 0; + if (!didSet) { + bottomRight = CGPointMake(size.width - rightWidth, size.height - bottomWidth); + } + + // RIGHT + if (rightWidth > 0) { + CGContextSaveGState(ctx); + + const CGPoint points[] = { + CGPointMake(size.width, 0), + topRight, + bottomRight, + CGPointMake(size.width, size.height), + }; + + CGContextSetFillColorWithColor(ctx, _borderRightColor ?: _borderColor); + CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); + CGContextFillPath(ctx); + + CGContextRestoreGState(ctx); + } - // Set up border layers - for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) { - [self updatePathForShapeLayerForSide:side]; + // BOTTOM + if (bottomWidth > 0) { + CGContextSaveGState(ctx); + + const CGPoint points[] = { + CGPointMake(0, size.height), + bottomLeft, + bottomRight, + CGPointMake(size.width, size.height), + }; + + CGContextSetFillColorWithColor(ctx, _borderBottomColor ?: _borderColor); + CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); + CGContextFillPath(ctx); + + CGContextRestoreGState(ctx); + } + + // LEFT + if (leftWidth > 0) { + CGContextSaveGState(ctx); + + const CGPoint points[] = { + CGPointMake(0, 0), + topLeft, + bottomLeft, + CGPointMake(0, size.height), + }; + + CGContextSetFillColorWithColor(ctx, _borderLeftColor ?: _borderColor); + CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); + CGContextFillPath(ctx); + + CGContextRestoreGState(ctx); + } + + // TOP + if (topWidth > 0) { + CGContextSaveGState(ctx); + + const CGPoint points[] = { + CGPointMake(0, 0), + topLeft, + topRight, + CGPointMake(size.width, 0), + }; + + CGContextSetFillColorWithColor(ctx, _borderTopColor ?: _borderColor); + CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); + CGContextFillPath(ctx); + + CGContextRestoreGState(ctx); } } -} -- (CGFloat)borderWidthForSide:(RCTBorderSide)side -{ - return _borderWidths[side] ?: _borderWidth; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + *contentsCenter = CGRectMake(edgeInsets.left / size.width, edgeInsets.top / size.height, 1.0 / size.width, 1.0 / size.height); + return [image resizableImageWithCapInsets:edgeInsets]; } -- (void)setBorderWidth:(CGFloat)width forSide:(RCTBorderSide)side +- (void)displayLayer:(CALayer *)layer { - _borderWidths[side] = width; - [self updateBorderLayers]; -} + CGRect contentsCenter; + UIImage *image = [self generateBorderImage:&contentsCenter]; -#define BORDER_WIDTH(SIDE) \ -- (CGFloat)border##SIDE##Width { return [self borderWidthForSide:RCTBorderSide##SIDE]; } \ -- (void)setBorder##SIDE##Width:(CGFloat)width { [self setBorderWidth:width forSide:RCTBorderSide##SIDE]; } + if (RCTRunningInTestEnvironment()) { + const CGSize size = self.bounds.size; + UIGraphicsBeginImageContextWithOptions(size, NO, image.scale); + [image drawInRect:(CGRect){CGPointZero, size}]; + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); -BORDER_WIDTH(Top) -BORDER_WIDTH(Right) -BORDER_WIDTH(Bottom) -BORDER_WIDTH(Left) + contentsCenter = CGRectMake(0, 0, 1, 1); + } -- (CGColorRef)borderColorForSide:(RCTBorderSide)side -{ - return _borderLayers[side].fillColor ?: self.layer.borderColor; + layer.contents = (id)image.CGImage; + layer.contentsCenter = contentsCenter; + layer.contentsScale = image.scale; + layer.magnificationFilter = kCAFilterNearest; } -- (void)setBorderColor:(CGColorRef)color forSide:(RCTBorderSide)side -{ - [self createShapeLayerIfNotExistsForSide:side].fillColor = color; - [self updateBorderLayers]; -} +#pragma mark Border Color -#define BORDER_COLOR(SIDE) \ -- (CGColorRef)border##SIDE##Color { return [self borderColorForSide:RCTBorderSide##SIDE]; } \ -- (void)setBorder##SIDE##Color:(CGColorRef)color { [self setBorderColor:color forSide:RCTBorderSide##SIDE]; } +#define setBorderColor(side) \ + - (void)setBorder##side##Color:(CGColorRef)border##side##Color \ + { \ + if (CGColorEqualToColor(_border##side##Color, border##side##Color)) { \ + return; \ + } \ + _border##side##Color = border##side##Color; \ + [self.layer setNeedsDisplay]; \ + } -BORDER_COLOR(Top) -BORDER_COLOR(Right) -BORDER_COLOR(Bottom) -BORDER_COLOR(Left) +setBorderColor() +setBorderColor(Top) +setBorderColor(Right) +setBorderColor(Bottom) +setBorderColor(Left) -- (void)setBorderWidth:(CGFloat)borderWidth -{ - _borderWidth = borderWidth; - for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) { - _borderWidths[side] = borderWidth; +#pragma mark - Border Width + +#define setBorderWidth(side) \ + - (void)setBorder##side##Width:(CGFloat)border##side##Width \ + { \ + if (_border##side##Width == border##side##Width) { \ + return; \ + } \ + _border##side##Width = border##side##Width; \ + [self.layer setNeedsDisplay]; \ } - [self updateBorderLayers]; -} -- (void)setBorderColor:(CGColorRef)borderColor +setBorderWidth() +setBorderWidth(Top) +setBorderWidth(Right) +setBorderWidth(Bottom) +setBorderWidth(Left) + +@end + +static void RCTPathAddEllipticArc(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y, CGFloat xRadius, CGFloat yRadius, CGFloat startAngle, CGFloat endAngle, bool clockwise) { - self.layer.borderColor = borderColor; - for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) { - _borderLayers[side].fillColor = borderColor; + CGFloat xScale = 1, yScale = 1, radius = 0; + if (xRadius != 0) { + xScale = 1; + yScale = yRadius / xRadius; + radius = xRadius; + } else if (yRadius != 0) { + xScale = xRadius / yRadius; + yScale = 1; + radius = yRadius; } - [self updateBorderLayers]; + + CGAffineTransform t = CGAffineTransformMakeTranslation(x, y); + t = CGAffineTransformScale(t, xScale, yScale); + if (m != NULL) { + t = CGAffineTransformConcat(t, *m); + } + + CGPathAddArc(path, &t, 0, 0, radius, startAngle, endAngle, clockwise); } -- (CGColorRef)borderColor +static CGPathRef RCTPathCreateWithRoundedRect(CGRect rect, CGFloat topLeftRadiusX, CGFloat topLeftRadiusY, CGFloat topRightRadiusX, CGFloat topRightRadiusY, CGFloat bottomLeftRadiusX, CGFloat bottomLeftRadiusY, CGFloat bottomRightRadiusX, CGFloat bottomRightRadiusY, const CGAffineTransform *transform) { - return self.layer.borderColor; + const CGFloat minX = CGRectGetMinX(rect); + const CGFloat minY = CGRectGetMinY(rect); + const CGFloat maxX = CGRectGetMaxX(rect); + const CGFloat maxY = CGRectGetMaxY(rect); + + CGMutablePathRef path = CGPathCreateMutable(); + RCTPathAddEllipticArc(path, transform, minX + topLeftRadiusX, minY + topLeftRadiusY, topLeftRadiusX, topLeftRadiusY, M_PI, 3 * M_PI_2, false); + RCTPathAddEllipticArc(path, transform, maxX - topRightRadiusX, minY + topRightRadiusY, topRightRadiusX, topRightRadiusY, 3 * M_PI_2, 0, false); + RCTPathAddEllipticArc(path, transform, maxX - bottomRightRadiusX, maxY - bottomRightRadiusY, bottomRightRadiusX, bottomRightRadiusY, 0, M_PI_2, false); + RCTPathAddEllipticArc(path, transform, minX + bottomLeftRadiusX, maxY - bottomLeftRadiusY, bottomLeftRadiusX, bottomLeftRadiusY, M_PI_2, M_PI, false); + CGPathCloseSubpath(path); + return path; } -@end +static BOOL RCTEllipseGetIntersectionsWithLine(CGRect ellipseBoundingRect, CGPoint p1, CGPoint p2, CGPoint intersections[2]) +{ + const CGFloat ellipseCenterX = CGRectGetMidX(ellipseBoundingRect); + const CGFloat ellipseCenterY = CGRectGetMidY(ellipseBoundingRect); + + // ellipseBoundingRect.origin.x -= ellipseCenterX; + // ellipseBoundingRect.origin.y -= ellipseCenterY; + + p1.x -= ellipseCenterX; + p1.y -= ellipseCenterY; + + p2.x -= ellipseCenterX; + p2.y -= ellipseCenterY; + + const CGFloat m = (p2.y - p1.y) / (p2.x - p1.x); + const CGFloat a = ellipseBoundingRect.size.width / 2; + const CGFloat b = ellipseBoundingRect.size.height / 2; + const CGFloat c = p1.y - m * p1.x; + const CGFloat A = (b * b + a * a * m * m); + const CGFloat B = 2 * a * a * c * m; + const CGFloat D = sqrt((a * a * (b * b - c * c)) / A + pow(B / (2 * A), 2)); + + const CGFloat x_ = -B / (2 * A); + const CGFloat x1 = x_ + D; + const CGFloat x2 = x_ - D; + const CGFloat y1 = m * x1 + c; + const CGFloat y2 = m * x2 + c; + + intersections[0] = CGPointMake(x1 + ellipseCenterX, y1 + ellipseCenterY); + intersections[1] = CGPointMake(x2 + ellipseCenterX, y2 + ellipseCenterY); + return YES; +} From bae4e44c604e236ba5f60083f83d3fc0f6d2045e Mon Sep 17 00:00:00 2001 From: Maksim Bunkow Date: Mon, 27 Apr 2015 10:24:49 -0700 Subject: [PATCH 116/250] Added placeholderTextColor property for TextInput class Summary: Closes https://github.com/facebook/react-native/pull/997 Github Author: Maksim Bunkow Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Components/TextInput/TextInput.js | 1 + React/Views/RCTTextField.h | 1 + React/Views/RCTTextField.m | 22 +++++++++++++++++++++ React/Views/RCTTextFieldManager.m | 2 +- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index dfd3ab1a128469..9de1e15e92176f 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -445,6 +445,7 @@ var TextInput = React.createClass({ onSubmitEditing={this.props.onSubmitEditing} onSelectionChangeShouldSetResponder={() => true} placeholder={this.props.placeholder} + placeholderTextColor={this.props.placeholderTextColor} text={this.state.bufferedValue} autoCapitalize={autoCapitalize} autoCorrect={this.props.autoCorrect} diff --git a/React/Views/RCTTextField.h b/React/Views/RCTTextField.h index bd1be9c187bd39..ef0a07887faabc 100644 --- a/React/Views/RCTTextField.h +++ b/React/Views/RCTTextField.h @@ -17,6 +17,7 @@ @property (nonatomic, assign) BOOL autoCorrect; @property (nonatomic, assign) BOOL selectTextOnFocus; @property (nonatomic, assign) UIEdgeInsets contentInset; +@property (nonatomic, strong) UIColor *placeholderTextColor; - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; diff --git a/React/Views/RCTTextField.m b/React/Views/RCTTextField.m index 35eb84d9653dde..ff56214d8ead02 100644 --- a/React/Views/RCTTextField.m +++ b/React/Views/RCTTextField.m @@ -42,6 +42,28 @@ - (void)setText:(NSString *)text } } +static void RCTUpdatePlaceholder(RCTTextField *self) +{ + if (self.placeholder.length > 0 && self.placeholderTextColor) { + self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder + attributes:@{ + NSForegroundColorAttributeName : self.placeholderTextColor + }]; + } else if (self.placeholder.length) { + self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder]; + } +} + +- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor { + _placeholderTextColor = placeholderTextColor; + RCTUpdatePlaceholder(self); +} + +- (void)setPlaceholder:(NSString *)placeholder { + super.placeholder = placeholder; + RCTUpdatePlaceholder(self); +} + - (NSArray *)reactSubviews { // TODO: do we support subviews of textfield in React? diff --git a/React/Views/RCTTextFieldManager.m b/React/Views/RCTTextFieldManager.m index 6e78d86a3b1c39..ff401a719c7c7a 100644 --- a/React/Views/RCTTextFieldManager.m +++ b/React/Views/RCTTextFieldManager.m @@ -10,7 +10,6 @@ #import "RCTTextFieldManager.h" #import "RCTBridge.h" -#import "RCTConvert.h" #import "RCTShadowView.h" #import "RCTSparseArray.h" #import "RCTTextField.h" @@ -28,6 +27,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL) RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString) +RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(text, NSString) RCT_EXPORT_VIEW_PROPERTY(clearButtonMode, UITextFieldViewMode) RCT_REMAP_VIEW_PROPERTY(clearTextOnFocus, clearsOnBeginEditing, BOOL) From 3f723f451d2eccd73d1306813c9d40d4b9e683a6 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Mon, 27 Apr 2015 13:24:15 -0700 Subject: [PATCH 117/250] [ReactNative][RFC] Yellow Box for warnings --- Libraries/ReactIOS/WarningBox.js | 398 ++++++++++++++++++++ Libraries/ReactIOS/renderApplication.ios.js | 24 +- 2 files changed, 419 insertions(+), 3 deletions(-) create mode 100644 Libraries/ReactIOS/WarningBox.js diff --git a/Libraries/ReactIOS/WarningBox.js b/Libraries/ReactIOS/WarningBox.js new file mode 100644 index 00000000000000..37076ef5c299fe --- /dev/null +++ b/Libraries/ReactIOS/WarningBox.js @@ -0,0 +1,398 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule WarningBox + */ +'use strict'; + +var AsyncStorage = require('AsyncStorage'); +var EventEmitter = require('EventEmitter'); +var Map = require('Map'); +var PanResponder = require('PanResponder'); +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var TouchableOpacity = require('TouchableOpacity'); +var View = require('View'); + +var invariant = require('invariant'); +var rebound = require('rebound'); +var stringifySafe = require('stringifySafe'); + +var SCREEN_WIDTH = require('Dimensions').get('window').width; +var IGNORED_WARNINGS_KEY = '__DEV_WARNINGS_IGNORED'; + +var consoleWarn = console.warn.bind(console); + +var warningCounts = new Map(); +var ignoredWarnings: Array = []; +var totalWarningCount = 0; +var warningCountEvents = new EventEmitter(); + +/** + * WarningBox renders warnings on top of the app being developed. Warnings help + * guard against subtle yet significant issues that can impact the quality of + * your application, such as accessibility and memory leaks. This "in your + * face" style of warning allows developers to notice and correct these issues + * as quickly as possible. + * + * The warning box is currently opt-in. Set the following flag to enable it: + * + * `console.yellowBoxEnabled = true;` + * + * If "ignore" is tapped on a warning, the WarningBox will record that warning + * and will not display it again. This is useful for hiding errors that already + * exist or have been introduced elsewhere. To re-enable all of the errors, set + * the following: + * + * `console.yellowBoxResetIgnored = true;` + * + * This can also be set permanently, and ignore will only silence the warnings + * until the next refresh. + */ + +if (__DEV__) { + console.warn = function() { + consoleWarn.apply(null, arguments); + if (!console.yellowBoxEnabled) { + return; + } + var warning = Array.prototype.map.call(arguments, stringifySafe).join(' '); + if (!console.yellowBoxResetIgnored && + ignoredWarnings.indexOf(warning) !== -1) { + return; + } + var count = warningCounts.has(warning) ? warningCounts.get(warning) + 1 : 1; + warningCounts.set(warning, count); + totalWarningCount += 1; + warningCountEvents.emit('count', totalWarningCount); + }; +} + +function saveIgnoredWarnings() { + AsyncStorage.setItem( + IGNORED_WARNINGS_KEY, + JSON.stringify(ignoredWarnings), + function(err) { + if (err) { + console.warn('Could not save ignored warnings.', err); + } + } + ); +} + +AsyncStorage.getItem(IGNORED_WARNINGS_KEY, function(err, data) { + if (!err && data && !console.yellowBoxResetIgnored) { + ignoredWarnings = JSON.parse(data); + } +}); + +var WarningRow = React.createClass({ + componentWillMount: function() { + this.springSystem = new rebound.SpringSystem(); + this.dismissalSpring = this.springSystem.createSpring(); + this.dismissalSpring.setRestSpeedThreshold(0.05); + this.dismissalSpring.setCurrentValue(0); + this.dismissalSpring.addListener({ + onSpringUpdate: () => { + var val = this.dismissalSpring.getCurrentValue(); + this.text && this.text.setNativeProps({ + left: SCREEN_WIDTH * val, + }); + this.container && this.container.setNativeProps({ + opacity: 1 - val, + }); + this.closeButton && this.closeButton.setNativeProps({ + opacity: 1 - (val * 5), + }); + }, + onSpringAtRest: () => { + if (this.dismissalSpring.getCurrentValue()) { + this.collapseSpring.setEndValue(1); + } + }, + }); + this.collapseSpring = this.springSystem.createSpring(); + this.collapseSpring.setRestSpeedThreshold(0.05); + this.collapseSpring.setCurrentValue(0); + this.collapseSpring.getSpringConfig().friction = 20; + this.collapseSpring.getSpringConfig().tension = 200; + this.collapseSpring.addListener({ + onSpringUpdate: () => { + var val = this.collapseSpring.getCurrentValue(); + this.container && this.container.setNativeProps({ + height: Math.abs(46 - (val * 46)), + }); + }, + onSpringAtRest: () => { + this.props.onDismissed(); + }, + }); + this.panGesture = PanResponder.create({ + onStartShouldSetPanResponder: () => { + return !! this.dismissalSpring.getCurrentValue(); + }, + onMoveShouldSetPanResponder: () => true, + onPanResponderGrant: () => { + this.isResponderOnlyToBlockTouches = + !! this.dismissalSpring.getCurrentValue(); + }, + onPanResponderMove: (e, gestureState) => { + if (this.isResponderOnlyToBlockTouches) { + return; + } + this.dismissalSpring.setCurrentValue(gestureState.dx / SCREEN_WIDTH); + }, + onPanResponderRelease: (e, gestureState) => { + if (this.isResponderOnlyToBlockTouches) { + return; + } + var gestureCompletion = gestureState.dx / SCREEN_WIDTH; + var doesGestureRelease = (gestureState.vx + gestureCompletion) > 0.5; + this.dismissalSpring.setEndValue(doesGestureRelease ? 1 : 0); + } + }); + }, + render: function() { + var countText; + if (warningCounts.get(this.props.warning) > 1) { + countText = ( + + ({warningCounts.get(this.props.warning)}){" "} + + ); + } + return ( + { this.container = container; }} + {...this.panGesture.panHandlers}> + + + { this.text = text; }}> + {countText} + {this.props.warning} + + + + { this.closeButton = closeButton; }} + style={styles.closeButton}> + { + this.dismissalSpring.setEndValue(1); + }}> + ✕ + + + + ); + } +}); + +var WarningBoxOpened = React.createClass({ + render: function() { + var countText; + if (warningCounts.get(this.props.warning) > 1) { + countText = ( + + ({warningCounts.get(this.props.warning)}){" "} + + ); + } + return ( + + + + {countText} + {this.props.warning} + + + + + + Dismiss + + + + + + + Ignore + + + + + + + ); + }, +}); + +var canMountWarningBox = true; + +var WarningBox = React.createClass({ + getInitialState: function() { + return { + totalWarningCount, + openWarning: null, + }; + }, + componentWillMount: function() { + if (console.yellowBoxResetIgnored) { + AsyncStorage.setItem(IGNORED_WARNINGS_KEY, '[]', function(err) { + if (err) { + console.warn('Could not reset ignored warnings.', err); + } + }); + ignoredWarnings = []; + } + }, + componentDidMount: function() { + invariant( + canMountWarningBox, + 'There can only be one WarningBox' + ); + canMountWarningBox = false; + warningCountEvents.addListener( + 'count', + this._onWarningCount + ); + }, + componentWillUnmount: function() { + warningCountEvents.removeAllListeners(); + canMountWarningBox = true; + }, + _onWarningCount: function(totalWarningCount) { + // Must use setImmediate because warnings often happen during render and + // state cannot be set while rendering + setImmediate(() => { + this.setState({ totalWarningCount, }); + }); + }, + _onDismiss: function(warning) { + warningCounts.delete(warning); + this.setState({ + openWarning: null, + }); + }, + render: function() { + if (warningCounts.size === 0) { + return ; + } + if (this.state.openWarning) { + return ( + { + this.setState({ openWarning: null }); + }} + onDismissed={this._onDismiss.bind(this, this.state.openWarning)} + onIgnored={() => { + ignoredWarnings.push(this.state.openWarning); + saveIgnoredWarnings(); + this._onDismiss(this.state.openWarning); + }} + /> + ); + } + var warningRows = []; + warningCounts.forEach((count, warning) => { + warningRows.push( + { + this.setState({ openWarning: warning }); + }} + onDismissed={this._onDismiss.bind(this, warning)} + warning={warning} + /> + ); + }); + return ( + + {warningRows} + + ); + }, +}); + +var styles = StyleSheet.create({ + bold: { + fontWeight: 'bold', + }, + closeButton: { + position: 'absolute', + right: 0, + height: 46, + width: 46, + }, + closeButtonText: { + color: 'white', + fontSize: 32, + position: 'relative', + left: 8, + }, + warningContainer: { + position: 'absolute', + left: 0, + right: 0, + bottom: 0 + }, + warningBox: { + position: 'relative', + backgroundColor: 'rgba(171, 124, 36, 0.9)', + flex: 1, + height: 46, + }, + warningText: { + color: 'white', + position: 'absolute', + left: 0, + marginLeft: 15, + marginRight: 46, + top: 7, + }, + yellowBox: { + backgroundColor: 'rgba(171, 124, 36, 0.9)', + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + padding: 15, + paddingTop: 35, + }, + yellowBoxText: { + color: 'white', + fontSize: 20, + }, + yellowBoxButtons: { + flexDirection: 'row', + position: 'absolute', + bottom: 0, + }, + yellowBoxButton: { + flex: 1, + padding: 25, + }, + yellowBoxButtonText: { + color: 'white', + fontSize: 16, + } +}); + +module.exports = WarningBox; diff --git a/Libraries/ReactIOS/renderApplication.ios.js b/Libraries/ReactIOS/renderApplication.ios.js index 084390ac50057d..16052c6fa69605 100644 --- a/Libraries/ReactIOS/renderApplication.ios.js +++ b/Libraries/ReactIOS/renderApplication.ios.js @@ -12,6 +12,9 @@ 'use strict'; var React = require('React'); +var StyleSheet = require('StyleSheet'); +var View = require('View'); +var WarningBox = require('WarningBox'); var invariant = require('invariant'); @@ -24,12 +27,27 @@ function renderApplication( rootTag, 'Expect to have a valid rootTag, instead got ', rootTag ); + var shouldRenderWarningBox = __DEV__ && console.yellowBoxEnabled; + var warningBox = shouldRenderWarningBox ? : null; React.render( - , + + + {warningBox} + , rootTag ); } +var styles = StyleSheet.create({ + appContainer: { + position: 'absolute', + left: 0, + top: 0, + right: 0, + bottom: 0, + }, +}); + module.exports = renderApplication; From dd56ccb9c732511fb380d28dd2a5c17119467c72 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Mon, 27 Apr 2015 13:55:01 -0700 Subject: [PATCH 118/250] [react-native] Fix capitalization of "REact" --- Libraries/Components/View/View.js | 2 +- Libraries/Image/Image.ios.js | 2 +- Libraries/ReactIOS/ReactIOSComponentMixin.js | 2 +- Libraries/Text/Text.js | 2 +- Libraries/vendor/react/platform/NodeHandle.js | 2 +- React/Base/RCTEventDispatcher.h | 2 +- React/Base/RCTEventDispatcher.m | 2 +- React/Base/RCTRootView.m | 4 ++-- React/Base/RCTTouchHandler.m | 2 +- React/Modules/RCTUIManager.m | 6 +++--- React/Views/RCTNavigator.m | 4 ++-- React/Views/RCTViewNodeProtocol.h | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 0da57f554257e5..c7ca2ee261ab91 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -29,7 +29,7 @@ var stylePropType = StyleSheetPropType(ViewStylePropTypes); * container that supports layout with flexbox, style, some touch handling, and * accessibility controls, and is designed to be nested inside other views and * to have 0 to many children of any type. `View` maps directly to the native - * view equivalent on whatever platform react is running on, whether that is a + * view equivalent on whatever platform React is running on, whether that is a * `UIView`, `
`, `android.view`, etc. This example creates a `View` that * wraps two colored boxes and custom component in a row with padding. * diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index e917b6b637355b..a41352f44ce4b0 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -31,7 +31,7 @@ var verifyPropTypes = require('verifyPropTypes'); var warning = require('warning'); /** - * A react component for displaying different types of images, + * A React component for displaying different types of images, * including network images, static resources, temporary local images, and * images from local disk, such as the camera roll. * diff --git a/Libraries/ReactIOS/ReactIOSComponentMixin.js b/Libraries/ReactIOS/ReactIOSComponentMixin.js index 94c708a433e863..03d7f5ad04fc3b 100644 --- a/Libraries/ReactIOS/ReactIOSComponentMixin.js +++ b/Libraries/ReactIOS/ReactIOSComponentMixin.js @@ -46,7 +46,7 @@ var ReactInstanceMap = require('ReactInstanceMap'); * * `mountImage`: A way to represent the potential to create lower level * resources whos `nodeHandle` can be discovered immediately by knowing the - * `rootNodeID`. Today's web react represents this with `innerHTML` annotated + * `rootNodeID`. Today's web React represents this with `innerHTML` annotated * with DOM ids that match the `rootNodeID`. * * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index ce7b4078e9acd4..a67abacb087ec5 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -33,7 +33,7 @@ var viewConfig = { }; /** - * A react component for displaying text which supports nesting, + * A React component for displaying text which supports nesting, * styling, and touch handling. In the following example, the nested title and * body text will inherit the `fontFamily` from `styles.baseText`, but the title * provides its own additional styles. The title and body will stack on top of diff --git a/Libraries/vendor/react/platform/NodeHandle.js b/Libraries/vendor/react/platform/NodeHandle.js index 19f74c6ece0b73..c7c93545bf0317 100644 --- a/Libraries/vendor/react/platform/NodeHandle.js +++ b/Libraries/vendor/react/platform/NodeHandle.js @@ -19,7 +19,7 @@ * worker thread. * * The only other requirement of a platform/environment is that it always be - * possible to extract the react rootNodeID in a blocking manner (see + * possible to extract the React rootNodeID in a blocking manner (see * `getRootNodeID`). * * +------------------+ +------------------+ +------------------+ diff --git a/React/Base/RCTEventDispatcher.h b/React/Base/RCTEventDispatcher.h index dd6bd8ed6f8aa2..15cb180210f6e6 100644 --- a/React/Base/RCTEventDispatcher.h +++ b/React/Base/RCTEventDispatcher.h @@ -50,7 +50,7 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) { /** * Send a user input event. The body dictionary must contain a "target" - * parameter, representing the react tag of the view sending the event + * parameter, representing the React tag of the view sending the event */ - (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body; diff --git a/React/Base/RCTEventDispatcher.m b/React/Base/RCTEventDispatcher.m index fb4e02eae30f75..8487556e576a53 100644 --- a/React/Base/RCTEventDispatcher.m +++ b/React/Base/RCTEventDispatcher.m @@ -44,7 +44,7 @@ - (void)sendDeviceEventWithName:(NSString *)name body:(id)body - (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body { RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]], - @"Event body dictionary must include a 'target' property containing a react tag"); + @"Event body dictionary must include a 'target' property containing a React tag"); [_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent" args:body ? @[body[@"target"], name, body] : @[body[@"target"], name]]; diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 0c171951e28467..54556d41810ba4 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -110,11 +110,11 @@ - (void)bundleFinishedLoading dispatch_async(dispatch_get_main_queue(), ^{ /** - * Every root view that is created must have a unique react tag. + * Every root view that is created must have a unique React tag. * Numbering of these tags goes from 1, 11, 21, 31, etc * * NOTE: Since the bridge persists, the RootViews might be reused, so now - * the react tag is assigned every time we load new content. + * the React tag is assigned every time we load new content. */ [_contentView removeFromSuperview]; _contentView = [[UIView alloc] initWithFrame:self.bounds]; diff --git a/React/Base/RCTTouchHandler.m b/React/Base/RCTTouchHandler.m index bd731a99844715..7af26da74eab70 100644 --- a/React/Base/RCTTouchHandler.m +++ b/React/Base/RCTTouchHandler.m @@ -26,7 +26,7 @@ @implementation RCTTouchHandler /** * Arrays managed in parallel tracking native touch object along with the - * native view that was touched, and the react touch data dictionary. + * native view that was touched, and the React touch data dictionary. * This must be kept track of because `UIKit` destroys the touch targets * if touches are canceled and we have no other way to recover this information. */ diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index b7ae182bad84f9..496b9c3513a677 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -661,7 +661,7 @@ - (void)_manageChildren:(NSNumber *)containerReactTag { id container = registry[containerReactTag]; RCTAssert(moveFromIndices.count == moveToIndices.count, @"moveFromIndices had size %tu, moveToIndices had size %tu", moveFromIndices.count, moveToIndices.count); - RCTAssert(addChildReactTags.count == addAtIndices.count, @"there should be at least one react child to add"); + RCTAssert(addChildReactTags.count == addAtIndices.count, @"there should be at least one React child to add"); // Removes (both permanent and temporary moves) are using "before" indices NSArray *permanentlyRemovedChildren = [self _childrenToRemoveFromContainer:container atIndices:removeAtIndices]; @@ -918,7 +918,7 @@ - (void)flushUIBlocks } // TODO: this doesn't work because sometimes view is inside a modal window - // RCTAssert([rootView isReactRootView], @"React view is not inside a react root view"); + // RCTAssert([rootView isReactRootView], @"React view is not inside a React root view"); // By convention, all coordinates, whether they be touch coordinates, or // measurement coordinates are with respect to the root view. @@ -995,7 +995,7 @@ static void RCTMeasureLayout(RCTShadowView *view, } /** - * Returns an array of computed offset layouts in a dictionary form. The layouts are of any react subviews + * Returns an array of computed offset layouts in a dictionary form. The layouts are of any React subviews * that are immediate descendants to the parent view found within a specified rect. The dictionary result * contains left, top, width, height and an index. The index specifies the position among the other subviews. * Only layouts for views that are within the rect passed in are returned. Invokes the error callback if the diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index f3ebb6554a2cb8..a4cb338fb3c88b 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -60,7 +60,7 @@ @interface RCTNavigationController : UINavigationController Date: Mon, 27 Apr 2015 15:18:37 -0700 Subject: [PATCH 119/250] [Flow] Properly fix new errors as of Flow v0.10. --- Examples/UIExplorer/ActivityIndicatorIOSExample.js | 1 + Examples/UIExplorer/UIExplorerList.js | 2 +- Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Examples/UIExplorer/ActivityIndicatorIOSExample.js b/Examples/UIExplorer/ActivityIndicatorIOSExample.js index 4151609e068de3..9655d680c71b63 100644 --- a/Examples/UIExplorer/ActivityIndicatorIOSExample.js +++ b/Examples/UIExplorer/ActivityIndicatorIOSExample.js @@ -57,6 +57,7 @@ var ToggleAnimatingActivityIndicator = React.createClass({ } }); +exports.displayName = (undefined: ?string); exports.framework = 'React'; exports.title = ''; exports.description = 'Animated loading indicators.'; diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index df3c9bc7decf34..73010617732abd 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -11,7 +11,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * --flow disabled-- + * @flow */ 'use strict'; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js index bb2a8e991b7ced..b27e22d4b1e92c 100644 --- a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -89,7 +89,7 @@ var TabBarItemIOS = React.createClass({ } }, - componentWillReceiveProps: function(nextProps: { selected: any /* workaround for flow bug */ }) { + componentWillReceiveProps: function(nextProps: { selected?: boolean }) { if (this.state.hasBeenSelected || nextProps.selected) { this.setState({hasBeenSelected: true}); } From 469ae1f2ca9da9665bf02ce6eea557c3a22f43db Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 27 Apr 2015 16:13:47 -0700 Subject: [PATCH 120/250] temporarily disable flow check in packager while we figure out versioning issues. cc @gabelevi, @bhosmer --- packager/getFlowTypeCheckMiddleware.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packager/getFlowTypeCheckMiddleware.js b/packager/getFlowTypeCheckMiddleware.js index 059f0e319b6b75..c7f3e2b1c6a2bd 100644 --- a/packager/getFlowTypeCheckMiddleware.js +++ b/packager/getFlowTypeCheckMiddleware.js @@ -13,11 +13,12 @@ var exec = require('child_process').exec; var Activity = require('./react-packager/src/Activity'); var hasWarned = {}; +var DISABLE_FLOW_CHECK = true; // temporarily disable while we figure out versioning issues. function getFlowTypeCheckMiddleware(options) { return function(req, res, next) { var isBundle = req.url.indexOf('.bundle') !== -1; - if (options.skipflow || !isBundle) { + if (DISABLE_FLOW_CHECK || options.skipflow || !isBundle) { return next(); } if (options.flowroot || options.projectRoots.length === 1) { From abea586c6522245167a01a48df169317744e4dbb Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Mon, 27 Apr 2015 17:17:26 -0700 Subject: [PATCH 121/250] 0.4.1 --- React.podspec | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/React.podspec b/React.podspec index 2aa8bc93ba6ba8..ce7083a6ac4dbe 100644 --- a/React.podspec +++ b/React.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "React" - s.version = "0.4.0" + s.version = "0.4.1" s.summary = "Build high quality mobile apps using React." s.description = <<-DESC React Native apps are built using the React JS diff --git a/package.json b/package.json index a29d697447e32d..be9d9b943b67bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native", - "version": "0.4.0", + "version": "0.4.1", "description": "A framework for building native apps using React", "repository": { "type": "git", From 6749f88650646a7cf26c5f4ca2e789005330760d Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Mon, 27 Apr 2015 17:23:19 -0700 Subject: [PATCH 122/250] [ReactNative] Fix Navigator resetTo --- Libraries/CustomComponents/Navigator/Navigator.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index da4d2ab10f52ca..152890dd146678 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -957,7 +957,7 @@ var Navigator = React.createClass({ * `index` specifies the route in the stack that should be replaced. * If it's negative, it counts from the back. */ - replaceAtIndex: function(route, index) { + replaceAtIndex: function(route, index, cb) { invariant(!!route, 'Must supply route to replace'); if (index < 0) { index += this.state.routeStack.length; @@ -988,6 +988,7 @@ var Navigator = React.createClass({ this._emitWillFocus(route); this._emitDidFocus(route); } + cb && cb(); }); }, @@ -1034,8 +1035,9 @@ var Navigator = React.createClass({ resetTo: function(route) { invariant(!!route, 'Must supply route to push'); if (this._canNavigate()) { - this.replaceAtIndex(route, 0); - this.popToRoute(route); + this.replaceAtIndex(route, 0, () => { + this.popToRoute(route); + }); } }, From ec3d70e738c26ccf817f7384d42916de28e5051a Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Mon, 27 Apr 2015 17:25:34 -0700 Subject: [PATCH 123/250] [ReactNative] Quiet Navigator context warning --- Libraries/CustomComponents/Navigator/Navigator.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 152890dd146678..d3163378eca943 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -267,7 +267,8 @@ var Navigator = React.createClass({ }, contextTypes: { - navigator: PropTypes.object, + // TODO (t6707746) Re-enable this when owner context switches to parent context + // navigator: PropTypes.object, }, statics: { From b94610887c76f7e37406abbf2ffbd5103906284d Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 27 Apr 2015 19:55:24 -0700 Subject: [PATCH 124/250] [ReactNative] temp disable flow check in packager for OSS --- packager/packager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packager/packager.js b/packager/packager.js index 212e17f715e547..48552d93edbc7d 100644 --- a/packager/packager.js +++ b/packager/packager.js @@ -213,7 +213,8 @@ function runServer( .use(openStackFrameInEditor) .use(getDevToolsLauncher(options)) .use(statusPageMiddleware) - .use(getFlowTypeCheckMiddleware(options)) + // Temporarily disable flow check until it's more stable + //.use(getFlowTypeCheckMiddleware(options)) .use(getAppMiddleware(options)); options.projectRoots.forEach(function(root) { From 282a2071ad75fcf4ec7fee7b61a76ffb375ebb33 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Mon, 27 Apr 2015 19:33:01 -0700 Subject: [PATCH 125/250] [ReactNative] Bump watchman timeout to 25s --- packager/react-packager/src/FileWatcher/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/react-packager/src/FileWatcher/index.js b/packager/react-packager/src/FileWatcher/index.js index 38ad19bf952675..cd1a28e558cdbb 100644 --- a/packager/react-packager/src/FileWatcher/index.js +++ b/packager/react-packager/src/FileWatcher/index.js @@ -26,7 +26,7 @@ var detectingWatcherClass = new Promise(function(resolve) { module.exports = FileWatcher; -var MAX_WAIT_TIME = 10000; +var MAX_WAIT_TIME = 25000; // Singleton var fileWatcher = null; From 473acdbc743a29cf4097706248d1cb246d17731e Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 27 Apr 2015 20:27:59 -0700 Subject: [PATCH 126/250] [ReactNative] Fix fatal from long press --- .../vendor/react_contrib/interactions/Touchable/Touchable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Libraries/vendor/react_contrib/interactions/Touchable/Touchable.js b/Libraries/vendor/react_contrib/interactions/Touchable/Touchable.js index 020731fde52cbe..37c42382712627 100644 --- a/Libraries/vendor/react_contrib/interactions/Touchable/Touchable.js +++ b/Libraries/vendor/react_contrib/interactions/Touchable/Touchable.js @@ -436,6 +436,7 @@ var TouchableMixin = { if (isTouchWithinActive) { this._receiveSignal(Signals.ENTER_PRESS_RECT, e); } else { + this._cancelLongPressDelayTimeout(); this._receiveSignal(Signals.LEAVE_PRESS_RECT, e); } }, From 8afdf30363bc6301579ff2cd9d480845c56260c3 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Mon, 27 Apr 2015 21:13:19 -0700 Subject: [PATCH 127/250] [ReactNative] Navigator refactor and improvements --- .../CustomComponents/Navigator/Navigator.js | 407 ++++++++++-------- .../NavigatorBreadcrumbNavigationBar.js | 13 +- .../Navigator/NavigatorSceneConfigs.js | 5 + 3 files changed, 237 insertions(+), 188 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index d3163378eca943..4503c4185ac129 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -315,13 +315,11 @@ var Navigator = React.createClass({ // On first render, we will render every scene in the initialRouteStack updatingRangeStart: 0, updatingRangeLength: routeStack.length, - // Either animating or gesturing. - isAnimating: false, - jumpToIndex: routeStack.length - 1, presentedIndex: initialRouteIndex, - isResponderOnlyToBlockTouches: false, - fromIndex: initialRouteIndex, - toIndex: initialRouteIndex, + transitionFromIndex: null, + activeGesture: null, + pendingGestureProgress: null, + transitionQueue: [], }; }, @@ -356,9 +354,24 @@ var Navigator = React.createClass({ popToTop: this.popToTop, }; this._handlers = {}; - + this.springSystem = new rebound.SpringSystem(); + this.spring = this.springSystem.createSpring(); + this.spring.setRestSpeedThreshold(0.05); + this.spring.setCurrentValue(0).setAtRest(); + this.spring.addListener({ + onSpringEndStateChange: () => { + if (!this._interactionHandle) { + this._interactionHandle = this.createInteractionHandle(); + } + }, + onSpringUpdate: () => { + this._handleSpringUpdate(); + }, + onSpringAtRest: () => { + this._completeTransition(); + }, + }); this.panGesture = PanResponder.create({ - onStartShouldSetPanResponderCapture: this._handleStartShouldSetPanResponderCapture, onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder, onPanResponderGrant: this._handlePanResponderGrant, onPanResponderRelease: this._handlePanResponderRelease, @@ -427,20 +440,8 @@ var Navigator = React.createClass({ this._handlers[this.state.routeStack.indexOf(route)] = handler; }, - _configureSpring: function(animationConfig) { - var config = this.spring.getSpringConfig(); - config.friction = animationConfig.springFriction; - config.tension = animationConfig.springTension; - }, - componentDidMount: function() { - this.springSystem = new rebound.SpringSystem(); - this.spring = this.springSystem.createSpring(); - this.spring.setRestSpeedThreshold(0.05); - var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - animationConfig && this._configureSpring(animationConfig); - this.spring.addListener(this); - this.onSpringUpdate(); + this._handleSpringUpdate(); this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]); if (this.parentNavigator) { this.parentNavigator.setHandler(this._handleRequest); @@ -484,98 +485,113 @@ var Navigator = React.createClass({ updatingRangeStart: 0, updatingRangeLength: nextRouteStack.length, presentedIndex: destIndex, - jumpToIndex: destIndex, - toIndex: destIndex, - fromIndex: destIndex, + activeGesture: null, + transitionFromIndex: null, + transitionQueue: [], }, () => { - this.onSpringUpdate(); + this._handleSpringUpdate(); }); }, + _transitionTo: function(destIndex, velocity, jumpSpringTo, cb) { + if (destIndex === this.state.presentedIndex) { + return; + } + if (this.state.transitionFromIndex !== null) { + this.state.transitionQueue.push({ + destIndex, + velocity, + cb, + }); + return; + } + this.state.transitionFromIndex = this.state.presentedIndex; + this.state.presentedIndex = destIndex; + this.state.transitionCb = cb; + this._onAnimationStart(); + if (AnimationsDebugModule) { + AnimationsDebugModule.startRecordingFps(); + } + var sceneConfig = this.state.sceneConfigStack[this.state.transitionFromIndex] || + this.state.sceneConfigStack[this.state.presentedIndex]; + invariant( + sceneConfig, + 'Cannot configure scene at index ' + this.state.transitionFromIndex + ); + if (jumpSpringTo != null) { + this.spring.setCurrentValue(jumpSpringTo); + } + this.spring.setOvershootClampingEnabled(true); + this.spring.getSpringConfig().friction = sceneConfig.springFriction; + this.spring.getSpringConfig().tension = sceneConfig.springTension; + this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity); + this.spring.setEndValue(1); + var willFocusRoute = this._subRouteFocus[this.state.presentedIndex] || this.state.routeStack[this.state.presentedIndex]; + this._emitWillFocus(willFocusRoute); + }, + /** - * TODO: Accept callback for spring completion. + * This happens for each frame of either a gesture or a transition. If both are + * happening, we only set values for the transition and the gesture will catch up later */ - _requestTransitionTo: function(topOfStack) { - if (topOfStack !== this.state.presentedIndex) { - invariant(!this.state.isAnimating, 'Cannot navigate while transitioning'); - this.state.fromIndex = this.state.presentedIndex; - this.state.toIndex = topOfStack; - this.spring.setOvershootClampingEnabled(false); - if (AnimationsDebugModule) { - AnimationsDebugModule.startRecordingFps(); - } - this._transitionToToIndexWithVelocity( - this.state.sceneConfigStack[this.state.fromIndex].defaultTransitionVelocity + _handleSpringUpdate: function() { + // Prioritize handling transition in progress over a gesture: + if (this.state.transitionFromIndex != null) { + this._transitionBetween( + this.state.transitionFromIndex, + this.state.presentedIndex, + this.spring.getCurrentValue() + ); + } else if (this.state.activeGesture != null) { + this._transitionBetween( + this.state.presentedIndex, + this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture), + this.spring.getCurrentValue() ); } }, /** - * `onSpring*` spring delegate. Wired up via `spring.addListener(this)` + * This happens at the end of a transition started by transitionTo */ - onSpringEndStateChange: function() { - if (!this._interactionHandle) { - this._interactionHandle = this.createInteractionHandle(); + _completeTransition: function() { + if (this.spring.getCurrentValue() !== 1) { + if (this.state.pendingGestureProgress) { + this.state.pendingGestureProgress = null; + } + return; } - }, - - onSpringUpdate: function() { - this._transitionBetween( - this.state.fromIndex, - this.state.toIndex, - this.spring.getCurrentValue() - ); - }, - - onSpringAtRest: function() { - this.state.isAnimating = false; - this._completeTransition(); + this._onAnimationEnd(); + var presentedIndex = this.state.presentedIndex; + var didFocusRoute = this._subRouteFocus[presentedIndex] || this.state.routeStack[presentedIndex]; + this._emitDidFocus(didFocusRoute); + if (AnimationsDebugModule) { + AnimationsDebugModule.stopRecordingFps(Date.now()); + } + this.state.transitionFromIndex = null; this.spring.setCurrentValue(0).setAtRest(); + this._hideScenes(); + if (this.state.transitionCb) { + this.state.transitionCb(); + this.state.transitionCb = null; + } if (this._interactionHandle) { this.clearInteractionHandle(this._interactionHandle); this._interactionHandle = null; } - }, - - _completeTransition: function() { - if (this.spring.getCurrentValue() === 1) { - this._onAnimationEnd(); - var presentedIndex = this.state.toIndex; - this.state.presentedIndex = presentedIndex; - this.state.fromIndex = presentedIndex; - var didFocusRoute = this._subRouteFocus[presentedIndex] || this.state.routeStack[presentedIndex]; - this._emitDidFocus(didFocusRoute); - this._removePoppedRoutes(); - if (AnimationsDebugModule) { - AnimationsDebugModule.stopRecordingFps(Date.now()); - } - } else { - this.state.fromIndex = this.state.presentedIndex; - this.state.toIndex = this.state.presentedIndex; + if (this.state.pendingGestureProgress) { + this.spring.setEndValue(this.state.pendingGestureProgress); + return; + } + if (this.state.transitionQueue.length) { + var queuedTransition = this.state.transitionQueue.shift(); + this._transitionTo( + queuedTransition.destIndex, + queuedTransition.velocity, + null, + queuedTransition.cb + ); } - this._hideOtherScenes(this.state.presentedIndex); - }, - - _transitionToToIndexWithVelocity: function(v) { - this._configureSpring( - // For visual consistency, the from index is always used to configure the spring - this.state.sceneConfigStack[this.state.fromIndex] - ); - this._onAnimationStart(); - this.state.isAnimating = true; - this.spring.setVelocity(v); - this.spring.setEndValue(1); - var willFocusRoute = this._subRouteFocus[this.state.toIndex] || this.state.routeStack[this.state.toIndex]; - this._emitWillFocus(willFocusRoute); - }, - - _transitionToFromIndexWithVelocity: function(v) { - this._configureSpring( - this.state.sceneConfigStack[this.state.fromIndex] - ); - this.state.isAnimating = true; - this.spring.setVelocity(v); - this.spring.setEndValue(0); }, _emitDidFocus: function(route) { @@ -609,9 +625,11 @@ var Navigator = React.createClass({ /** * Does not delete the scenes - merely hides them. */ - _hideOtherScenes: function(activeIndex) { + _hideScenes: function() { for (var i = 0; i < this.state.routeStack.length; i++) { - if (i === activeIndex) { + // This gets called when we detach a gesture, so there will not be a + // current gesture, but there might be a transition in progress + if (i === this.state.presentedIndex || i === this.state.transitionFromIndex) { continue; } var sceneRef = 'scene_' + i; @@ -621,22 +639,31 @@ var Navigator = React.createClass({ }, _onAnimationStart: function() { - this._setRenderSceneToHarwareTextureAndroid(this.state.fromIndex, true); - this._setRenderSceneToHarwareTextureAndroid(this.state.toIndex, true); + var fromIndex = this.state.presentedIndex; + var toIndex = this.state.presentedIndex; + if (this.state.transitionFromIndex != null) { + fromIndex = this.state.transitionFromIndex; + } else if (this.state.activeGesture) { + toIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); + } + this._setRenderSceneToHarwareTextureAndroid(fromIndex, true); + this._setRenderSceneToHarwareTextureAndroid(toIndex, true); var navBar = this._navBar; if (navBar && navBar.onAnimationStart) { - navBar.onAnimationStart(this.state.fromIndex, this.state.toIndex); + navBar.onAnimationStart(fromIndex, toIndex); } }, _onAnimationEnd: function() { - this._setRenderSceneToHarwareTextureAndroid(this.state.fromIndex, false); - this._setRenderSceneToHarwareTextureAndroid(this.state.toIndex, false); + var max = this.state.routeStack.length - 1; + for (var index = 0; index <= max; index++) { + this._setRenderSceneToHarwareTextureAndroid(index, false); + } var navBar = this._navBar; if (navBar && navBar.onAnimationEnd) { - navBar.onAnimationEnd(this.state.fromIndex, this.state.toIndex); + navBar.onAnimationEnd(); } }, @@ -648,16 +675,6 @@ var Navigator = React.createClass({ viewAtIndex.setNativeProps({renderToHardwareTextureAndroid: shouldRenderToHardwareTexture}); }, - /** - * Becomes the responder on touch start (capture) while animating so that it - * blocks all touch interactions inside of it. However, this responder lock - * means nothing more than that. We record if the sole reason for being - * responder is to block interactions (`isResponderOnlyToBlockTouches`). - */ - _handleStartShouldSetPanResponderCapture: function(e, gestureState) { - return this.state.isAnimating; - }, - _handleMoveShouldSetPanResponder: function(e, gestureState) { var currentRoute = this.state.routeStack[this.state.presentedIndex]; var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; @@ -675,18 +692,12 @@ var Navigator = React.createClass({ _handlePanResponderGrant: function(e, gestureState) { invariant( - this._expectingGestureGrant || this.state.isAnimating, + this._expectingGestureGrant, 'Responder granted unexpectedly.' ); - this._activeGestureAction = this._expectingGestureGrant; + this._attachGesture(this._expectingGestureGrant); + this._onAnimationStart(); this._expectingGestureGrant = null; - this.state.isResponderOnlyToBlockTouches = this.state.isAnimating; - if (!this.state.isAnimating) { - this.state.fromIndex = this.state.presentedIndex; - var gestureSceneDelta = this._deltaForGestureAction(this._activeGestureAction); - this.state.toIndex = this.state.presentedIndex + gestureSceneDelta; - this._onAnimationStart(); - } }, _deltaForGestureAction: function(gestureAction) { @@ -704,13 +715,9 @@ var Navigator = React.createClass({ _handlePanResponderRelease: function(e, gestureState) { var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - var releaseGestureAction = this._activeGestureAction; - this._activeGestureAction = null; - if (this.state.isResponderOnlyToBlockTouches) { - this.state.isResponderOnlyToBlockTouches = false; - return; - } + var releaseGestureAction = this.state.activeGesture; var releaseGesture = sceneConfig.gestures[releaseGestureAction]; + var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); if (this.spring.getCurrentValue() === 0) { // The spring is at zero, so the gesture is already complete this.spring.setCurrentValue(0).setAtRest(); @@ -733,38 +740,86 @@ var Navigator = React.createClass({ var hasGesturedEnoughToComplete = gestureDistance > releaseGesture.fullDistance * releaseGesture.stillCompletionRatio; transitionVelocity = hasGesturedEnoughToComplete ? releaseGesture.snapVelocity : -releaseGesture.snapVelocity; } - this.spring.setOvershootClampingEnabled(true); if (transitionVelocity < 0 || this._doesGestureOverswipe(releaseGestureAction)) { - this._transitionToFromIndexWithVelocity(transitionVelocity); + // This gesture is to an overswiped region or does not have enough velocity to complete + // If we are currently mid-transition, then this gesture was a pending gesture. Because this gesture takes no action, we can stop here + if (this.state.transitionFromIndex == null) { + // There is no current transition, so we need to transition back to the presented index + var transitionBackToPresentedIndex = this.state.presentedIndex; + // slight hack: change the presented index for a moment in order to transitionTo correctly + this.state.presentedIndex = destIndex; + this._transitionTo( + transitionBackToPresentedIndex, + - transitionVelocity, + 1 - this.spring.getCurrentValue() + ); + } } else { - this._transitionToToIndexWithVelocity(transitionVelocity); + // The gesture has enough velocity to complete, so we transition to the gesture's destination + this._transitionTo(destIndex, transitionVelocity); } + this._detachGesture(); }, _handlePanResponderTerminate: function(e, gestureState) { - this._activeGestureAction = null; - this.state.isResponderOnlyToBlockTouches = false; - this._transitionToFromIndexWithVelocity(0); + var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); + this._detachGesture(); + var transitionBackToPresentedIndex = this.state.presentedIndex; + // slight hack: change the presented index for a moment in order to transitionTo correctly + this.state.presentedIndex = destIndex; + this._transitionTo( + transitionBackToPresentedIndex, + null, + 1 - this.spring.getCurrentValue() + ); + }, + + _attachGesture: function(gestureId) { + this.state.activeGesture = gestureId; + }, + + _detachGesture: function() { + this.state.activeGesture = null; + this.state.pendingGestureProgress = null; + this._hideScenes(); }, _handlePanResponderMove: function(e, gestureState) { - if (!this.state.isResponderOnlyToBlockTouches) { - var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - var gesture = sceneConfig.gestures[this._activeGestureAction]; - var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top'; - var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top'; - var distance = isTravelVertical ? gestureState.dy : gestureState.dx; - distance = isTravelInverted ? - distance : distance; - var gestureDetectMovement = gesture.gestureDetectMovement; - var nextProgress = (distance - gestureDetectMovement) / - (gesture.fullDistance - gestureDetectMovement); - if (this._doesGestureOverswipe(this._activeGestureAction)) { - var frictionConstant = gesture.overswipe.frictionConstant; - var frictionByDistance = gesture.overswipe.frictionByDistance; - var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance)); - nextProgress *= frictionRatio; - } - this.spring.setCurrentValue(clamp(0, nextProgress, 1)); + var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; + if (this.state.activeGesture) { + var gesture = sceneConfig.gestures[this.state.activeGesture]; + return this._moveAttachedGesture(gesture, gestureState); + } + var matchedGesture = this._matchGestureAction(sceneConfig.gestures, gestureState); + if (matchedGesture) { + this._attachGesture(matchedGesture); + } + }, + + _moveAttachedGesture: function(gesture, gestureState) { + var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top'; + var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top'; + var distance = isTravelVertical ? gestureState.dy : gestureState.dx; + distance = isTravelInverted ? - distance : distance; + var gestureDetectMovement = gesture.gestureDetectMovement; + var nextProgress = (distance - gestureDetectMovement) / + (gesture.fullDistance - gestureDetectMovement); + if (nextProgress < 0 && gesture.isDetachable) { + this._detachGesture(); + } + if (this._doesGestureOverswipe(this.state.activeGesture)) { + var frictionConstant = gesture.overswipe.frictionConstant; + var frictionByDistance = gesture.overswipe.frictionByDistance; + var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance)); + nextProgress *= frictionRatio; + } + nextProgress = clamp(0, nextProgress, 1); + if (this.state.transitionFromIndex != null) { + this.state.pendingGestureProgress = nextProgress; + } else if (this.state.pendingGestureProgress) { + this.spring.setEndValue(nextProgress); + } else { + this.spring.setCurrentValue(nextProgress); } }, @@ -772,9 +827,6 @@ var Navigator = React.createClass({ if (!gestures) { return null; } - if (this.state.isResponderOnlyToBlockTouches || this.state.isAnimating) { - return null; - } var matchedGesture = null; GESTURE_ACTIONS.some((gestureName) => { var gesture = gestures[gestureName]; @@ -815,7 +867,7 @@ var Navigator = React.createClass({ return; } // Use toIndex animation when we move forwards. Use fromIndex when we move back - var sceneConfigIndex = this.state.presentedIndex < toIndex ? toIndex : fromIndex; + var sceneConfigIndex = fromIndex < toIndex ? toIndex : fromIndex; var sceneConfig = this.state.sceneConfigStack[sceneConfigIndex]; // this happens for overswiping when there is no scene at toIndex if (!sceneConfig) { @@ -850,10 +902,6 @@ var Navigator = React.createClass({ this.state.updatingRangeLength = this.state.routeStack.length; }, - _canNavigate: function() { - return !this.state.isAnimating; - }, - _getDestIndexWithinBounds: function(n) { var currentIndex = this.state.presentedIndex; var destIndex = currentIndex + n; @@ -871,17 +919,13 @@ var Navigator = React.createClass({ _jumpN: function(n) { var destIndex = this._getDestIndexWithinBounds(n); - if (!this._canNavigate()) { - return; // It's busy animating or transitioning. - } var requestTransitionAndResetUpdatingRange = () => { - this._requestTransitionTo(destIndex); + this._transitionTo(destIndex); this._resetUpdatingRange(); }; this.setState({ updatingRangeStart: destIndex, updatingRangeLength: 1, - toIndex: destIndex, }, requestTransitionAndResetUpdatingRange); }, @@ -904,9 +948,6 @@ var Navigator = React.createClass({ push: function(route) { invariant(!!route, 'Must supply route to push'); - if (!this._canNavigate()) { - return; // It's busy animating or transitioning. - } var activeLength = this.state.presentedIndex + 1; var activeStack = this.state.routeStack.slice(0, activeLength); var activeIDStack = this.state.idStack.slice(0, activeLength); @@ -917,34 +958,34 @@ var Navigator = React.createClass({ this.props.configureScene(route), ]); var requestTransitionAndResetUpdatingRange = () => { - this._requestTransitionTo(nextStack.length - 1); + this._transitionTo(nextStack.length - 1); this._resetUpdatingRange(); }; - var navigationState = { - toRoute: route, - fromRoute: this.state.routeStack[this.state.routeStack.length - 1], - }; this.setState({ idStack: nextIDStack, routeStack: nextStack, sceneConfigStack: nextAnimationConfigStack, - jumpToIndex: nextStack.length - 1, updatingRangeStart: nextStack.length - 1, updatingRangeLength: 1, }, requestTransitionAndResetUpdatingRange); }, _popN: function(n) { - if (n === 0 || !this._canNavigate()) { + if (n === 0) { return; } invariant( this.state.presentedIndex - n >= 0, 'Cannot pop below zero' ); - this.state.jumpToIndex = this.state.presentedIndex - n; - this._requestTransitionTo( - this.state.presentedIndex - n + var popIndex = this.state.presentedIndex - n; + this._transitionTo( + popIndex, + null, // default velocity + null, // no spring jumping + () => { + this._cleanScenesPastIndex(popIndex); + } ); }, @@ -1026,7 +1067,7 @@ var Navigator = React.createClass({ }, replacePreviousAndPop: function(route) { - if (this.state.routeStack.length < 2 || !this._canNavigate()) { + if (this.state.routeStack.length < 2) { return; } this.replacePrevious(route); @@ -1035,11 +1076,9 @@ var Navigator = React.createClass({ resetTo: function(route) { invariant(!!route, 'Must supply route to push'); - if (this._canNavigate()) { - this.replaceAtIndex(route, 0, () => { - this.popToRoute(route); - }); - } + this.replaceAtIndex(route, 0, () => { + this.popToRoute(route); + }); }, getCurrentRoutes: function() { @@ -1055,8 +1094,8 @@ var Navigator = React.createClass({ this.props.onItemRef && this.props.onItemRef(ref, itemIndex); }, - _removePoppedRoutes: function() { - var newStackLength = this.state.jumpToIndex + 1; + _cleanScenesPastIndex: function(index) { + var newStackLength = index + 1; // Remove any unneeded rendered routes. if (newStackLength < this.state.routeStack.length) { var updatingRangeStart = newStackLength; // One past the top diff --git a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js index fa62b3f455baf6..a43f2dafdebba9 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js +++ b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js @@ -33,6 +33,8 @@ var StaticContainer = require('StaticContainer.react'); var StyleSheet = require('StyleSheet'); var View = require('View'); +var invariant = require('invariant'); + var Interpolators = NavigatorBreadcrumbNavigationBarStyles.Interpolators; var PropTypes = React.PropTypes; @@ -99,6 +101,10 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({ var oldDistToCenter = index - fromIndex; var newDistToCenter = index - toIndex; var interpolate; + invariant( + Interpolators[index], + 'Cannot find breadcrumb interpolators for ' + index + ); if (oldDistToCenter > 0 && newDistToCenter === 0 || newDistToCenter > 0 && oldDistToCenter === 0) { interpolate = Interpolators[index].RightToCenter; @@ -146,10 +152,9 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({ } }, - onAnimationEnd: function(fromIndex, toIndex) { - var max = Math.max(fromIndex, toIndex); - var min = Math.min(fromIndex, toIndex); - for (var index = min; index <= max; index++) { + onAnimationEnd: function() { + var max = this.props.navState.routeStack.length - 1; + for (var index = 0; index <= max; index++) { this._setRenderViewsToHardwareTextureAndroid(index, false); } }, diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index ac16542adc2ea7..eaeb23c99f7534 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -243,6 +243,9 @@ var BaseOverswipeConfig = { var BaseLeftToRightGesture = { + // If the gesture can end and restart during one continuous touch + isDetachable: false, + // How far the swipe must drag to start transitioning gestureDetectMovement: 2, @@ -323,11 +326,13 @@ var NavigatorSceneConfigs = { ...BaseLeftToRightGesture, overswipe: BaseOverswipeConfig, edgeHitWidth: null, + isDetachable: true, }, jumpForward: { ...BaseRightToLeftGesture, overswipe: BaseOverswipeConfig, edgeHitWidth: null, + isDetachable: true, }, }, animationInterpolators: { From d662a307b72afe60486d264571063ab3700c46fe Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Mon, 27 Apr 2015 23:32:11 -0700 Subject: [PATCH 128/250] [Docs] Fix missing : in Swift NativeModules doc --- docs/NativeModulesIOS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/NativeModulesIOS.md b/docs/NativeModulesIOS.md index df3b4d65b20a36..24821ff88d6c59 100644 --- a/docs/NativeModulesIOS.md +++ b/docs/NativeModulesIOS.md @@ -285,7 +285,7 @@ class CalendarManager: NSObject { } ``` -> **NOTE** It is important to use the @objc modifiers to ensure the class and functions are exported properly to the Objective-C runtime. +> **NOTE**: It is important to use the @objc modifiers to ensure the class and functions are exported properly to the Objective-C runtime. Then create a private implementation file that will register the required information with the React Native bridge: @@ -300,4 +300,4 @@ RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:( @end ``` -You can also use `RCT_EXTERN_REMAP_MODULE` and `RCT_EXTERN_REMAP_METHOD` to alter the JavaScript name of the module or methods you are exporting. For more information see [`RCTBridgeModule`](https://github.com/facebook/react-native/blob/master/React/Base/RCTBridgeModule.h). \ No newline at end of file +You can also use `RCT_EXTERN_REMAP_MODULE` and `RCT_EXTERN_REMAP_METHOD` to alter the JavaScript name of the module or methods you are exporting. For more information see [`RCTBridgeModule`](https://github.com/facebook/react-native/blob/master/React/Base/RCTBridgeModule.h). From bc24be8584c47e5c091dd08880c3073fe10e72a4 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 28 Apr 2015 04:53:55 -0700 Subject: [PATCH 129/250] Fixed nil object insertion crash due to incorrectly encoded HTTP response body data --- Libraries/Network/RCTDataManager.m | 2 +- React/Base/RCTBridge.m | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Libraries/Network/RCTDataManager.m b/Libraries/Network/RCTDataManager.m index 634d325e913359..63aa8f10ae1ebd 100644 --- a/Libraries/Network/RCTDataManager.m +++ b/Libraries/Network/RCTDataManager.m @@ -61,7 +61,7 @@ @implementation RCTDataManager responseJSON = @{ @"status": @0, @"responseHeaders": @{}, - @"responseText": [connectionError localizedDescription] + @"responseText": [connectionError localizedDescription] ?: [NSNull null]; }; } diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 7dc2322f0c0813..4a14713c75f4df 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -191,6 +191,7 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { superclass = class_getSuperclass(superclass); } } + free(classes); } }); From f7276b0ae4b0d8b7b66b6e23acb117248e9ce804 Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Tue, 28 Apr 2015 07:31:11 -0700 Subject: [PATCH 130/250] [React Native] Fix borderWidth setter bug --- React/Views/RCTViewManager.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 8388b83cfb489c..62fb29116f5540 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -164,7 +164,9 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *) RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(borderWidth, CGFloat); +RCT_CUSTOM_SHADOW_PROPERTY(borderWidth, CGFloat, RCTShadowView) { + [view setBorderWidth:[RCTConvert CGFloat:json]]; +} RCT_EXPORT_SHADOW_PROPERTY(marginTop, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(marginRight, CGFloat); From 2f4430cf515e42cbd298ea5bc69a91db604bdcc6 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Tue, 28 Apr 2015 08:02:56 -0700 Subject: [PATCH 131/250] [ReactNative] Fix bridge event dedupe --- React/Base/RCTBridge.m | 119 ++++++++++++++++++++------- React/Base/RCTJavaScriptExecutor.h | 9 ++ React/Executors/RCTContextExecutor.m | 20 +++-- 3 files changed, 113 insertions(+), 35 deletions(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 4a14713c75f4df..5b6a92682afbdf 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -672,6 +672,8 @@ - (NSString *)description */ static NSMutableDictionary *RCTLocalModuleIDs; static NSMutableDictionary *RCTLocalMethodIDs; +static NSMutableArray *RCTLocalModuleNames; +static NSMutableArray *RCTLocalMethodNames; static NSDictionary *RCTLocalModulesConfig() { static NSMutableDictionary *localModules; @@ -680,6 +682,8 @@ - (NSString *)description RCTLocalModuleIDs = [[NSMutableDictionary alloc] init]; RCTLocalMethodIDs = [[NSMutableDictionary alloc] init]; + RCTLocalModuleNames = [[NSMutableArray alloc] init]; + RCTLocalMethodNames = [[NSMutableArray alloc] init]; localModules = [[NSMutableDictionary alloc] init]; for (NSString *moduleDotMethod in RCTJSMethods()) { @@ -711,6 +715,8 @@ - (NSString *)description // Add module and method lookup RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"]; RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"]; + [RCTLocalModuleNames addObject:moduleName]; + [RCTLocalMethodNames addObject:methodName]; } }); @@ -1072,7 +1078,7 @@ - (void)invalidate #pragma mark - RCTBridge methods /** - * Like JS::call, for objective-c. + * Public. Can be invoked from any thread. */ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args { @@ -1083,12 +1089,10 @@ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod]; RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod); - if (!_loading) { - [self _invokeAndProcessModule:@"BatchedBridge" - method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, args ?: @[]] - context:RCTGetExecutorID(_javaScriptExecutor)]; - } + [self _invokeAndProcessModule:@"BatchedBridge" + method:@"callFunctionReturnFlushedQueue" + arguments:@[moduleID, methodID, args ?: @[]] + context:RCTGetExecutorID(_javaScriptExecutor)]; } /** @@ -1106,10 +1110,17 @@ - (void)_immediatelyCallTimer:(NSNumber *)timer if (!_loading) { #if BATCHED_BRIDGE - [self _actuallyInvokeAndProcessModule:@"BatchedBridge" - method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, @[@[timer]]] - context:RCTGetExecutorID(_javaScriptExecutor)]; + dispatch_block_t block = ^{ + [self _actuallyInvokeAndProcessModule:@"BatchedBridge" + method:@"callFunctionReturnFlushedQueue" + arguments:@[moduleID, methodID, @[@[timer]]] + context:RCTGetExecutorID(_javaScriptExecutor)]; + }; + if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) { + [_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block]; + } else { + [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; + } #else @@ -1163,33 +1174,83 @@ - (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID } } +/** + * Called by enqueueJSCall from any thread, or from _immediatelyCallTimer, + * on the JS thread, but only in non-batched mode. + */ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context { #if BATCHED_BRIDGE - RCTProfileBeginEvent(); - if ([module isEqualToString:@"RCTEventEmitter"]) { - for (NSDictionary *call in _scheduledCalls) { - if ([call[@"module"] isEqualToString:module] && [call[@"method"] isEqualToString:method] && [call[@"args"][0] isEqualToString:args[0]]) { - [_scheduledCalls removeObject:call]; + __weak NSMutableArray *weakScheduledCalls = _scheduledCalls; + __weak RCTSparseArray *weakScheduledCallbacks = _scheduledCallbacks; + + [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ + RCTProfileBeginEvent(); + + NSMutableArray *scheduledCalls = weakScheduledCalls; + RCTSparseArray *scheduledCallbacks = weakScheduledCallbacks; + if (!scheduledCalls || !scheduledCallbacks) { + return; + } + + /** + * Event deduping + * + * Right now we make a lot of assumptions about the arguments structure + * so just iterate if it's a `callFunctionReturnFlushedQueue()` + */ + if ([method isEqualToString:@"callFunctionReturnFlushedQueue"]) { + NSString *moduleName = RCTLocalModuleNames[[args[0] integerValue]]; + /** + * Keep going if it any event emmiter, e.g. RCT(Device|NativeApp)?EventEmitter + */ + if ([moduleName hasSuffix:@"EventEmitter"]) { + for (NSDictionary *call in [scheduledCalls copy]) { + NSArray *callArgs = call[@"args"]; + /** + * If it's the same module && method call on the bridge && + * the same EventEmitter module && method + */ + if ( + [call[@"module"] isEqualToString:module] && + [call[@"method"] isEqualToString:method] && + [callArgs[0] isEqual:args[0]] && + [callArgs[1] isEqual:args[1]] + ) { + /** + * args[2] contains the actual arguments for the event call, where + * args[2][0] is the target for RCTEventEmitter or the eventName + * for the other EventEmitters + * if RCTEventEmitter we need to compare args[2][1] that will be + * the eventName + */ + if ( + [args[2][0] isEqual:callArgs[2][0]] && + ([moduleName isEqualToString:@"RCTEventEmitter"] ? [args[2][1] isEqual:callArgs[2][1]] : YES) + ) { + [scheduledCalls removeObject:call]; + } + } + } } } - } - id call = @{ - @"module": module, - @"method": method, - @"args": args, - @"context": context ?: @0, - }; + id call = @{ + @"module": module, + @"method": method, + @"args": args, + @"context": context ?: @0, + }; - if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) { - _scheduledCallbacks[args[0]] = call; - } else { - [_scheduledCalls addObject:call]; - } + if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) { + scheduledCallbacks[args[0]] = call; + } else { + [scheduledCalls addObject:call]; + } - RCTProfileEndEvent(@"enqueue_call", @"objc_call", call); + RCTProfileEndEvent(@"enqueue_call", @"objc_call", call); + }]; } - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context diff --git a/React/Base/RCTJavaScriptExecutor.h b/React/Base/RCTJavaScriptExecutor.h index eb7fd7d31947cd..8f1eb8a98c076a 100644 --- a/React/Base/RCTJavaScriptExecutor.h +++ b/React/Base/RCTJavaScriptExecutor.h @@ -49,6 +49,15 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error); */ - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block; +@optional + +/** + * Special case for Timers + ContextExecutor - instead of the default + * if jsthread then call else dispatch call on jsthread + * ensure the call is made async on the jsthread + */ +- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block; + @end static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID"; diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index f5089a9d6a76ca..412ffd25693c36 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -312,12 +312,20 @@ - (void)executeApplicationScript:(NSString *)script - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block { - /** - * Always dispatch async, ensure there are no sync calls on the JS thread - * otherwise timers can cause a deadlock - */ - [self performSelector:@selector(_runBlock:) - onThread:_javaScriptThread withObject:block waitUntilDone:NO]; + if ([NSThread currentThread] != _javaScriptThread) { + [self performSelector:@selector(executeBlockOnJavaScriptQueue:) + onThread:_javaScriptThread withObject:block waitUntilDone:NO]; + } else { + block(); + } +} + +- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block +{ + [self performSelector:@selector(executeBlockOnJavaScriptQueue:) + onThread:_javaScriptThread + withObject:block + waitUntilDone:NO]; } - (void)_runBlock:(dispatch_block_t)block From f2b38bacbfc032d8f1acbd6ff3541231007d1816 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Mon, 27 Apr 2015 15:58:21 -0700 Subject: [PATCH 132/250] [ReactNative] parse localized JSON files in JS, not in ObjC --- .../BatchedBridgedModules/__mocks__/NativeModules.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js index 7eed36e01b1d87..4d5c7b34c3b9c8 100644 --- a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js +++ b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js @@ -5,9 +5,9 @@ var NativeModules = { I18n: { - translationsDictionary: { + translationsDictionary: JSON.stringify({ 'Good bye, {name}!|Bye message': '¡Adiós {name}!', - }, + }), }, Timing: { createTimer: jest.genMockFunction(), From 48297753cf482a8be591f3823f0a95090c917126 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 28 Apr 2015 09:53:25 -0700 Subject: [PATCH 133/250] Refactored RKSounds, moved it into it's own library, and fixed tests --- Libraries/Network/RCTDataManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Network/RCTDataManager.m b/Libraries/Network/RCTDataManager.m index 63aa8f10ae1ebd..1d0a793de8e454 100644 --- a/Libraries/Network/RCTDataManager.m +++ b/Libraries/Network/RCTDataManager.m @@ -61,7 +61,7 @@ @implementation RCTDataManager responseJSON = @{ @"status": @0, @"responseHeaders": @{}, - @"responseText": [connectionError localizedDescription] ?: [NSNull null]; + @"responseText": [connectionError localizedDescription] ?: [NSNull null] }; } From 9209c444c7ffffbe343ab863c06c73354feae3f7 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Tue, 28 Apr 2015 13:07:55 -0700 Subject: [PATCH 134/250] [ReactNative] Androidized Navigator animations Summary: Added configurations for android-like animations, with gestures disabled. @public Test Plan: Tested on AdsManager Android --- .../Navigator/NavigatorSceneConfigs.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index eaeb23c99f7534..b1c837025feacc 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -101,6 +101,30 @@ var FadeToTheLeft = { }, }; +var FadeIn = { + opacity: { + from: 0, + to: 1, + min: 0.5, + max: 1, + type: 'linear', + extrapolate: false, + round: 100, + }, +}; + +var FadeOut = { + opacity: { + from: 1, + to: 0, + min: 0, + max: 0.5, + type: 'linear', + extrapolate: false, + round: 100, + }, +}; + var ToTheLeft = { transformTranslate: { from: {x: 0, y: 0, z: 0}, @@ -236,6 +260,43 @@ var FromTheFront = { }, }; +var ToTheBackAndroid = { + opacity: { + value: 1, + type: 'constant', + }, +}; + +var FromTheFrontAndroid = { + opacity: { + from: 0, + to: 1, + min: 0, + max: 1, + type: 'linear', + extrapolate: false, + round: 100, + }, + transformTranslate: { + from: {x: 0, y: 50, z: 0}, + to: {x: 0, y: 0, z: 0}, + min: 0, + max: 1, + type: 'linear', + extrapolate: true, + round: PixelRatio.get(), + }, + translateY: { + from: 50, + to: 0, + min: 0, + max: 1, + type: 'linear', + extrapolate: true, + round: PixelRatio.get(), + }, +}; + var BaseOverswipeConfig = { frictionConstant: 1, frictionByDistance: 1.5, @@ -319,6 +380,22 @@ var NavigatorSceneConfigs = { out: buildStyleInterpolator(ToTheBack), }, }, + FloatFromBottomAndroid: { + ...BaseConfig, + gestures: null, + animationInterpolators: { + into: buildStyleInterpolator(FromTheFrontAndroid), + out: buildStyleInterpolator(ToTheBackAndroid), + }, + }, + FadeAndroid: { + ...BaseConfig, + gestures: null, + animationInterpolators: { + into: buildStyleInterpolator(FadeIn), + out: buildStyleInterpolator(FadeOut), + }, + }, HorizontalSwipeJump: { ...BaseConfig, gestures: { From ecf7baebea8d82dc48c6dad3cf2a7a4d17513c6b Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 28 Apr 2015 15:41:33 -0700 Subject: [PATCH 135/250] [React Native] Actually bring back console.error redboxes with disable flag --- .../Initialization/ExceptionsManager.js | 58 +++++++++++++++---- Libraries/ReactIOS/renderApplication.ios.js | 2 + 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index c5476eaab5cfe3..b5868139c7c135 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -16,6 +16,7 @@ var RCTExceptionsManager = require('NativeModules').ExceptionsManager; var loadSourceMap = require('loadSourceMap'); var parseErrorStack = require('parseErrorStack'); +var stringifySafe = require('stringifySafe'); var sourceMapPromise; @@ -25,17 +26,11 @@ type Exception = { message: string; } -function handleException(e: Exception) { - var stack = parseErrorStack(e); - console.error( - 'Err0r: ' + - '\n stack: \n' + stackToString(stack) + - '\n URL: ' + e.sourceURL + - '\n line: ' + e.line + - '\n message: ' + e.message - ); - +function reportException(e: Exception, stack?: any) { if (RCTExceptionsManager) { + if (!stack) { + stack = parseErrorStack(e); + } RCTExceptionsManager.reportUnhandledException(e.message, stack); if (__DEV__) { (sourceMapPromise = sourceMapPromise || loadSourceMap()) @@ -50,6 +45,47 @@ function handleException(e: Exception) { } } +function handleException(e: Exception) { + var stack = parseErrorStack(e); + var msg = + 'Error: ' + e.message + + '\n stack: \n' + stackToString(stack) + + '\n URL: ' + e.sourceURL + + '\n line: ' + e.line + + '\n message: ' + e.message; + if (console.errorOriginal) { + console.errorOriginal(msg); + } else { + console.error(msg); + } + reportException(e, stack); +} + +/** + * Shows a redbox with stacktrace for all console.error messages. Disable by + * setting `console.reportErrorsAsExceptions = false;` in your app. + */ +function installConsoleErrorReporter() { + if (console.reportException) { + return; // already installed + } + console.reportException = reportException; + console.errorOriginal = console.error.bind(console); + console.error = function reactConsoleError() { + console.errorOriginal.apply(null, arguments); + if (!console.reportErrorsAsExceptions) { + return; + } + var str = Array.prototype.map.call(arguments, stringifySafe).join(', '); + var error: any = new Error('console.error: ' + str); + error.framesToPop = 1; + reportException(error); + }; + if (console.reportErrorsAsExceptions === undefined) { + console.reportErrorsAsExceptions = true; // Individual apps can disable this + } +} + function stackToString(stack) { var maxLength = Math.max.apply(null, stack.map(frame => frame.methodName.length)); return stack.map(frame => stackFrameToString(frame, maxLength)).join('\n'); @@ -71,4 +107,4 @@ function fillSpaces(n) { return new Array(n + 1).join(' '); } -module.exports = { handleException }; +module.exports = { handleException, installConsoleErrorReporter }; diff --git a/Libraries/ReactIOS/renderApplication.ios.js b/Libraries/ReactIOS/renderApplication.ios.js index 16052c6fa69605..39e5720f520eb9 100644 --- a/Libraries/ReactIOS/renderApplication.ios.js +++ b/Libraries/ReactIOS/renderApplication.ios.js @@ -11,6 +11,8 @@ */ 'use strict'; +require('ExceptionsManager').installConsoleErrorReporter(); + var React = require('React'); var StyleSheet = require('StyleSheet'); var View = require('View'); From 43e7e69841f929b13dc98a2371eeb2e8da9e4124 Mon Sep 17 00:00:00 2001 From: Tim Yung Date: Tue, 28 Apr 2015 15:55:42 -0700 Subject: [PATCH 136/250] JS: Use Object.defineProperty for Array Polyfills --- .../haste/polyfills/Array.prototype.es6.js | 82 +++++-------------- 1 file changed, 20 insertions(+), 62 deletions(-) diff --git a/packager/react-packager/src/DependencyResolver/haste/polyfills/Array.prototype.es6.js b/packager/react-packager/src/DependencyResolver/haste/polyfills/Array.prototype.es6.js index 8df5bbcc05ab41..80e62f05dee332 100644 --- a/packager/react-packager/src/DependencyResolver/haste/polyfills/Array.prototype.es6.js +++ b/packager/react-packager/src/DependencyResolver/haste/polyfills/Array.prototype.es6.js @@ -3,53 +3,14 @@ * * @provides Array.prototype.es6 * @polyfill - * @requires __DEV__ */ /*eslint-disable */ /*jslint bitwise: true */ -(function (undefined) { - if (__DEV__) { - // Define DEV-only setter that blows up when someone incorrectly - // iterates over arrays. - try { - Object.defineProperty && Object.defineProperty( - Array.prototype, - '__ARRAY_ENUMERATION_GUARD__', - { - configurable: true, - enumerable: true, - get: function() { - console.error( - 'Your code is broken! Do not iterate over arrays with ' + - 'for...in.' - ); - } - } - ); - } catch (e) { - // Nothing - } - } - +(function(undefined) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex function findIndex(predicate, context) { - /** - * Why am I seeing this "findIndex" method as a value in my array!? - * - * We polyfill the "findIndex" method -- called like - * `[1, 2, 3].findIndex(1)` -- for older browsers. A side effect of the way - * we do that is that the method is enumerable. If you were incorrectly - * iterating over your array using the object property iterator syntax - * `for (key in obj)` you will see the method name "findIndex" as a key. - * - * To fix your code please do one of the following: - * - * - Use a regular for loop with index. - * - Use one of the array methods: a.forEach, a.map, etc. - * - Guard your body of your loop with a `arr.hasOwnProperty(key)` check. - */ if (this == null) { throw new TypeError( 'Array.prototype.findIndex called on null or undefined' @@ -69,32 +30,29 @@ } if (!Array.prototype.findIndex) { - Array.prototype.findIndex = findIndex; + Object.defineProperty(Array.prototype, 'findIndex', { + enumerable: false, + writable: true, + configurable: true, + value: findIndex + }); } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find if (!Array.prototype.find) { - Array.prototype.find = function(predicate, context) { - /** - * Why am I seeing this "find" method as a value in my array!? - * - * We polyfill the "find" method -- called like - * `[1, 2, 3].find(1)` -- for older browsers. A side effect of the way - * we do that is that the method is enumerable. If you were incorrectly - * iterating over your array using the object property iterator syntax - * `for (key in obj)` you will see the method name "find" as a key. - * - * To fix your code please do one of the following: - * - * - Use a regular for loop with index. - * - Use one of the array methods: a.forEach, a.map, etc. - * - Guard your body of your loop with a `arr.hasOwnProperty(key)` check. - */ - if (this == null) { - throw new TypeError('Array.prototype.find called on null or undefined'); + Object.defineProperty(Array.prototype, 'find', { + enumerable: false, + writable: true, + configurable: true, + value: function(predicate, context) { + if (this == null) { + throw new TypeError( + 'Array.prototype.find called on null or undefined' + ); + } + var index = findIndex.call(this, predicate, context); + return index === -1 ? undefined : this[index]; } - var index = findIndex.call(this, predicate, context); - return index === -1 ? undefined : this[index]; - }; + }); } })(); From 349f8b942a89b6849cab986d7e2e370cdb838a51 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Wed, 29 Apr 2015 00:38:40 -0700 Subject: [PATCH 137/250] [ReactNative] Avoid Navigator invariant in edge case Summary: It is very rare, but sometimes there is no active gesture when the responder gets released. This will avoid a redbox in that case @public Test Plan: Saw the redbox. No longer see the redbox. Hardcoded missing activeGesture while responder and verified no redbox --- Libraries/CustomComponents/Navigator/Navigator.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 4503c4185ac129..6d3d55c316f097 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -716,6 +716,10 @@ var Navigator = React.createClass({ _handlePanResponderRelease: function(e, gestureState) { var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; var releaseGestureAction = this.state.activeGesture; + if (!releaseGestureAction) { + // The gesture may have been detached while responder, so there is no action here + return; + } var releaseGesture = sceneConfig.gestures[releaseGestureAction]; var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); if (this.spring.getCurrentValue() === 0) { From c09bdebcd532bfb487763d1800aaf1f3e4d2f150 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Wed, 29 Apr 2015 01:29:00 -0700 Subject: [PATCH 138/250] Add support for multiline TextInput via UITextView Summary: @nicklockwood - Could I get a review of this? Just took `RCTTextField` and ported it from `UITextField` to `UITextView` as you mentioned in another discussion, and removed any `UITextField` specific attributes. - How do you think this should behave when there are subviews? - Do you know how we can respond to the `UIControlEventEditingDidEndOnExit` event to respond to submit? Because `UITextView` isn't a `UIControl` we can't just use `addTarget` with `UIControlEventEditingDidEndOnExit`. - Any other feedback? Still going to look over the `UITextView` docs in more detail and make sure we expose all important options, and add it to the UIExplorer example, just putting this out here for feedback. ![multiline](https://cloud.githubusercontent.com/assets/90494/7310854/32174d6a-e9e8-11e4-919e-71e54cf3c739.gif) Closes https://github.com/facebook/react-native/pull/991 Github Author: Brent Vatne Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Examples/UIExplorer/TextInputExample.js | 33 ++- .../Text/RCTText.xcodeproj/project.pbxproj | 12 ++ Libraries/Text/RCTTextManager.h | 1 - Libraries/Text/RCTTextManager.m | 2 +- Libraries/Text/RCTTextView.h | 27 +++ Libraries/Text/RCTTextView.m | 199 ++++++++++++++++++ Libraries/Text/RCTTextViewManager.h | 14 ++ Libraries/Text/RCTTextViewManager.m | 64 ++++++ React/Base/RCTConvert.m | 45 ++-- React/Views/RCTTextField.m | 6 +- 10 files changed, 381 insertions(+), 22 deletions(-) create mode 100644 Libraries/Text/RCTTextView.h create mode 100644 Libraries/Text/RCTTextView.m create mode 100644 Libraries/Text/RCTTextViewManager.h create mode 100644 Libraries/Text/RCTTextViewManager.m diff --git a/Examples/UIExplorer/TextInputExample.js b/Examples/UIExplorer/TextInputExample.js index e0ae1b46517070..a32937fa4d1590 100644 --- a/Examples/UIExplorer/TextInputExample.js +++ b/Examples/UIExplorer/TextInputExample.js @@ -88,9 +88,9 @@ var styles = StyleSheet.create({ height: 26, borderWidth: 0.5, borderColor: '#0f0f0f', - padding: 4, flex: 1, fontSize: 13, + padding: 4, }, multiline: { borderWidth: 0.5, @@ -98,6 +98,13 @@ var styles = StyleSheet.create({ flex: 1, fontSize: 13, height: 50, + padding: 4, + }, + multilineWithFontStyles: { + color: 'blue', + fontWeight: 'bold', + fontSize: 18, + fontFamily: 'Cochin', }, eventLabel: { margin: 3, @@ -118,7 +125,7 @@ var styles = StyleSheet.create({ }); exports.title = ''; -exports.description = 'Single-line text inputs.'; +exports.description = 'Single and multi-line text inputs.'; exports.examples = [ { title: 'Auto-focus', @@ -313,7 +320,7 @@ exports.examples = [ }, { title: 'Clear and select', - render: function () { + render: function() { return ( @@ -336,4 +343,24 @@ exports.examples = [ ); } }, + { + title: 'Multiline', + render: function() { + return ( + + + + + ) + } + } ]; diff --git a/Libraries/Text/RCTText.xcodeproj/project.pbxproj b/Libraries/Text/RCTText.xcodeproj/project.pbxproj index 3c4bcf5bae846f..224c7e6b97fa7e 100644 --- a/Libraries/Text/RCTText.xcodeproj/project.pbxproj +++ b/Libraries/Text/RCTText.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 131B6AC01AF0CD0600FFC3E0 /* RCTTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6ABD1AF0CD0600FFC3E0 /* RCTTextView.m */; }; + 131B6AC11AF0CD0600FFC3E0 /* RCTTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6ABF1AF0CD0600FFC3E0 /* RCTTextViewManager.m */; }; 58B511CE1A9E6C5C00147676 /* RCTRawTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511C71A9E6C5C00147676 /* RCTRawTextManager.m */; }; 58B511CF1A9E6C5C00147676 /* RCTShadowRawText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511C91A9E6C5C00147676 /* RCTShadowRawText.m */; }; 58B511D01A9E6C5C00147676 /* RCTShadowText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CB1A9E6C5C00147676 /* RCTShadowText.m */; }; @@ -27,6 +29,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 131B6ABC1AF0CD0600FFC3E0 /* RCTTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextView.h; sourceTree = ""; }; + 131B6ABD1AF0CD0600FFC3E0 /* RCTTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextView.m; sourceTree = ""; }; + 131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextViewManager.h; sourceTree = ""; }; + 131B6ABF1AF0CD0600FFC3E0 /* RCTTextViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextViewManager.m; sourceTree = ""; }; 58B5119B1A9E6C1200147676 /* libRCTText.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTText.a; sourceTree = BUILT_PRODUCTS_DIR; }; 58B511C61A9E6C5C00147676 /* RCTRawTextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRawTextManager.h; sourceTree = ""; }; 58B511C71A9E6C5C00147676 /* RCTRawTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRawTextManager.m; sourceTree = ""; }; @@ -64,6 +70,10 @@ 58B512141A9E6EFF00147676 /* RCTText.m */, 58B511CC1A9E6C5C00147676 /* RCTTextManager.h */, 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */, + 131B6ABC1AF0CD0600FFC3E0 /* RCTTextView.h */, + 131B6ABD1AF0CD0600FFC3E0 /* RCTTextView.m */, + 131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */, + 131B6ABF1AF0CD0600FFC3E0 /* RCTTextViewManager.m */, 58B5119C1A9E6C1200147676 /* Products */, ); indentWidth = 2; @@ -135,8 +145,10 @@ buildActionMask = 2147483647; files = ( 58B511D11A9E6C5C00147676 /* RCTTextManager.m in Sources */, + 131B6AC01AF0CD0600FFC3E0 /* RCTTextView.m in Sources */, 58B511CE1A9E6C5C00147676 /* RCTRawTextManager.m in Sources */, 58B512161A9E6EFF00147676 /* RCTText.m in Sources */, + 131B6AC11AF0CD0600FFC3E0 /* RCTTextViewManager.m in Sources */, 58B511CF1A9E6C5C00147676 /* RCTShadowRawText.m in Sources */, 58B511D01A9E6C5C00147676 /* RCTShadowText.m in Sources */, ); diff --git a/Libraries/Text/RCTTextManager.h b/Libraries/Text/RCTTextManager.h index 13e8f854642da4..91ac87ba88fdbc 100644 --- a/Libraries/Text/RCTTextManager.h +++ b/Libraries/Text/RCTTextManager.h @@ -12,4 +12,3 @@ @interface RCTTextManager : RCTViewManager @end - diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m index e5e9ad00a1ad23..ef518d20483d70 100644 --- a/Libraries/Text/RCTTextManager.m +++ b/Libraries/Text/RCTTextManager.m @@ -123,7 +123,7 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowText *)shadowVie UIEdgeInsets padding = shadowView.paddingAsInsets; return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { - RCTText *text = (RCTText *)viewRegistry[reactTag]; + RCTText *text = viewRegistry[reactTag]; text.contentInset = padding; text.layoutManager = shadowView.layoutManager; text.textContainer = shadowView.textContainer; diff --git a/Libraries/Text/RCTTextView.h b/Libraries/Text/RCTTextView.h new file mode 100644 index 00000000000000..742c815348909f --- /dev/null +++ b/Libraries/Text/RCTTextView.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "RCTView.h" +#import "UIView+React.h" + +@class RCTEventDispatcher; + +@interface RCTTextView : RCTView + +@property (nonatomic, assign) BOOL autoCorrect; +@property (nonatomic, assign) UIEdgeInsets contentInset; +@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; +@property (nonatomic, strong) UIColor *placeholderTextColor; +@property (nonatomic, assign) UIFont *font; + +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; + +@end diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m new file mode 100644 index 00000000000000..a98bbc3ea381bc --- /dev/null +++ b/Libraries/Text/RCTTextView.m @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTTextView.h" + +#import "RCTConvert.h" +#import "RCTEventDispatcher.h" +#import "RCTUtils.h" +#import "UIView+React.h" + +@implementation RCTTextView +{ + RCTEventDispatcher *_eventDispatcher; + BOOL _jsRequestingFirstResponder; + NSString *_placeholder; + UITextView *_placeholderView; + UITextView *_textView; +} + +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +{ + if ((self = [super initWithFrame:CGRectZero])) { + _contentInset = UIEdgeInsetsZero; + _eventDispatcher = eventDispatcher; + _placeholderTextColor = [self defaultPlaceholderTextColor]; + + _textView = [[UITextView alloc] initWithFrame:self.bounds]; + _textView.backgroundColor = [UIColor clearColor]; + _textView.delegate = self; + [self addSubview:_textView]; + } + + return self; +} + +- (void)updateFrames +{ + // Adjust the insets so that they are as close as possible to single-line + // RCTTextField defaults + UIEdgeInsets adjustedInset = (UIEdgeInsets){ + _contentInset.top - 5, _contentInset.left - 4, + _contentInset.bottom, _contentInset.right + }; + + [_textView setFrame:UIEdgeInsetsInsetRect(self.bounds, adjustedInset)]; + [_placeholderView setFrame:UIEdgeInsetsInsetRect(self.bounds, adjustedInset)]; +} + +- (void)updatePlaceholder +{ + [_placeholderView removeFromSuperview]; + _placeholderView = nil; + + if (_placeholder) { + _placeholderView = [[UITextView alloc] initWithFrame:self.bounds]; + _placeholderView.backgroundColor = [UIColor clearColor]; + _placeholderView.scrollEnabled = false; + _placeholderView.attributedText = + [[NSAttributedString alloc] initWithString:_placeholder attributes:@{ + NSFontAttributeName : (_textView.font ? _textView.font : [self defaultPlaceholderFont]), + NSForegroundColorAttributeName : _placeholderTextColor + }]; + + [self insertSubview:_placeholderView belowSubview:_textView]; + [self _setPlaceholderVisibility]; + } +} + +- (void)setFont:(UIFont *)font +{ + _font = font; + _textView.font = _font; + [self updatePlaceholder]; +} + +- (void)setTextColor:(UIColor *)textColor +{ + _textView.textColor = textColor; +} + +- (void)setPlaceholder:(NSString *)placeholder +{ + _placeholder = placeholder; + [self updatePlaceholder]; +} + +- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor +{ + if (placeholderTextColor) { + _placeholderTextColor = placeholderTextColor; + } else { + _placeholderTextColor = [self defaultPlaceholderTextColor]; + } + [self updatePlaceholder]; +} + +- (void)setContentInset:(UIEdgeInsets)contentInset +{ + _contentInset = contentInset; + [self updateFrames]; +} + +- (void)setText:(NSString *)text +{ + if (![text isEqualToString:_textView.text]) { + [_textView setText:text]; + [self _setPlaceholderVisibility]; + } +} + +- (void)_setPlaceholderVisibility +{ + if (_textView.text.length > 0) { + [_placeholderView setHidden:YES]; + } else { + [_placeholderView setHidden:NO]; + } +} + +- (void)setAutoCorrect:(BOOL)autoCorrect +{ + _textView.autocorrectionType = (autoCorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo); +} + +- (BOOL)autoCorrect +{ + return _textView.autocorrectionType == UITextAutocorrectionTypeYes; +} + +- (void)textViewDidBeginEditing:(UITextView *)textView +{ + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus + reactTag:self.reactTag + text:textView.text]; +} + +- (void)textViewDidChange:(UITextView *)textView +{ + [self _setPlaceholderVisibility]; + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange + reactTag:self.reactTag + text:textView.text]; + +} + +- (void)textViewDidEndEditing:(UITextView *)textView +{ + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd + reactTag:self.reactTag + text:textView.text]; +} + +- (BOOL)becomeFirstResponder +{ + _jsRequestingFirstResponder = YES; + BOOL result = [super becomeFirstResponder]; + _jsRequestingFirstResponder = NO; + return result; +} + +- (BOOL)resignFirstResponder +{ + BOOL result = [super resignFirstResponder]; + if (result) { + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur + reactTag:self.reactTag + text:_textView.text]; + } + return result; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + [self updateFrames]; +} + +- (BOOL)canBecomeFirstResponder +{ + return _jsRequestingFirstResponder; +} + +- (UIFont *)defaultPlaceholderFont +{ + return [UIFont fontWithName:@"Helvetica" size:17]; +} + +- (UIColor *)defaultPlaceholderTextColor +{ + return [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.098/255.0 alpha:0.22]; +} + +@end diff --git a/Libraries/Text/RCTTextViewManager.h b/Libraries/Text/RCTTextViewManager.h new file mode 100644 index 00000000000000..fd2f2b44d3d334 --- /dev/null +++ b/Libraries/Text/RCTTextViewManager.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTViewManager.h" + +@interface RCTTextViewManager : RCTViewManager + +@end diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m new file mode 100644 index 00000000000000..5218544d232fe7 --- /dev/null +++ b/Libraries/Text/RCTTextViewManager.m @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTTextViewManager.h" + +#import "RCTBridge.h" +#import "RCTConvert.h" +#import "RCTShadowView.h" +#import "RCTSparseArray.h" +#import "RCTTextView.h" + +@implementation RCTTextViewManager + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + return [[RCTTextView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; +} + +RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL) +RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString) +RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(text, NSString) +RCT_REMAP_VIEW_PROPERTY(clearTextOnFocus, clearsOnBeginEditing, BOOL) +RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType) +RCT_EXPORT_VIEW_PROPERTY(returnKeyType, UIReturnKeyType) +RCT_EXPORT_VIEW_PROPERTY(enablesReturnKeyAutomatically, BOOL) +RCT_EXPORT_VIEW_PROPERTY(secureTextEntry, BOOL) +RCT_REMAP_VIEW_PROPERTY(color, textColor, UIColor) +RCT_REMAP_VIEW_PROPERTY(autoCapitalize, autocapitalizationType, UITextAutocapitalizationType) +RCT_CUSTOM_VIEW_PROPERTY(fontSize, CGFloat, RCTTextView) +{ + view.font = [RCTConvert UIFont:view.font withSize:json ?: @(defaultView.font.pointSize)]; +} +RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, RCTTextView) +{ + view.font = [RCTConvert UIFont:view.font withWeight:json]; // defaults to normal +} +RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, RCTTextView) +{ + view.font = [RCTConvert UIFont:view.font withStyle:json]; // defaults to normal +} +RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextView) +{ + view.font = [RCTConvert UIFont:view.font withFamily:json ?: defaultView.font.familyName]; +} + +- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView +{ + NSNumber *reactTag = shadowView.reactTag; + UIEdgeInsets padding = shadowView.paddingAsInsets; + return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + ((RCTTextView *)viewRegistry[reactTag]).contentInset = padding; + }; +} + +@end diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index eacb03b857a91d..465d1f0bb65413 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -707,35 +707,48 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family const RCTFontWeight RCTDefaultFontWeight = UIFontWeightRegular; const CGFloat RCTDefaultFontSize = 14; - // Get existing properties + // Initialize properties to defaults + CGFloat fontSize = RCTDefaultFontSize; + RCTFontWeight fontWeight = RCTDefaultFontWeight; + NSString *familyName = RCTDefaultFontFamily; BOOL isItalic = NO; BOOL isCondensed = NO; - RCTFontWeight fontWeight = RCTDefaultFontWeight; + if (font) { - family = font.familyName; + familyName = font.familyName ?: RCTDefaultFontFamily; + fontSize = font.pointSize ?: RCTDefaultFontSize; fontWeight = RCTWeightOfFont(font); isItalic = RCTFontIsItalic(font); isCondensed = RCTFontIsCondensed(font); } + // Get font size + fontSize = [self CGFloat:size] ?: fontSize; + + // Get font family + familyName = [self NSString:family] ?: familyName; + // Get font style if (style) { isItalic = [self RCTFontStyle:style]; } - // Get font size - CGFloat fontSize = [self CGFloat:size] ?: RCTDefaultFontSize; + // Get font weight + if (weight) { + fontWeight = [self RCTFontWeight:weight]; + } - // Get font family - NSString *familyName = [self NSString:family] ?: RCTDefaultFontFamily; + // Gracefully handle being given a font name rather than font family, for + // example: "Helvetica Light Oblique" rather than just "Helvetica". if ([UIFont fontNamesForFamilyName:familyName].count == 0) { font = [UIFont fontWithName:familyName size:fontSize]; if (font) { // It's actually a font name, not a font family name, // but we'll do what was meant, not what was said. familyName = font.familyName; - NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute]; - fontWeight = [traits[UIFontWeightTrait] doubleValue]; + fontWeight = RCTWeightOfFont(font); + isItalic = RCTFontIsItalic(font); + isCondensed = RCTFontIsCondensed(font); } else { // Not a valid font or family RCTLogError(@"Unrecognized font family '%@'", familyName); @@ -743,14 +756,16 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family } } - // Get font weight - if (weight) { - fontWeight = [self RCTFontWeight:weight]; + // Get the closest font that matches the given weight for the fontFamily + UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize]; + CGFloat closestWeight; + + if (font && [font.familyName isEqualToString: familyName]) { + closestWeight = RCTWeightOfFont(font); + } else { + closestWeight = INFINITY; } - // Get closest match - UIFont *bestMatch = font; - CGFloat closestWeight = font ? RCTWeightOfFont(font) : INFINITY; for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) { UIFont *match = [UIFont fontWithName:name size:fontSize]; if (isItalic == RCTFontIsItalic(match) && diff --git a/React/Views/RCTTextField.m b/React/Views/RCTTextField.m index ff56214d8ead02..12d52b1b8b7561 100644 --- a/React/Views/RCTTextField.m +++ b/React/Views/RCTTextField.m @@ -54,12 +54,14 @@ static void RCTUpdatePlaceholder(RCTTextField *self) } } -- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor { +- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor +{ _placeholderTextColor = placeholderTextColor; RCTUpdatePlaceholder(self); } -- (void)setPlaceholder:(NSString *)placeholder { +- (void)setPlaceholder:(NSString *)placeholder +{ super.placeholder = placeholder; RCTUpdatePlaceholder(self); } From c82c6a07ae84bb9051e950f45c6d51aec69b11bf Mon Sep 17 00:00:00 2001 From: Andy Street Date: Wed, 29 Apr 2015 04:32:52 -0700 Subject: [PATCH 139/250] [react_native] JS files from D2027859: [react_native] Fix ToTheLeft wizard transitions --- .../Navigator/NavigatorSceneConfigs.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index b1c837025feacc..3b4666b9388ce5 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -139,8 +139,17 @@ var ToTheLeft = { value: 1.0, type: 'constant', }, -}; + translateX: { + from: 0, + to: -Dimensions.get('window').width, + min: 0, + max: 1, + type: 'linear', + extrapolate: true, + round: PixelRatio.get(), + }, +}; var FromTheRight = { opacity: { From 11f204748ddfd89990a7d8c3094836a0baaba44b Mon Sep 17 00:00:00 2001 From: Clay Allsopp Date: Wed, 29 Apr 2015 08:15:28 -0700 Subject: [PATCH 140/250] Add SegmentedControlIOS Summary: Fixes #534: ![screen shot 2015-03-31 at 7 52 10 pm](https://cloud.githubusercontent.com/assets/153704/6934038/742ddd34-d7e3-11e4-8f55-3eb7d9d3f1cd.png) ```jsx console.log(value) } /> ``` This only supports string-based segments, not images. Also doesn't support full customization (no separator images etc); I figure this is a good MVP to lock-down a basic API I also included a snapshot test case, but the images keep coming out funky. When I look at the sim, I see that the text labels show up for the selected segment, but the snapshot keeps coming out with no text on those segments. I tried forcing a delay, but same result. Is that explainable? Obviously happy to change anything about the API, code-style nitpicks, etc Closes https://github.com/facebook/react-native/pull/564 Github Author: Clay Allsopp Test Plan: Imported from GitHub, without a `Test Plan:` line. --- .../UIExplorer/SegmentedControlIOSExample.js | 169 ++++++++++++++++++ Examples/UIExplorer/UIExplorerList.js | 1 + .../SegmentedControlIOS.js | 120 +++++++++++++ Libraries/react-native/react-native.js | 1 + React/React.xcodeproj/project.pbxproj | 12 ++ React/Views/RCTSegmentedControl.h | 20 +++ React/Views/RCTSegmentedControl.m | 57 ++++++ React/Views/RCTSegmentedControlManager.h | 14 ++ React/Views/RCTSegmentedControlManager.m | 39 ++++ 9 files changed, 433 insertions(+) create mode 100644 Examples/UIExplorer/SegmentedControlIOSExample.js create mode 100644 Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js create mode 100644 React/Views/RCTSegmentedControl.h create mode 100644 React/Views/RCTSegmentedControl.m create mode 100644 React/Views/RCTSegmentedControlManager.h create mode 100644 React/Views/RCTSegmentedControlManager.m diff --git a/Examples/UIExplorer/SegmentedControlIOSExample.js b/Examples/UIExplorer/SegmentedControlIOSExample.js new file mode 100644 index 00000000000000..119196d8f8a97d --- /dev/null +++ b/Examples/UIExplorer/SegmentedControlIOSExample.js @@ -0,0 +1,169 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + SegmentedControlIOS, + Text, + View, + StyleSheet +} = React; + +var BasicSegmentedControlExample = React.createClass({ + render() { + return ( + + + + + + + + + ); + } +}); + +var PreSelectedSegmentedControlExample = React.createClass({ + render() { + return ( + + + + + + ); + } +}); + +var MomentarySegmentedControlExample = React.createClass({ + render() { + return ( + + + + + + ); + } +}); + +var DisabledSegmentedControlExample = React.createClass({ + render() { + return ( + + + + + + ); + }, +}); + +var ColorSegmentedControlExample = React.createClass({ + render() { + return ( + + + + + + + + + ); + }, +}); + +var EventSegmentedControlExample = React.createClass({ + getInitialState() { + return { + values: ['One', 'Two', 'Three'], + value: 'Not selected', + selectedIndex: undefined + }; + }, + + render() { + return ( + + + Value: {this.state.value} + + + Index: {this.state.selectedIndex} + + + + ); + }, + + _onChange(event) { + this.setState({ + selectedIndex: event.nativeEvent.selectedIndex, + }); + }, + + _onValueChange(value) { + this.setState({ + value: value, + }); + } +}); + +var styles = StyleSheet.create({ + text: { + fontSize: 14, + textAlign: 'center', + fontWeight: '500', + margin: 10, + }, +}); + +exports.title = ''; +exports.displayName = 'SegmentedControlExample'; +exports.description = 'Native segmented control'; +exports.examples = [ + { + title: 'Segmented controls can have values', + render(): ReactElement { return ; } + }, + { + title: 'Segmented controls can have a pre-selected value', + render(): ReactElement { return ; } + }, + { + title: 'Segmented controls can be momentary', + render(): ReactElement { return ; } + }, + { + title: 'Segmented controls can be disabled', + render(): ReactElement { return ; } + }, + { + title: 'Custom colors can be provided', + render(): ReactElement { return ; } + }, + { + title: 'Change events can be detected', + render(): ReactElement { return ; } + } +]; diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index 73010617732abd..a10d291bdb54d2 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -43,6 +43,7 @@ var COMPONENTS = [ require('./NavigatorIOSExample'), require('./PickerIOSExample'), require('./ScrollViewExample'), + require('./SegmentedControlIOSExample'), require('./SliderIOSExample'), require('./SwitchIOSExample'), require('./TabBarIOSExample'), diff --git a/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js new file mode 100644 index 00000000000000..23d952776bb96e --- /dev/null +++ b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule SegmentedControlIOS + * @flow + */ +'use strict'; + +var NativeMethodsMixin = require('NativeMethodsMixin'); +var NativeModules = require('NativeModules'); +var PropTypes = require('ReactPropTypes'); +var React = require('React'); +var StyleSheet = require('StyleSheet'); + +var requireNativeComponent = require('requireNativeComponent'); +var verifyPropTypes = require('verifyPropTypes'); + +type DefaultProps = { + values: Array; + enabled: boolean; +}; + +var SEGMENTED_CONTROL_REFERENCE = 'segmentedcontrol'; + +type Event = Object; + +/** + * Use `SegmentedControlIOS` to render a UISegmentedControl iOS. + */ +var SegmentedControlIOS = React.createClass({ + mixins: [NativeMethodsMixin], + + propTypes: { + /** + * The labels for the control's segment buttons, in order. + */ + values: PropTypes.arrayOf(PropTypes.string), + + /** + * The index in `props.values` of the segment to be pre-selected + */ + selectedIndex: PropTypes.number, + + /** + * Callback that is called when the user taps a segment; + * passes the segment's value as an argument + */ + onValueChange: PropTypes.func, + + /** + * Callback that is called when the user taps a segment; + * passes the event as an argument + */ + onChange: PropTypes.func, + + /** + * If false the user won't be able to interact with the control. + * Default value is true. + */ + enabled: PropTypes.bool, + + /** + * Accent color of the control. + */ + tintColor: PropTypes.string, + + /** + * If true, then selecting a segment won't persist visually. + * The `onValueChange` callback will still work as expected. + */ + momentary: PropTypes.bool + }, + + getDefaultProps: function(): DefaultProps { + return { + values: [], + enabled: true + }; + }, + + _onChange: function(event: Event) { + this.props.onChange && this.props.onChange(event); + this.props.onValueChange && this.props.onValueChange(event.nativeEvent.value); + }, + + render: function() { + return ( + + ); + } +}); + +var styles = StyleSheet.create({ + segmentedControl: { + height: NativeModules.SegmentedControlManager.ComponentHeight + }, +}); + +var RCTSegmentedControl = requireNativeComponent( + 'RCTSegmentedControl', + null +); +if (__DEV__) { + verifyPropTypes( + RCTSegmentedControl, + RCTSegmentedControl.viewConfig + ); +} + +module.exports = SegmentedControlIOS; diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index 01bff7eae5dfb0..b94b172f37de61 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -27,6 +27,7 @@ var ReactNative = Object.assign(Object.create(require('React')), { NavigatorIOS: require('NavigatorIOS'), PickerIOS: require('PickerIOS'), Navigator: require('Navigator'), + SegmentedControlIOS: require('SegmentedControlIOS'), ScrollView: require('ScrollView'), SliderIOS: require('SliderIOS'), SwitchIOS: require('SwitchIOS'), diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index fce2aae428aa55..10867e9cbc5aa9 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; }; 00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */; }; + 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */; }; + 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */; }; 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; }; 13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */; }; 134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; }; @@ -84,6 +86,10 @@ 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = ""; }; 00C1A2B11AC0B7E000E89A1C /* RCTDevMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDevMenu.h; sourceTree = ""; }; 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDevMenu.m; sourceTree = ""; }; + 131B6AF01AF1093D00FFC3E0 /* RCTSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControl.h; sourceTree = ""; }; + 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControl.m; sourceTree = ""; }; + 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControlManager.h; sourceTree = ""; }; + 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControlManager.m; sourceTree = ""; }; 13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; }; 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; }; 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; }; @@ -290,6 +296,10 @@ 58114A141AAE854800E7D092 /* RCTPickerManager.h */, 58114A151AAE854800E7D092 /* RCTPickerManager.m */, 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */, + 131B6AF01AF1093D00FFC3E0 /* RCTSegmentedControl.h */, + 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */, + 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */, + 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */, 13B07FF61A6947C200A75B9A /* RCTScrollView.h */, 13B07FF71A6947C200A75B9A /* RCTScrollView.m */, 13B07FF81A6947C200A75B9A /* RCTScrollViewManager.h */, @@ -489,6 +499,7 @@ 13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */, 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */, 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */, + 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */, 830A229E1A66C68A008503DA /* RCTRootView.m in Sources */, 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */, 83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */, @@ -529,6 +540,7 @@ 58114A161AAE854800E7D092 /* RCTPicker.m in Sources */, 137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */, 13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */, + 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */, 58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */, 13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */, 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */, diff --git a/React/Views/RCTSegmentedControl.h b/React/Views/RCTSegmentedControl.h new file mode 100644 index 00000000000000..8e6e1255ef0492 --- /dev/null +++ b/React/Views/RCTSegmentedControl.h @@ -0,0 +1,20 @@ +// +// RCTSegmentedControl.h +// React +// +// Created by Clay Allsopp on 3/31/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +@class RCTEventDispatcher; + +@interface RCTSegmentedControl : UISegmentedControl + +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, copy) NSArray *values; +@property (nonatomic, assign) NSInteger selectedIndex; + +@end diff --git a/React/Views/RCTSegmentedControl.m b/React/Views/RCTSegmentedControl.m new file mode 100644 index 00000000000000..59e4cfb86b5fa4 --- /dev/null +++ b/React/Views/RCTSegmentedControl.m @@ -0,0 +1,57 @@ +// +// RCTSegmentedControl.m +// React +// +// Created by Clay Allsopp on 3/31/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "RCTSegmentedControl.h" + +#import "RCTConvert.h" +#import "RCTEventDispatcher.h" +#import "UIView+React.h" + +@implementation RCTSegmentedControl +{ + RCTEventDispatcher *_eventDispatcher; +} + +- (id)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +{ + if ((self = [super initWithFrame:CGRectZero])) { + _eventDispatcher = eventDispatcher; + _selectedIndex = self.selectedSegmentIndex; + [self addTarget:self action:@selector(onChange:) + forControlEvents:UIControlEventValueChanged]; + } + return self; +} + +- (void)setValues:(NSArray *)values +{ + _values = [values copy]; + [self removeAllSegments]; + for (NSString *value in values) { + [self insertSegmentWithTitle:value atIndex:self.numberOfSegments animated:NO]; + } + super.selectedSegmentIndex = _selectedIndex; +} + +- (void)setSelectedIndex:(NSInteger)selectedIndex +{ + _selectedIndex = selectedIndex; + super.selectedSegmentIndex = selectedIndex; +} + +- (void)onChange:(UISegmentedControl *)sender +{ + NSDictionary *event = @{ + @"target": self.reactTag, + @"value": [self titleForSegmentAtIndex:sender.selectedSegmentIndex], + @"selectedSegmentIndex": @(sender.selectedSegmentIndex) + }; + [_eventDispatcher sendInputEventWithName:@"topChange" body:event]; +} + +@end diff --git a/React/Views/RCTSegmentedControlManager.h b/React/Views/RCTSegmentedControlManager.h new file mode 100644 index 00000000000000..03647c72edb9bf --- /dev/null +++ b/React/Views/RCTSegmentedControlManager.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTViewManager.h" + +@interface RCTSegmentedControlManager : RCTViewManager + +@end diff --git a/React/Views/RCTSegmentedControlManager.m b/React/Views/RCTSegmentedControlManager.m new file mode 100644 index 00000000000000..d7e1156ff00725 --- /dev/null +++ b/React/Views/RCTSegmentedControlManager.m @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTSegmentedControlManager.h" + +#import "RCTBridge.h" +#import "RCTConvert.h" +#import "RCTSegmentedControl.h" + +@implementation RCTSegmentedControlManager + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + return [[RCTSegmentedControl alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; +} + +RCT_EXPORT_VIEW_PROPERTY(values, NSStringArray) +RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(momentary, BOOL) +RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL) + +- (NSDictionary *)constantsToExport +{ + RCTSegmentedControl *view = [[RCTSegmentedControl alloc] init]; + return @{ + @"ComponentHeight": @(view.intrinsicContentSize.height), + }; +} + +@end From 1cff06dca3b7bfe4311fb83bdba992f9eeb25853 Mon Sep 17 00:00:00 2001 From: James Ide Date: Wed, 29 Apr 2015 08:21:44 -0700 Subject: [PATCH 141/250] [podspec] Include podspec in npm distribution Summary: The way RCT_EXPORT_MODULE currently works, any module that is included as a pod also needs react itself to be included as a pod. npm seems to be the preferred way to get the latest copy of react (compared to github directly or the cocoapods repo) so what this diff enables is including react as a pod if it was installed via npm. Closes https://github.com/facebook/react-native/pull/1057 Github Author: James Ide Test Plan: Imported from GitHub, without a `Test Plan:` line. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 5b7bedeaabafe5..40a3e9b517d0a7 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "main": "Libraries/react-native/react-native.js", "files": [ "React", + "React.podspec", "Examples/SampleApp", "Libraries", "packager", From 96850b7429503ec831f0d170da61ff13e22b7f34 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Wed, 29 Apr 2015 11:54:26 -0700 Subject: [PATCH 142/250] [react_native] JS files from D2027955: [react_native] Add bridge 'spy mode' to watch bridge traffic --- Libraries/Utilities/MessageQueue.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Libraries/Utilities/MessageQueue.js b/Libraries/Utilities/MessageQueue.js index 576aaa9917bba2..97658c2356b3d6 100644 --- a/Libraries/Utilities/MessageQueue.js +++ b/Libraries/Utilities/MessageQueue.js @@ -21,6 +21,9 @@ var JSTimersExecution = require('JSTimersExecution'); var INTERNAL_ERROR = 'Error in MessageQueue implementation'; +// Prints all bridge traffic to console.log +var DEBUG_SPY_MODE = false; + type ModulesConfig = { [key:string]: { moduleID: number; @@ -263,6 +266,9 @@ var MessageQueueMixin = { 'both the success callback and the error callback.', cbID ); + if (DEBUG_SPY_MODE) { + console.log('N->JS: Callback#' + cbID + '(' + JSON.stringify(args) + ')'); + } cb.apply(scope, args); } catch(ie_requires_catch) { throw ie_requires_catch; @@ -292,6 +298,11 @@ var MessageQueueMixin = { var moduleName = this._localModuleIDToModuleName[moduleID]; var methodName = this._localModuleNameToMethodIDToName[moduleName][methodID]; + if (DEBUG_SPY_MODE) { + console.log( + 'N->JS: ' + moduleName + '.' + methodName + + '(' + JSON.stringify(params) + ')'); + } var ret = jsCall(this._requireFunc(moduleName), methodName, params); return ret; @@ -460,6 +471,17 @@ var MessageQueueMixin = { var ret = currentOutgoingItems[REQUEST_MODULE_IDS].length || currentOutgoingItems[RESPONSE_RETURN_VALUES].length ? currentOutgoingItems : null; + if (DEBUG_SPY_MODE && ret) { + for (var i = 0; i < currentOutgoingItems[0].length; i++) { + var moduleName = this._remoteModuleIDToModuleName[currentOutgoingItems[0][i]]; + var methodName = + this._remoteModuleNameToMethodIDToName[moduleName][currentOutgoingItems[1][i]]; + console.log( + 'JS->N: ' + moduleName + '.' + methodName + + '(' + JSON.stringify(currentOutgoingItems[2][i]) + ')'); + } + } + return ret; }, From 483077d4daa23dd4fd6109c801a4ea3169366587 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Wed, 29 Apr 2015 12:29:04 -0700 Subject: [PATCH 143/250] [ReactNative] Don't log invalid utf8 strings --- React/Base/RCTUtils.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 0b4a3873c8c0cc..3c644dbff4e788 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -35,8 +35,7 @@ id RCTJSONParse(NSString *jsonString, NSError **error) if (jsonData) { RCTLogWarn(@"RCTJSONParse received the following string, which could not be losslessly converted to UTF8 data: '%@'", jsonString); } else { - // If our backup conversion fails, log the issue so we can see what strings are causing this (t6452813) - RCTLogError(@"RCTJSONParse received the following string, which could not be converted to UTF8 data: '%@'", jsonString); + RCTLogError(@"RCTJSONParse received invalid UTF8 data"); return nil; } } From 8efe148f0731189aed10e2828dc6cffee94c6397 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Wed, 29 Apr 2015 15:41:17 -0700 Subject: [PATCH 144/250] Fix bullet spacing for website. GitHub markdown rendered it fine, but it was all mashed inline on the website without the extra break. --- docs/NativeComponentsIOS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/NativeComponentsIOS.md b/docs/NativeComponentsIOS.md index 14a186fe4829fa..3dae09528a40a1 100644 --- a/docs/NativeComponentsIOS.md +++ b/docs/NativeComponentsIOS.md @@ -18,6 +18,7 @@ Let's say we want to add an interactive Map to our app - might as well use [`MKM Native views are created and manipulated by subclasses of `RCTViewManager`. These subclasses are similar in function to view controllers, but are essentially singletons - only one instance of each is created by the bridge. They vend native views to the `RCTUIManager`, which delegates back to them to set and update the properties of the views as necessary. The `RCTViewManager`s are also typically the delegates for the views, sending events back to JavaScript via the bridge. Vending a view is simple: + - Create the basic subclass. - Add the `RCT_EXPORT_MODULE()` marker macro. - Implement the `-(UIView *)view` method From f89bcacd86a7d3e85aadca35f62e10bcb8346c1f Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Wed, 29 Apr 2015 15:42:30 -0700 Subject: [PATCH 145/250] Add missing 'and' in docs. --- docs/NativeComponentsIOS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/NativeComponentsIOS.md b/docs/NativeComponentsIOS.md index 3dae09528a40a1..fa467a0cee4c92 100644 --- a/docs/NativeComponentsIOS.md +++ b/docs/NativeComponentsIOS.md @@ -58,7 +58,7 @@ This is now a fully-functioning native map view component in JavaScript, complet ## Properties -The first thing we can do to make this component more usable is to bridge over some native properties. Let's say we want to be able to disable pitch control specify the visible region. Disabling pitch is a simple boolean, so we just add this one line: +The first thing we can do to make this component more usable is to bridge over some native properties. Let's say we want to be able to disable pitch control and specify the visible region. Disabling pitch is a simple boolean, so we just add this one line: ```objective-c // RCTMapManager.m From c6af33e796019bfd571d16fa712af9cd122575fd Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 29 Apr 2015 15:28:18 -0700 Subject: [PATCH 146/250] [react-native] Open-source ReactART for native --- Libraries/ART/ART.xcodeproj/project.pbxproj | 371 +++++++++++ Libraries/ART/ARTCGFloatArray.h | 20 + Libraries/ART/ARTContainer.h | 18 + Libraries/ART/ARTGroup.h | 17 + Libraries/ART/ARTGroup.m | 23 + Libraries/ART/ARTNode.h | 33 + Libraries/ART/ARTNode.m | 76 +++ Libraries/ART/ARTRenderable.h | 25 + Libraries/ART/ARTRenderable.m | 89 +++ Libraries/ART/ARTSerializablePath.js | 77 +++ Libraries/ART/ARTShape.h | 18 + Libraries/ART/ARTShape.m | 68 ++ Libraries/ART/ARTSurfaceView.h | 16 + Libraries/ART/ARTSurfaceView.m | 30 + Libraries/ART/ARTText.h | 20 + Libraries/ART/ARTText.m | 127 ++++ Libraries/ART/ARTTextFrame.h | 25 + Libraries/ART/Brushes/ARTBrush.h | 35 + Libraries/ART/Brushes/ARTBrush.m | 29 + Libraries/ART/Brushes/ARTLinearGradient.h | 14 + Libraries/ART/Brushes/ARTLinearGradient.m | 49 ++ Libraries/ART/Brushes/ARTPattern.h | 14 + Libraries/ART/Brushes/ARTPattern.m | 50 ++ Libraries/ART/Brushes/ARTRadialGradient.h | 14 + Libraries/ART/Brushes/ARTRadialGradient.m | 56 ++ Libraries/ART/Brushes/ARTSolidColor.h | 14 + Libraries/ART/Brushes/ARTSolidColor.m | 39 ++ Libraries/ART/RCTConvert+ART.h | 31 + Libraries/ART/RCTConvert+ART.m | 234 +++++++ Libraries/ART/ReactIOSART.js | 611 ++++++++++++++++++ Libraries/ART/ViewManagers/ARTGroupManager.h | 14 + Libraries/ART/ViewManagers/ARTGroupManager.m | 23 + Libraries/ART/ViewManagers/ARTNodeManager.h | 17 + Libraries/ART/ViewManagers/ARTNodeManager.m | 36 ++ .../ART/ViewManagers/ARTRenderableManager.h | 17 + .../ART/ViewManagers/ARTRenderableManager.m | 30 + Libraries/ART/ViewManagers/ARTShapeManager.h | 14 + Libraries/ART/ViewManagers/ARTShapeManager.m | 26 + .../ART/ViewManagers/ARTSurfaceViewManager.h | 14 + .../ART/ViewManagers/ARTSurfaceViewManager.m | 23 + Libraries/ART/ViewManagers/ARTTextManager.h | 14 + Libraries/ART/ViewManagers/ARTTextManager.m | 27 + 42 files changed, 2498 insertions(+) create mode 100644 Libraries/ART/ART.xcodeproj/project.pbxproj create mode 100644 Libraries/ART/ARTCGFloatArray.h create mode 100644 Libraries/ART/ARTContainer.h create mode 100644 Libraries/ART/ARTGroup.h create mode 100644 Libraries/ART/ARTGroup.m create mode 100644 Libraries/ART/ARTNode.h create mode 100644 Libraries/ART/ARTNode.m create mode 100644 Libraries/ART/ARTRenderable.h create mode 100644 Libraries/ART/ARTRenderable.m create mode 100644 Libraries/ART/ARTSerializablePath.js create mode 100644 Libraries/ART/ARTShape.h create mode 100644 Libraries/ART/ARTShape.m create mode 100644 Libraries/ART/ARTSurfaceView.h create mode 100644 Libraries/ART/ARTSurfaceView.m create mode 100644 Libraries/ART/ARTText.h create mode 100644 Libraries/ART/ARTText.m create mode 100644 Libraries/ART/ARTTextFrame.h create mode 100644 Libraries/ART/Brushes/ARTBrush.h create mode 100644 Libraries/ART/Brushes/ARTBrush.m create mode 100644 Libraries/ART/Brushes/ARTLinearGradient.h create mode 100644 Libraries/ART/Brushes/ARTLinearGradient.m create mode 100644 Libraries/ART/Brushes/ARTPattern.h create mode 100644 Libraries/ART/Brushes/ARTPattern.m create mode 100644 Libraries/ART/Brushes/ARTRadialGradient.h create mode 100644 Libraries/ART/Brushes/ARTRadialGradient.m create mode 100644 Libraries/ART/Brushes/ARTSolidColor.h create mode 100644 Libraries/ART/Brushes/ARTSolidColor.m create mode 100644 Libraries/ART/RCTConvert+ART.h create mode 100644 Libraries/ART/RCTConvert+ART.m create mode 100644 Libraries/ART/ReactIOSART.js create mode 100644 Libraries/ART/ViewManagers/ARTGroupManager.h create mode 100644 Libraries/ART/ViewManagers/ARTGroupManager.m create mode 100644 Libraries/ART/ViewManagers/ARTNodeManager.h create mode 100644 Libraries/ART/ViewManagers/ARTNodeManager.m create mode 100644 Libraries/ART/ViewManagers/ARTRenderableManager.h create mode 100644 Libraries/ART/ViewManagers/ARTRenderableManager.m create mode 100644 Libraries/ART/ViewManagers/ARTShapeManager.h create mode 100644 Libraries/ART/ViewManagers/ARTShapeManager.m create mode 100644 Libraries/ART/ViewManagers/ARTSurfaceViewManager.h create mode 100644 Libraries/ART/ViewManagers/ARTSurfaceViewManager.m create mode 100644 Libraries/ART/ViewManagers/ARTTextManager.h create mode 100644 Libraries/ART/ViewManagers/ARTTextManager.m diff --git a/Libraries/ART/ART.xcodeproj/project.pbxproj b/Libraries/ART/ART.xcodeproj/project.pbxproj new file mode 100644 index 00000000000000..c3255c9656a9da --- /dev/null +++ b/Libraries/ART/ART.xcodeproj/project.pbxproj @@ -0,0 +1,371 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0CF68B051AF0549300FF9E5C /* ARTGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */; }; + 0CF68B061AF0549300FF9E5C /* ARTNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE01AF0549300FF9E5C /* ARTNode.m */; }; + 0CF68B071AF0549300FF9E5C /* ARTRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */; }; + 0CF68B081AF0549300FF9E5C /* ARTShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE41AF0549300FF9E5C /* ARTShape.m */; }; + 0CF68B091AF0549300FF9E5C /* ARTSurfaceView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */; }; + 0CF68B0A1AF0549300FF9E5C /* ARTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE81AF0549300FF9E5C /* ARTText.m */; }; + 0CF68B0B1AF0549300FF9E5C /* ARTBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */; }; + 0CF68B0C1AF0549300FF9E5C /* ARTLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */; }; + 0CF68B0D1AF0549300FF9E5C /* ARTPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */; }; + 0CF68B0E1AF0549300FF9E5C /* ARTRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */; }; + 0CF68B0F1AF0549300FF9E5C /* ARTSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */; }; + 0CF68B101AF0549300FF9E5C /* RCTConvert+ART.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */; }; + 0CF68B111AF0549300FF9E5C /* ARTGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */; }; + 0CF68B121AF0549300FF9E5C /* ARTNodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */; }; + 0CF68B131AF0549300FF9E5C /* ARTRenderableManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */; }; + 0CF68B141AF0549300FF9E5C /* ARTShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */; }; + 0CF68B151AF0549300FF9E5C /* ARTSurfaceViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */; }; + 0CF68B161AF0549300FF9E5C /* ARTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 0CF68ABF1AF0540F00FF9E5C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0CF68AC11AF0540F00FF9E5C /* libART.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libART.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTCGFloatArray.h; sourceTree = ""; }; + 0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTContainer.h; sourceTree = ""; }; + 0CF68ADD1AF0549300FF9E5C /* ARTGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTGroup.h; sourceTree = ""; }; + 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTGroup.m; sourceTree = ""; }; + 0CF68ADF1AF0549300FF9E5C /* ARTNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTNode.h; sourceTree = ""; }; + 0CF68AE01AF0549300FF9E5C /* ARTNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTNode.m; sourceTree = ""; }; + 0CF68AE11AF0549300FF9E5C /* ARTRenderable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRenderable.h; sourceTree = ""; }; + 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRenderable.m; sourceTree = ""; }; + 0CF68AE31AF0549300FF9E5C /* ARTShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTShape.h; sourceTree = ""; }; + 0CF68AE41AF0549300FF9E5C /* ARTShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTShape.m; sourceTree = ""; }; + 0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSurfaceView.h; sourceTree = ""; }; + 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSurfaceView.m; sourceTree = ""; }; + 0CF68AE71AF0549300FF9E5C /* ARTText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTText.h; sourceTree = ""; }; + 0CF68AE81AF0549300FF9E5C /* ARTText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTText.m; sourceTree = ""; }; + 0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTextFrame.h; sourceTree = ""; }; + 0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTBrush.h; sourceTree = ""; }; + 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTBrush.m; sourceTree = ""; }; + 0CF68AED1AF0549300FF9E5C /* ARTLinearGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTLinearGradient.h; sourceTree = ""; }; + 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTLinearGradient.m; sourceTree = ""; }; + 0CF68AEF1AF0549300FF9E5C /* ARTPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPattern.h; sourceTree = ""; }; + 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPattern.m; sourceTree = ""; }; + 0CF68AF11AF0549300FF9E5C /* ARTRadialGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRadialGradient.h; sourceTree = ""; }; + 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRadialGradient.m; sourceTree = ""; }; + 0CF68AF31AF0549300FF9E5C /* ARTSolidColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSolidColor.h; sourceTree = ""; }; + 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSolidColor.m; sourceTree = ""; }; + 0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+ART.h"; sourceTree = ""; }; + 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+ART.m"; sourceTree = ""; }; + 0CF68AF91AF0549300FF9E5C /* ARTGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTGroupManager.h; sourceTree = ""; }; + 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTGroupManager.m; sourceTree = ""; }; + 0CF68AFB1AF0549300FF9E5C /* ARTNodeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTNodeManager.h; sourceTree = ""; }; + 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTNodeManager.m; sourceTree = ""; }; + 0CF68AFD1AF0549300FF9E5C /* ARTRenderableManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRenderableManager.h; sourceTree = ""; }; + 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRenderableManager.m; sourceTree = ""; }; + 0CF68AFF1AF0549300FF9E5C /* ARTShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTShapeManager.h; sourceTree = ""; }; + 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTShapeManager.m; sourceTree = ""; }; + 0CF68B011AF0549300FF9E5C /* ARTSurfaceViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSurfaceViewManager.h; sourceTree = ""; }; + 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSurfaceViewManager.m; sourceTree = ""; }; + 0CF68B031AF0549300FF9E5C /* ARTTextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTextManager.h; sourceTree = ""; }; + 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTTextManager.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0CF68ABE1AF0540F00FF9E5C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0CF68AB81AF0540F00FF9E5C = { + isa = PBXGroup; + children = ( + 0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */, + 0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */, + 0CF68ADD1AF0549300FF9E5C /* ARTGroup.h */, + 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */, + 0CF68ADF1AF0549300FF9E5C /* ARTNode.h */, + 0CF68AE01AF0549300FF9E5C /* ARTNode.m */, + 0CF68AE11AF0549300FF9E5C /* ARTRenderable.h */, + 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */, + 0CF68AE31AF0549300FF9E5C /* ARTShape.h */, + 0CF68AE41AF0549300FF9E5C /* ARTShape.m */, + 0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */, + 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */, + 0CF68AE71AF0549300FF9E5C /* ARTText.h */, + 0CF68AE81AF0549300FF9E5C /* ARTText.m */, + 0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */, + 0CF68AEA1AF0549300FF9E5C /* Brushes */, + 0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */, + 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */, + 0CF68AF81AF0549300FF9E5C /* ViewManagers */, + 0CF68AC21AF0540F00FF9E5C /* Products */, + ); + sourceTree = ""; + }; + 0CF68AC21AF0540F00FF9E5C /* Products */ = { + isa = PBXGroup; + children = ( + 0CF68AC11AF0540F00FF9E5C /* libART.a */, + ); + name = Products; + sourceTree = ""; + }; + 0CF68AEA1AF0549300FF9E5C /* Brushes */ = { + isa = PBXGroup; + children = ( + 0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */, + 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */, + 0CF68AED1AF0549300FF9E5C /* ARTLinearGradient.h */, + 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */, + 0CF68AEF1AF0549300FF9E5C /* ARTPattern.h */, + 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */, + 0CF68AF11AF0549300FF9E5C /* ARTRadialGradient.h */, + 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */, + 0CF68AF31AF0549300FF9E5C /* ARTSolidColor.h */, + 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */, + ); + path = Brushes; + sourceTree = ""; + }; + 0CF68AF81AF0549300FF9E5C /* ViewManagers */ = { + isa = PBXGroup; + children = ( + 0CF68AF91AF0549300FF9E5C /* ARTGroupManager.h */, + 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */, + 0CF68AFB1AF0549300FF9E5C /* ARTNodeManager.h */, + 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */, + 0CF68AFD1AF0549300FF9E5C /* ARTRenderableManager.h */, + 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */, + 0CF68AFF1AF0549300FF9E5C /* ARTShapeManager.h */, + 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */, + 0CF68B011AF0549300FF9E5C /* ARTSurfaceViewManager.h */, + 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */, + 0CF68B031AF0549300FF9E5C /* ARTTextManager.h */, + 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */, + ); + path = ViewManagers; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0CF68AC01AF0540F00FF9E5C /* ART */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0CF68AD51AF0540F00FF9E5C /* Build configuration list for PBXNativeTarget "ART" */; + buildPhases = ( + 0CF68ABD1AF0540F00FF9E5C /* Sources */, + 0CF68ABE1AF0540F00FF9E5C /* Frameworks */, + 0CF68ABF1AF0540F00FF9E5C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ART; + productName = ART; + productReference = 0CF68AC11AF0540F00FF9E5C /* libART.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0CF68AB91AF0540F00FF9E5C /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0620; + TargetAttributes = { + 0CF68AC01AF0540F00FF9E5C = { + CreatedOnToolsVersion = 6.2; + }; + }; + }; + buildConfigurationList = 0CF68ABC1AF0540F00FF9E5C /* Build configuration list for PBXProject "ART" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 0CF68AB81AF0540F00FF9E5C; + productRefGroup = 0CF68AC21AF0540F00FF9E5C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0CF68AC01AF0540F00FF9E5C /* ART */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0CF68ABD1AF0540F00FF9E5C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CF68B161AF0549300FF9E5C /* ARTTextManager.m in Sources */, + 0CF68B111AF0549300FF9E5C /* ARTGroupManager.m in Sources */, + 0CF68B0D1AF0549300FF9E5C /* ARTPattern.m in Sources */, + 0CF68B0A1AF0549300FF9E5C /* ARTText.m in Sources */, + 0CF68B121AF0549300FF9E5C /* ARTNodeManager.m in Sources */, + 0CF68B051AF0549300FF9E5C /* ARTGroup.m in Sources */, + 0CF68B131AF0549300FF9E5C /* ARTRenderableManager.m in Sources */, + 0CF68B091AF0549300FF9E5C /* ARTSurfaceView.m in Sources */, + 0CF68B0E1AF0549300FF9E5C /* ARTRadialGradient.m in Sources */, + 0CF68B151AF0549300FF9E5C /* ARTSurfaceViewManager.m in Sources */, + 0CF68B081AF0549300FF9E5C /* ARTShape.m in Sources */, + 0CF68B071AF0549300FF9E5C /* ARTRenderable.m in Sources */, + 0CF68B101AF0549300FF9E5C /* RCTConvert+ART.m in Sources */, + 0CF68B061AF0549300FF9E5C /* ARTNode.m in Sources */, + 0CF68B0F1AF0549300FF9E5C /* ARTSolidColor.m in Sources */, + 0CF68B0C1AF0549300FF9E5C /* ARTLinearGradient.m in Sources */, + 0CF68B0B1AF0549300FF9E5C /* ARTBrush.m in Sources */, + 0CF68B141AF0549300FF9E5C /* ARTShapeManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0CF68AD31AF0540F00FF9E5C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 0CF68AD41AF0540F00FF9E5C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 0CF68AD61AF0540F00FF9E5C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 0CF68AD71AF0540F00FF9E5C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0CF68ABC1AF0540F00FF9E5C /* Build configuration list for PBXProject "ART" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0CF68AD31AF0540F00FF9E5C /* Debug */, + 0CF68AD41AF0540F00FF9E5C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0CF68AD51AF0540F00FF9E5C /* Build configuration list for PBXNativeTarget "ART" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0CF68AD61AF0540F00FF9E5C /* Debug */, + 0CF68AD71AF0540F00FF9E5C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0CF68AB91AF0540F00FF9E5C /* Project object */; +} diff --git a/Libraries/ART/ARTCGFloatArray.h b/Libraries/ART/ARTCGFloatArray.h new file mode 100644 index 00000000000000..9d748549973d30 --- /dev/null +++ b/Libraries/ART/ARTCGFloatArray.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +// A little helper to make sure we have the right memory allocation ready for use. +// We assume that we will only this in one place so no reference counting is necessary. +// Needs to be freed when dealloced. + +// This is fragile since this relies on these values not getting reused. Consider +// wrapping these in an Obj-C class or some ARC hackery to get refcounting. + +typedef struct { + size_t count; + CGFloat *array; +} ARTCGFloatArray; diff --git a/Libraries/ART/ARTContainer.h b/Libraries/ART/ARTContainer.h new file mode 100644 index 00000000000000..d83f7ae1a559f8 --- /dev/null +++ b/Libraries/ART/ARTContainer.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@protocol ARTContainer + +// This is used as a hook for child to mark it's parent as dirty. +// This bubbles up to the root which gets marked as dirty. +- (void)invalidate; + +@end diff --git a/Libraries/ART/ARTGroup.h b/Libraries/ART/ARTGroup.h new file mode 100644 index 00000000000000..15a8b643bb86fb --- /dev/null +++ b/Libraries/ART/ARTGroup.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "ARTContainer.h" +#import "ARTNode.h" + +@interface ARTGroup : ARTNode + +@end diff --git a/Libraries/ART/ARTGroup.m b/Libraries/ART/ARTGroup.m new file mode 100644 index 00000000000000..9ecbf8ee821b84 --- /dev/null +++ b/Libraries/ART/ARTGroup.m @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTGroup.h" + +@implementation ARTGroup + +- (void)renderLayerTo:(CGContextRef)context +{ +// TO-DO: Clipping rectangle + + for (ARTNode *node in self.subviews) { + [node renderTo:context]; + } +} + +@end diff --git a/Libraries/ART/ARTNode.h b/Libraries/ART/ARTNode.h new file mode 100644 index 00000000000000..511c09a5a6aae2 --- /dev/null +++ b/Libraries/ART/ARTNode.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +/** + * ART nodes are implemented as empty UIViews but this is just an implementation detail to fit + * into the existing view management. They should also be shadow views and painted on a background + * thread. + */ + +@interface ARTNode : UIView + +@property (nonatomic, assign) CGFloat opacity; + +- (void)invalidate; +- (void)renderTo:(CGContextRef)context; + +/** + * renderTo will take opacity into account and draw renderLayerTo off-screen if there is opacity + * specified, then composite that onto the context. renderLayerTo always draws at opacity=1. + * @abstract + */ +- (void)renderLayerTo:(CGContextRef)context; + +@end diff --git a/Libraries/ART/ARTNode.m b/Libraries/ART/ARTNode.m new file mode 100644 index 00000000000000..d23d5880a95677 --- /dev/null +++ b/Libraries/ART/ARTNode.m @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTNode.h" + +#import "ARTContainer.h" + +@implementation ARTNode + +- (void)insertSubview:(UIView *)subview atIndex:(NSInteger)index +{ + [self invalidate]; + [super insertSubview:subview atIndex:index]; +} + +- (void)removeFromSuperview +{ + [self invalidate]; + [super removeFromSuperview]; +} + +- (void)setOpacity:(CGFloat)opacity +{ + [self invalidate]; + _opacity = opacity; +} + +- (void)setTransform:(CGAffineTransform)transform +{ + [self invalidate]; + super.transform = transform; +} + +- (void)invalidate +{ + id container = (id)self.superview; + [container invalidate]; +} + +- (void)renderTo:(CGContextRef)context +{ + if (self.opacity <= 0) { + // Nothing to paint + return; + } + if (self.opacity >= 1) { + // Just paint at full opacity + CGContextSaveGState(context); + CGContextConcatCTM(context, self.transform); + CGContextSetAlpha(context, 1); + [self renderLayerTo:context]; + CGContextRestoreGState(context); + return; + } + // This needs to be painted on a layer before being composited. + CGContextSaveGState(context); + CGContextConcatCTM(context, self.transform); + CGContextSetAlpha(context, self.opacity); + CGContextBeginTransparencyLayer(context, NULL); + [self renderLayerTo:context]; + CGContextEndTransparencyLayer(context); + CGContextRestoreGState(context); +} + +- (void)renderLayerTo:(CGContextRef)context +{ + // abstract +} + +@end diff --git a/Libraries/ART/ARTRenderable.h b/Libraries/ART/ARTRenderable.h new file mode 100644 index 00000000000000..8eae9c25ae4a70 --- /dev/null +++ b/Libraries/ART/ARTRenderable.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "ARTBrush.h" +#import "ARTCGFloatArray.h" +#import "ARTNode.h" + +@interface ARTRenderable : ARTNode + +@property (nonatomic, strong) ARTBrush *fill; +@property (nonatomic, assign) CGColorRef stroke; +@property (nonatomic, assign) CGFloat strokeWidth; +@property (nonatomic, assign) CGLineCap strokeCap; +@property (nonatomic, assign) CGLineJoin strokeJoin; +@property (nonatomic, assign) ARTCGFloatArray strokeDash; + +@end diff --git a/Libraries/ART/ARTRenderable.m b/Libraries/ART/ARTRenderable.m new file mode 100644 index 00000000000000..7ba9a9a6073915 --- /dev/null +++ b/Libraries/ART/ARTRenderable.m @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTRenderable.h" + +@implementation ARTRenderable + +- (void)setFill:(ARTBrush *)fill +{ + [self invalidate]; + _fill = fill; +} + +- (void)setStroke:(CGColorRef)stroke +{ + if (stroke == _stroke) { + return; + } + [self invalidate]; + CGColorRelease(_stroke); + _stroke = CGColorRetain(stroke); +} + +- (void)setStrokeWidth:(CGFloat)strokeWidth +{ + [self invalidate]; + _strokeWidth = strokeWidth; +} + +- (void)setStrokeCap:(CGLineCap)strokeCap +{ + [self invalidate]; + _strokeCap = strokeCap; +} + +- (void)setStrokeJoin:(CGLineJoin)strokeJoin +{ + [self invalidate]; + _strokeJoin = strokeJoin; +} + +- (void)setStrokeDash:(ARTCGFloatArray)strokeDash +{ + if (strokeDash.array == _strokeDash.array) { + return; + } + if (_strokeDash.array) { + free(_strokeDash.array); + } + [self invalidate]; + _strokeDash = strokeDash; +} + +- (void)dealloc +{ + CGColorRelease(_stroke); + if (_strokeDash.array) { + free(_strokeDash.array); + } +} + +- (void)renderTo:(CGContextRef)context +{ + if (self.opacity <= 0 || self.opacity >= 1 || (self.fill && self.stroke)) { + // If we have both fill and stroke, we will need to paint this using normal compositing + [super renderTo: context]; + return; + } + // This is a terminal with only one painting. Therefore we don't need to paint this + // off-screen. We can just composite it straight onto the buffer. + CGContextSaveGState(context); + CGContextConcatCTM(context, self.transform); + CGContextSetAlpha(context, self.opacity); + [self renderLayerTo:context]; + CGContextRestoreGState(context); +} + +- (void)renderLayerTo:(CGContextRef)context +{ + // abstract +} + +@end diff --git a/Libraries/ART/ARTSerializablePath.js b/Libraries/ART/ARTSerializablePath.js new file mode 100644 index 00000000000000..2df8ff6bb83bcd --- /dev/null +++ b/Libraries/ART/ARTSerializablePath.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ARTSerializablePath + */ + +"use strict"; + +// TODO: Move this into an ART mode called "serialized" or something + +var Class = require('art/core/class.js'); +var Path = require('art/core/path.js'); + +var MOVE_TO = 0; +var CLOSE = 1; +var LINE_TO = 2; +var CURVE_TO = 3; +var ARC = 4; + +var SerializablePath = Class(Path, { + + initialize: function(path) { + this.reset(); + if (path instanceof SerializablePath) { + this.path = path.path.slice(0); + } else if (path) { + if (path.applyToPath) { + path.applyToPath(this); + } else { + this.push(path); + } + } + }, + + onReset: function() { + this.path = []; + }, + + onMove: function(sx, sy, x, y) { + this.path.push(MOVE_TO, x, y); + }, + + onLine: function(sx, sy, x, y) { + this.path.push(LINE_TO, x, y); + }, + + onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y) { + this.path.push(CURVE_TO, p1x, p1y, p2x, p2y, x, y); + }, + + _arcToBezier: Path.prototype.onArc, + + onArc: function(sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) { + if (rx !== ry || rotation) { + return this._arcToBezier( + sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation + ); + } + this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1); + }, + + onClose: function() { + this.path.push(CLOSE); + }, + + toJSON: function() { + return this.path; + } + +}); + +module.exports = SerializablePath; diff --git a/Libraries/ART/ARTShape.h b/Libraries/ART/ARTShape.h new file mode 100644 index 00000000000000..7d13c268f6e802 --- /dev/null +++ b/Libraries/ART/ARTShape.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "ARTRenderable.h" + +@interface ARTShape : ARTRenderable + +@property (nonatomic, assign) CGPathRef d; + +@end diff --git a/Libraries/ART/ARTShape.m b/Libraries/ART/ARTShape.m new file mode 100644 index 00000000000000..c07d68e62aa4a3 --- /dev/null +++ b/Libraries/ART/ARTShape.m @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTShape.h" + +@implementation ARTShape + +- (void)setD:(CGPathRef)d +{ + if (d == _d) { + return; + } + [self invalidate]; + CGPathRelease(_d); + _d = CGPathRetain(d); +} + +- (void)dealloc +{ + CGPathRelease(_d); +} + +- (void)renderLayerTo:(CGContextRef)context +{ + if ((!self.fill && !self.stroke) || !self.d) { + return; + } + + CGPathDrawingMode mode = kCGPathStroke; + if (self.fill) { + if ([self.fill applyFillColor:context]) { + mode = kCGPathFill; + } else { + CGContextSaveGState(context); + CGContextAddPath(context, self.d); + CGContextClip(context); + [self.fill paint:context]; + CGContextRestoreGState(context); + if (!self.stroke) { + return; + } + } + } + if (self.stroke) { + CGContextSetStrokeColorWithColor(context, self.stroke); + CGContextSetLineWidth(context, self.strokeWidth); + CGContextSetLineCap(context, self.strokeCap); + CGContextSetLineJoin(context, self.strokeJoin); + ARTCGFloatArray dash = self.strokeDash; + if (dash.count) { + CGContextSetLineDash(context, 0, dash.array, dash.count); + } + if (mode == kCGPathFill) { + mode = kCGPathFillStroke; + } + } + + CGContextAddPath(context, self.d); + CGContextDrawPath(context, mode); +} + +@end diff --git a/Libraries/ART/ARTSurfaceView.h b/Libraries/ART/ARTSurfaceView.h new file mode 100644 index 00000000000000..8be8d95040c146 --- /dev/null +++ b/Libraries/ART/ARTSurfaceView.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "ARTContainer.h" + +@interface ARTSurfaceView : UIView + +@end diff --git a/Libraries/ART/ARTSurfaceView.m b/Libraries/ART/ARTSurfaceView.m new file mode 100644 index 00000000000000..8949e43c349d05 --- /dev/null +++ b/Libraries/ART/ARTSurfaceView.m @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTSurfaceView.h" + +#import "ARTNode.h" +#import "RCTLog.h" + +@implementation ARTSurfaceView + +- (void)invalidate +{ + [self setNeedsDisplay]; +} + +- (void)drawRect:(CGRect)rect +{ + CGContextRef context = UIGraphicsGetCurrentContext(); + for (ARTNode *node in self.subviews) { + [node renderTo:context]; + } +} + +@end diff --git a/Libraries/ART/ARTText.h b/Libraries/ART/ARTText.h new file mode 100644 index 00000000000000..ee976e329a46e8 --- /dev/null +++ b/Libraries/ART/ARTText.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "ARTRenderable.h" +#import "ARTTextFrame.h" + +@interface ARTText : ARTRenderable + +@property (nonatomic, assign) CTTextAlignment alignment; +@property (nonatomic, assign) ARTTextFrame textFrame; + +@end diff --git a/Libraries/ART/ARTText.m b/Libraries/ART/ARTText.m new file mode 100644 index 00000000000000..7c8a570270b7ff --- /dev/null +++ b/Libraries/ART/ARTText.m @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTText.h" + +#import + +@implementation ARTText + +- (void)setAlignment:(CTTextAlignment)alignment +{ + [self invalidate]; + _alignment = alignment; +} + +- (void)setTextFrame:(ARTTextFrame)frame +{ + if (frame.lines != _textFrame.lines && _textFrame.count) { + // We must release each line before overriding the old one + for (int i = 0; i < _textFrame.count; i++) { + CFRelease(_textFrame.lines[0]); + } + free(_textFrame.lines); + free(_textFrame.widths); + } + [self invalidate]; + _textFrame = frame; +} + +- (void)dealloc +{ + if (_textFrame.count) { + // We must release each line before freeing up this struct + for (int i = 0; i < _textFrame.count; i++) { + CFRelease(_textFrame.lines[0]); + } + free(_textFrame.lines); + free(_textFrame.widths); + } +} + +- (void)renderLayerTo:(CGContextRef)context +{ + ARTTextFrame frame = self.textFrame; + + if ((!self.fill && !self.stroke) || !frame.count) { + return; + } + + // to-do: draw along a path + + CGTextDrawingMode mode = kCGTextStroke; + if (self.fill) { + if ([self.fill applyFillColor:context]) { + mode = kCGTextFill; + } else { + + for (int i = 0; i < frame.count; i++) { + CGContextSaveGState(context); + // Inverse the coordinate space since CoreText assumes a bottom-up coordinate space + CGContextScaleCTM(context, 1.0, -1.0); + CGContextSetTextDrawingMode(context, kCGTextClip); + [self renderLineTo:context atIndex:i]; + // Inverse the coordinate space back to the original before filling + CGContextScaleCTM(context, 1.0, -1.0); + [self.fill paint:context]; + // Restore the state so that the next line can be clipped separately + CGContextRestoreGState(context); + } + + if (!self.stroke) { + return; + } + } + } + if (self.stroke) { + CGContextSetStrokeColorWithColor(context, self.stroke); + CGContextSetLineWidth(context, self.strokeWidth); + CGContextSetLineCap(context, self.strokeCap); + CGContextSetLineJoin(context, self.strokeJoin); + ARTCGFloatArray dash = self.strokeDash; + if (dash.count) { + CGContextSetLineDash(context, 0, dash.array, dash.count); + } + if (mode == kCGTextFill) { + mode = kCGTextFillStroke; + } + } + + CGContextSetTextDrawingMode(context, mode); + + // Inverse the coordinate space since CoreText assumes a bottom-up coordinate space + CGContextScaleCTM(context, 1.0, -1.0); + for (int i = 0; i < frame.count; i++) { + [self renderLineTo:context atIndex:i]; + } +} + +- (void)renderLineTo:(CGContextRef)context atIndex:(int)index +{ + ARTTextFrame frame = self.textFrame; + CGFloat shift; + switch (self.alignment) { + case kCTTextAlignmentRight: + shift = frame.widths[index]; + break; + case kCTTextAlignmentCenter: + shift = (frame.widths[index] / 2); + break; + default: + shift = 0; + break; + } + // We should consider snapping this shift to device pixels to improve rendering quality + // when a line has subpixel width. + CGContextSetTextPosition(context, -shift, -frame.baseLine - frame.lineHeight * index); + CTLineRef line = frame.lines[index]; + CTLineDraw(line, context); +} + +@end diff --git a/Libraries/ART/ARTTextFrame.h b/Libraries/ART/ARTTextFrame.h new file mode 100644 index 00000000000000..1f6b557bfc77af --- /dev/null +++ b/Libraries/ART/ARTTextFrame.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +// A little helper to make sure we have a set of lines including width ready for use. +// We assume that we will only this in one place so no reference counting is necessary. +// Needs to be freed when dealloced. + +// This is fragile since this relies on these values not getting reused. Consider +// wrapping these in an Obj-C class or some ARC hackery to get refcounting. + +typedef struct { + size_t count; + CGFloat baseLine; // Distance from the origin to the base line of the first line + CGFloat lineHeight; // Distance between lines + CTLineRef *lines; + CGFloat *widths; // Width of each line +} ARTTextFrame; diff --git a/Libraries/ART/Brushes/ARTBrush.h b/Libraries/ART/Brushes/ARTBrush.h new file mode 100644 index 00000000000000..05020dd7bafa26 --- /dev/null +++ b/Libraries/ART/Brushes/ARTBrush.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +@interface ARTBrush : NSObject + +/* @abstract */ +- (instancetype)initWithArray:(NSArray *)data NS_DESIGNATED_INITIALIZER; + +/** + * For certain brushes we can fast path a combined fill and stroke. + * For those brushes we override applyFillColor which sets the fill + * color to be used by those batch paints. Those return YES. + * We can't batch gradient painting in CoreGraphics, so those will + * return NO and paint gets called instead. + * @abstract + */ +- (BOOL)applyFillColor:(CGContextRef)context; + +/** + * paint fills the context with a brush. The context is assumed to + * be clipped. + * @abstract + */ +- (void)paint:(CGContextRef)context; + +@end diff --git a/Libraries/ART/Brushes/ARTBrush.m b/Libraries/ART/Brushes/ARTBrush.m new file mode 100644 index 00000000000000..efc82dea35bb25 --- /dev/null +++ b/Libraries/ART/Brushes/ARTBrush.m @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTBrush.h" + +@implementation ARTBrush + +- (instancetype)initWithArray:(NSArray *)data +{ + return [super init]; +} + +- (BOOL)applyFillColor:(CGContextRef)context +{ + return NO; +} + +- (void)paint:(CGContextRef)context +{ + // abstract +} + +@end diff --git a/Libraries/ART/Brushes/ARTLinearGradient.h b/Libraries/ART/Brushes/ARTLinearGradient.h new file mode 100644 index 00000000000000..d7ff2e5684458f --- /dev/null +++ b/Libraries/ART/Brushes/ARTLinearGradient.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTBrush.h" + +@interface ARTLinearGradient : ARTBrush + +@end diff --git a/Libraries/ART/Brushes/ARTLinearGradient.m b/Libraries/ART/Brushes/ARTLinearGradient.m new file mode 100644 index 00000000000000..8793ff07bf71ff --- /dev/null +++ b/Libraries/ART/Brushes/ARTLinearGradient.m @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTLinearGradient.h" + +#import "RCTConvert+ART.h" +#import "RCTLog.h" + +@implementation ARTLinearGradient +{ + CGGradientRef _gradient; + CGPoint _startPoint; + CGPoint _endPoint; +} + +- (instancetype)initWithArray:(NSArray *)array +{ + if ((self = [super initWithArray:array])) { + if (array.count < 5) { + RCTLogError(@"-[%@ %@] expects 5 elements, received %@", + self.class, NSStringFromSelector(_cmd), array); + return nil; + } + _startPoint = [RCTConvert CGPoint:array offset:1]; + _endPoint = [RCTConvert CGPoint:array offset:3]; + _gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]); + } + return self; +} + +- (void)dealloc +{ + CGGradientRelease(_gradient); +} + +- (void)paint:(CGContextRef)context +{ + CGGradientDrawingOptions extendOptions = + kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; + CGContextDrawLinearGradient(context, _gradient, _startPoint, _endPoint, extendOptions); +} + +@end diff --git a/Libraries/ART/Brushes/ARTPattern.h b/Libraries/ART/Brushes/ARTPattern.h new file mode 100644 index 00000000000000..5f513ec60e2ae6 --- /dev/null +++ b/Libraries/ART/Brushes/ARTPattern.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTBrush.h" + +@interface ARTPattern : ARTBrush + +@end diff --git a/Libraries/ART/Brushes/ARTPattern.m b/Libraries/ART/Brushes/ARTPattern.m new file mode 100644 index 00000000000000..07dd867001880f --- /dev/null +++ b/Libraries/ART/Brushes/ARTPattern.m @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTPattern.h" + +#import "RCTConvert+ART.h" +#import "RCTLog.h" + +@implementation ARTPattern +{ + CGImageRef _image; + CGRect _rect; +} + +- (instancetype)initWithArray:(NSArray *)array +{ + if ((self = [super initWithArray:array])) { + if (array.count < 6) { + RCTLogError(@"-[%@ %@] expects 6 elements, received %@", + self.class, NSStringFromSelector(_cmd), array); + return nil; + } + _image = CGImageRetain([RCTConvert CGImage:array[1]]); + _rect = [RCTConvert CGRect:array offset:2]; + } + return self; +} + +- (void)dealloc +{ + CGImageRelease(_image); +} + +// Note: This could use applyFillColor with a pattern. This could be more efficient but +// to do that, we need to calculate our own user space CTM. + +- (void)paint:(CGContextRef)context +{ + CGContextDrawTiledImage(context, _rect, _image); +} + + + +@end diff --git a/Libraries/ART/Brushes/ARTRadialGradient.h b/Libraries/ART/Brushes/ARTRadialGradient.h new file mode 100644 index 00000000000000..7f86d93058ce80 --- /dev/null +++ b/Libraries/ART/Brushes/ARTRadialGradient.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTBrush.h" + +@interface ARTRadialGradient : ARTBrush + +@end diff --git a/Libraries/ART/Brushes/ARTRadialGradient.m b/Libraries/ART/Brushes/ARTRadialGradient.m new file mode 100644 index 00000000000000..b59b1736937a37 --- /dev/null +++ b/Libraries/ART/Brushes/ARTRadialGradient.m @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTRadialGradient.h" + +#import "RCTConvert+ART.h" +#import "RCTLog.h" + +@implementation ARTRadialGradient +{ + CGGradientRef _gradient; + CGPoint _focusPoint; + CGPoint _centerPoint; + CGFloat _radius; + CGFloat _radiusRatio; +} + +- (instancetype)initWithArray:(NSArray *)array +{ + if ((self = [super initWithArray:array])) { + if (array.count < 7) { + RCTLogError(@"-[%@ %@] expects 7 elements, received %@", + self.class, NSStringFromSelector(_cmd), array); + return nil; + } + _radius = [RCTConvert CGFloat:array[3]]; + _radiusRatio = [RCTConvert CGFloat:array[4]] / _radius; + _focusPoint.x = [RCTConvert CGFloat:array[1]]; + _focusPoint.y = [RCTConvert CGFloat:array[2]] / _radiusRatio; + _centerPoint.x = [RCTConvert CGFloat:array[5]]; + _centerPoint.y = [RCTConvert CGFloat:array[6]] / _radiusRatio; + _gradient = CGGradientRetain([RCTConvert CGGradient:array offset:7]); + } + return self; +} + +- (void)dealloc +{ + CGGradientRelease(_gradient); +} + +- (void)paint:(CGContextRef)context +{ + CGAffineTransform transform = CGAffineTransformMakeScale(1, _radiusRatio); + CGContextConcatCTM(context, transform); + CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; + CGContextDrawRadialGradient(context, _gradient, _focusPoint, 0, _centerPoint, _radius, extendOptions); +} + +@end diff --git a/Libraries/ART/Brushes/ARTSolidColor.h b/Libraries/ART/Brushes/ARTSolidColor.h new file mode 100644 index 00000000000000..f212c735680f60 --- /dev/null +++ b/Libraries/ART/Brushes/ARTSolidColor.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTBrush.h" + +@interface ARTSolidColor : ARTBrush + +@end diff --git a/Libraries/ART/Brushes/ARTSolidColor.m b/Libraries/ART/Brushes/ARTSolidColor.m new file mode 100644 index 00000000000000..229942ddec6490 --- /dev/null +++ b/Libraries/ART/Brushes/ARTSolidColor.m @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTSolidColor.h" + +#import "RCTConvert+ART.h" +#import "RCTLog.h" + +@implementation ARTSolidColor +{ + CGColorRef _color; +} + +- (instancetype)initWithArray:(NSArray *)array +{ + if ((self = [super initWithArray:array])) { + _color = CGColorRetain([RCTConvert CGColor:array offset:1]); + } + return self; +} + +- (void)dealloc +{ + CGColorRelease(_color); +} + +- (BOOL)applyFillColor:(CGContextRef)context +{ + CGContextSetFillColorWithColor(context, _color); + return YES; +} + +@end diff --git a/Libraries/ART/RCTConvert+ART.h b/Libraries/ART/RCTConvert+ART.h new file mode 100644 index 00000000000000..24944fb1298b42 --- /dev/null +++ b/Libraries/ART/RCTConvert+ART.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "ARTBrush.h" +#import "ARTCGFloatArray.h" +#import "ARTTextFrame.h" +#import "RCTConvert.h" + +@interface RCTConvert (ART) + ++ (CGPathRef)CGPath:(id)json; ++ (CTFontRef)CTFont:(id)json; ++ (CTTextAlignment)CTTextAlignment:(id)json; ++ (ARTTextFrame)ARTTextFrame:(id)json; ++ (ARTCGFloatArray)ARTCGFloatArray:(id)json; ++ (ARTBrush *)ARTBrush:(id)json; + ++ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset; ++ (CGRect)CGRect:(id)json offset:(NSUInteger)offset; ++ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset; ++ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset; + +@end diff --git a/Libraries/ART/RCTConvert+ART.m b/Libraries/ART/RCTConvert+ART.m new file mode 100644 index 00000000000000..4cd11bd3fbba9d --- /dev/null +++ b/Libraries/ART/RCTConvert+ART.m @@ -0,0 +1,234 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTConvert+ART.h" + +#import "ARTLinearGradient.h" +#import "ARTPattern.h" +#import "ARTRadialGradient.h" +#import "ARTSolidColor.h" +#import "RCTLog.h" + +@implementation RCTConvert (ART) + ++ (CGPathRef)CGPath:(id)json +{ + NSArray *arr = [self NSNumberArray:json]; + + NSUInteger count = [arr count]; + +#define NEXT_VALUE [self double:arr[i++]] + + CGMutablePathRef path = CGPathCreateMutable(); + CGPathMoveToPoint(path, NULL, 0, 0); + + @try { + NSUInteger i = 0; + while (i < count) { + NSUInteger type = [arr[i++] unsignedIntegerValue]; + switch (type) { + case 0: + CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE); + break; + case 1: + CGPathCloseSubpath(path); + break; + case 2: + CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE); + break; + case 3: + CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE); + break; + case 4: + CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0); + break; + default: + RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr); + CGPathRelease(path); + return NULL; + } + } + } + @catch (NSException *exception) { + RCTLogError(@"Invalid CGPath format: %@", arr); + CGPathRelease(path); + return NULL; + } + + return (CGPathRef)CFAutorelease(path); +} + ++ (CTFontRef)CTFont:(id)json +{ + NSDictionary *dict = [self NSDictionary:json]; + if (!dict) { + return nil; + } + CTFontDescriptorRef fontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)dict); + CTFontRef font = CTFontCreateWithFontDescriptor(fontDescriptor, 0.0, NULL); + CFRelease(fontDescriptor); + return (CTFontRef)CFAutorelease(font); +} + +RCT_ENUM_CONVERTER(CTTextAlignment, (@{ + @"auto": @(kCTTextAlignmentNatural), + @"left": @(kCTTextAlignmentLeft), + @"center": @(kCTTextAlignmentCenter), + @"right": @(kCTTextAlignmentRight), + @"justify": @(kCTTextAlignmentJustified), +}), kCTTextAlignmentNatural, integerValue) + +// This takes a tuple of text lines and a font to generate a CTLine for each text line. +// This prepares everything for rendering a frame of text in ARTText. ++ (ARTTextFrame)ARTTextFrame:(id)json +{ + NSDictionary *dict = [self NSDictionary:json]; + ARTTextFrame frame; + frame.count = 0; + + NSArray *lines = [self NSArray:dict[@"lines"]]; + NSUInteger lineCount = [lines count]; + if (lineCount == 0) { + return frame; + } + + CTFontRef font = [self CTFont:dict[@"font"]]; + if (!font) { + return frame; + } + + // Create a dictionary for this font + CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ + (NSString *)kCTFontAttributeName: (__bridge id)font, + (NSString *)kCTForegroundColorFromContextAttributeName: @YES + }; + + // Set up text frame with font metrics + CGFloat size = CTFontGetSize(font); + frame.count = lineCount; + frame.baseLine = size; // estimate base line + frame.lineHeight = size * 1.1; // Base on ART canvas line height estimate + frame.lines = malloc(sizeof(CTLineRef) * lineCount); + frame.widths = malloc(sizeof(CGFloat) * lineCount); + + [lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) { + + CFStringRef string = (__bridge CFStringRef)text; + CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); + CTLineRef line = CTLineCreateWithAttributedString(attrString); + CFRelease(attrString); + + frame.lines[i] = line; + frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL); + }]; + + return frame; +} + ++ (ARTCGFloatArray)ARTCGFloatArray:(id)json +{ + NSArray *arr = [self NSNumberArray:json]; + NSUInteger count = arr.count; + + ARTCGFloatArray array; + array.count = count; + array.array = NULL; + + if (count) { + // Ideally, these arrays should already use the same memory layout. + // In that case we shouldn't need this new malloc. + array.array = malloc(sizeof(CGFloat) * count); + for (NSUInteger i = 0; i < count; i++) { + array.array[i] = [arr[i] doubleValue]; + } + } + + return array; +} + ++ (ARTBrush *)ARTBrush:(id)json +{ + NSArray *arr = [self NSArray:json]; + NSUInteger type = [self NSUInteger:arr[0]]; + switch (type) { + case 0: // solid color + // These are probably expensive allocations since it's often the same value. + // We should memoize colors but look ups may be just as expensive. + return [[ARTSolidColor alloc] initWithArray:arr]; + case 1: // linear gradient + return [[ARTLinearGradient alloc] initWithArray:arr]; + case 2: // radial gradient + return [[ARTRadialGradient alloc] initWithArray:arr]; + case 3: // pattern + return [[ARTPattern alloc] initWithArray:arr]; + default: + RCTLogError(@"Unknown brush type: %zd", type); + return nil; + } +} + ++ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset +{ + NSArray *arr = [self NSArray:json]; + if (arr.count < offset + 2) { + RCTLogError(@"Too few elements in array (expected at least %zd): %@", 2 + offset, arr); + return CGPointZero; + } + return (CGPoint){ + [self CGFloat:arr[offset]], + [self CGFloat:arr[offset + 1]], + }; +} + ++ (CGRect)CGRect:(id)json offset:(NSUInteger)offset +{ + NSArray *arr = [self NSArray:json]; + if (arr.count < offset + 4) { + RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr); + return CGRectZero; + } + return (CGRect){ + {[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]}, + {[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]}, + }; +} + ++ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset +{ + NSArray *arr = [self NSArray:json]; + if (arr.count < offset + 4) { + RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr); + return NULL; + } + return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]]; +} + ++ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset +{ + NSArray *arr = [self NSArray:json]; + if (arr.count < offset) { + RCTLogError(@"Too few elements in array (expected at least %zd): %@", offset, arr); + return NULL; + } + arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}]; + ARTCGFloatArray colorsAndOffsets = [self ARTCGFloatArray:arr]; + size_t stops = colorsAndOffsets.count / 5; + CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColorComponents( + rgb, + colorsAndOffsets.array, + colorsAndOffsets.array + stops * 4, + stops + ); + CGColorSpaceRelease(rgb); + free(colorsAndOffsets.array); + return (CGGradientRef)CFAutorelease(gradient); +} + +@end diff --git a/Libraries/ART/ReactIOSART.js b/Libraries/ART/ReactIOSART.js new file mode 100644 index 00000000000000..9ef2f8843bf7e1 --- /dev/null +++ b/Libraries/ART/ReactIOSART.js @@ -0,0 +1,611 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactIOSART + */ + +"use strict"; + +var Color = require('art/core/color'); +var Path = require('ARTSerializablePath'); +var Transform = require('art/core/transform'); + +var React = require('React'); +var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); + +var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); +var merge = require('merge'); + +// Diff Helpers + +function arrayDiffer(a, b) { + if (a == null) { + return true; + } + if (a.length !== b.length) { + return true; + } + for (var i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return true; + } + } + return false; +} + +function fontAndLinesDiffer(a, b) { + if (a === b) { + return false; + } + if (a.font !== b.font) { + if (a.font === null) { + return true; + } + if (b.font === null) { + return true; + } + + var aTraits = a.font.NSCTFontTraitsAttribute; + var bTraits = b.font.NSCTFontTraitsAttribute; + + if ( + a.font.fontFamily !== b.font.fontFamily || + a.font.fontSize !== b.font.fontSize || + a.font.fontWeight !== b.font.fontWeight || + a.font.fontStyle !== b.font.fontStyle || + // TODO(6364240): remove iOS-specific attrs + a.font.NSFontFamilyAttribute !== b.font.NSFontFamilyAttribute || + a.font.NSFontSizeAttribute !== b.font.NSFontSizeAttribute || + aTraits.NSCTFontSymbolicTrait !== bTraits.NSCTFontSymbolicTrait + ) { + return true; + } + } + return arrayDiffer(a.lines, b.lines); +} + +// Native Attributes + +var SurfaceViewAttributes = merge(ReactIOSViewAttributes.UIView, { + // This should contain pixel information such as width, height and + // resolution to know what kind of buffer needs to be allocated. + // Currently we rely on UIViews and style to figure that out. +}); + +var NodeAttributes = { + transform: { diff: arrayDiffer }, + opacity: true, +}; + +var GroupAttributes = merge(NodeAttributes, { + clipping: { diff: arrayDiffer } +}); + +var RenderableAttributes = merge(NodeAttributes, { + fill: { diff: arrayDiffer }, + stroke: { diff: arrayDiffer }, + strokeWidth: true, + strokeCap: true, + strokeJoin: true, + strokeDash: { diff: arrayDiffer }, +}); + +var ShapeAttributes = merge(RenderableAttributes, { + d: { diff: arrayDiffer }, +}); + +var TextAttributes = merge(RenderableAttributes, { + alignment: true, + frame: { diff: fontAndLinesDiffer }, + path: { diff: arrayDiffer } +}); + +// Native Components + +var NativeSurfaceView = createReactIOSNativeComponentClass({ + validAttributes: SurfaceViewAttributes, + uiViewClassName: 'ARTSurfaceView', +}); + +var NativeGroup = createReactIOSNativeComponentClass({ + validAttributes: GroupAttributes, + uiViewClassName: 'ARTGroup', +}); + +var NativeShape = createReactIOSNativeComponentClass({ + validAttributes: ShapeAttributes, + uiViewClassName: 'ARTShape', +}); + +var NativeText = createReactIOSNativeComponentClass({ + validAttributes: TextAttributes, + uiViewClassName: 'ARTText', +}); + +// Utilities + +function childrenAsString(children) { + if (!children) { + return ''; + } + if (typeof children === 'string') { + return children; + } + if (children.length) { + return children.join('\n'); + } + return ''; +} + +// Surface - Root node of all ART + +var Surface = React.createClass({ + + render: function() { + var props = this.props; + var w = extractNumber(props.width, 0); + var h = extractNumber(props.height, 0); + return ( + + {this.props.children} + + ); + } + +}); + +// Node Props + +// TODO: The desktop version of ART has title and cursor. We should have +// accessibility support here too even though hovering doesn't work. + +function extractNumber(value, defaultValue) { + if (value == null) { + return defaultValue; + } + return +value; +} + +var pooledTransform = new Transform(); + +function extractTransform(props) { + var scaleX = props.scaleX != null ? props.scaleX : + props.scale != null ? props.scale : 1; + var scaleY = props.scaleY != null ? props.scaleY : + props.scale != null ? props.scale : 1; + + pooledTransform + .transformTo(1, 0, 0, 1, 0, 0) + .move(props.x || 0, props.y || 0) + .rotate(props.rotation || 0, props.originX, props.originY) + .scale(scaleX, scaleY, props.originX, props.originY); + + if (props.transform != null) { + pooledTransform.transform(props.transform); + } + + return [ + pooledTransform.xx, pooledTransform.yx, + pooledTransform.xy, pooledTransform.yy, + pooledTransform.x, pooledTransform.y, + ]; +} + +function extractOpacity(props) { + // TODO: visible === false should also have no hit detection + if (props.visible === false) { + return 0; + } + if (props.opacity == null) { + return 1; + } + return +props.opacity; +} + +// Groups + +// Note: ART has a notion of width and height on Group but AFAIK it's a noop in +// ReactART. + +var Group = React.createClass({ + + render: function() { + var props = this.props; + return ( + + {this.props.children} + + ); + } + +}); + +var ClippingRectangle = React.createClass({ + + render: function() { + var props = this.props; + var x = extractNumber(props.x, 0); + var y = extractNumber(props.y, 0); + var w = extractNumber(props.width, 0); + var h = extractNumber(props.height, 0); + var clipping = new Path() + .moveTo(x, y) + .line(w, 0) + .line(0, h) + .line(w, 0) + .close() + .toJSON(); + // The current clipping API requires x and y to be ignored in the transform + var propsExcludingXAndY = merge(props); + delete propsExcludingXAndY.x; + delete propsExcludingXAndY.y; + return ( + + {this.props.children} + + ); + } + +}); + +// Renderables + +var SOLID_COLOR = 0; +var LINEAR_GRADIENT = 1; +var RADIAL_GRADIENT = 2; +var PATTERN = 3; + +function insertColorIntoArray(color, targetArray, atIndex) { + var c = new Color(color); + targetArray[atIndex + 0] = c.red / 255; + targetArray[atIndex + 1] = c.green / 255; + targetArray[atIndex + 2] = c.blue / 255; + targetArray[atIndex + 3] = c.alpha; +} + +function insertColorsIntoArray(stops, targetArray, atIndex) { + var i = 0; + if ('length' in stops) { + while (i < stops.length) { + insertColorIntoArray(stops[i], targetArray, atIndex + i * 4); + i++; + } + } else { + for (var offset in stops) { + insertColorIntoArray(stops[offset], targetArray, atIndex + i * 4); + i++; + } + } + return atIndex + i * 4; +} + +function insertOffsetsIntoArray(stops, targetArray, atIndex, multi, reverse) { + var offsetNumber; + var i = 0; + if ('length' in stops) { + while (i < stops.length) { + offsetNumber = i / (stops.length - 1) * multi; + targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber; + i++; + } + } else { + for (var offsetString in stops) { + offsetNumber = (+offsetString) * multi; + targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber; + i++; + } + } + return atIndex + i; +} + +function insertColorStopsIntoArray(stops, targetArray, atIndex) { + var lastIndex = insertColorsIntoArray(stops, targetArray, atIndex); + insertOffsetsIntoArray(stops, targetArray, lastIndex, 1, false); +} + +function insertDoubleColorStopsIntoArray(stops, targetArray, atIndex) { + var lastIndex = insertColorsIntoArray(stops, targetArray, atIndex); + lastIndex = insertColorsIntoArray(stops, targetArray, lastIndex); + lastIndex = insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, false); + insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, true); +} + +function applyBoundingBoxToBrushData(brushData, props) { + var type = brushData[0]; + var width = +props.width; + var height = +props.height; + if (type === LINEAR_GRADIENT) { + brushData[1] *= width; + brushData[2] *= height; + brushData[3] *= width; + brushData[4] *= height; + } else if (type === RADIAL_GRADIENT) { + brushData[1] *= width; + brushData[2] *= height; + brushData[3] *= width; + brushData[4] *= height; + brushData[5] *= width; + brushData[6] *= height; + } else if (type === PATTERN) { + // todo + } +} + +function extractBrush(colorOrBrush, props) { + if (colorOrBrush == null) { + return null; + } + if (colorOrBrush._brush) { + if (colorOrBrush._bb) { + // The legacy API for Gradients allow for the bounding box to be used + // as a convenience for specifying gradient positions. This should be + // deprecated. It's not properly implemented in canvas mode. ReactART + // doesn't handle update to the bounding box correctly. That's why we + // mutate this so that if it's reused, we reuse the same resolved box. + applyBoundingBoxToBrushData(colorOrBrush._brush, props); + colorOrBrush._bb = false; + } + return colorOrBrush._brush; + } + var c = new Color(colorOrBrush); + return [SOLID_COLOR, c.red / 255, c.green / 255, c.blue / 255, c.alpha]; +} + +function extractColor(color) { + if (color == null) { + return null; + } + var c = new Color(color); + return [c.red / 255, c.green / 255, c.blue / 255, c.alpha]; +} + +function extractStrokeCap(strokeCap) { + switch (strokeCap) { + case 'butt': return 0; + case 'square': return 2; + default: return 1; // round + } +} + +function extractStrokeJoin(strokeJoin) { + switch (strokeJoin) { + case 'miter': return 0; + case 'bevel': return 2; + default: return 1; // round + } +} + +// Shape + +// Note: ART has a notion of width and height on Shape but AFAIK it's a noop in +// ReactART. + +var Shape = React.createClass({ + + render: function() { + var props = this.props; + var path = props.d || childrenAsString(props.children); + var d = new Path(path).toJSON(); + return ( + + ); + } + +}); + +// Text + +var cachedFontObjectsFromString = {}; + +function extractFontTraits(isBold, isItalic) { + var italic = isItalic ? 1 : 0; + var bold = isBold ? 2 : 0; + return { + NSCTFontSymbolicTrait: italic | bold + }; +} + +var fontFamilyPrefix = /^[\s"']*/; +var fontFamilySuffix = /[\s"']*$/; + +function extractSingleFontFamily(fontFamilyString) { + // ART on the web allows for multiple font-families to be specified. + // For compatibility, we extract the first font-family, hoping + // we'll get a match. + return fontFamilyString.split(',')[0] + .replace(fontFamilyPrefix, '') + .replace(fontFamilySuffix, ''); +} + +function parseFontString(font) { + if (cachedFontObjectsFromString.hasOwnProperty(font)) { + return cachedFontObjectsFromString[font]; + } + var regexp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm\%]*(?:\s*\/.*?)?\s+)?\s*\"?([^\"]*)/i; + var match = regexp.exec(font); + if (!match) { + return null; + } + var fontFamily = extractSingleFontFamily(match[3]); + var fontSize = +match[2] || 12; + var isBold = /bold/.exec(match[1]); + var isItalic = /italic/.exec(match[1]); + cachedFontObjectsFromString[font] = { + fontFamily: fontFamily, + fontSize: fontSize, + fontWeight: isBold ? 'bold' : 'normal', + fontStyle: isItalic ? 'italic' : 'normal', + // TODO(6364240): remove iOS-specific attrs + NSFontFamilyAttribute: fontFamily, + NSFontSizeAttribute: fontSize, + NSCTFontTraitsAttribute: extractFontTraits(isBold, isItalic) + }; + return cachedFontObjectsFromString[font]; +} + +function extractFont(font) { + if (font == null) { + return null; + } + if (typeof font === 'string') { + return parseFontString(font); + } + var fontFamily = extractSingleFontFamily(font.fontFamily); + var fontSize = +font.fontSize || 12; + return { + // Normalize + fontFamily: fontFamily, + fontSize: fontSize, + fontWeight: font.fontWeight, + fontStyle: font.fontStyle, + // TODO(6364240): remove iOS-specific attrs + NSFontFamilyAttribute: fontFamily, + NSFontSizeAttribute: fontSize, + NSCTFontTraitsAttribute: extractFontTraits( + font.fontWeight === 'bold', + font.fontStyle === 'italic' + ) + }; +} + +var newLine = /\n/g; +function extractFontAndLines(font, text) { + return { font: extractFont(font), lines: text.split(newLine) }; +} + +function extractAlignment(alignment) { + switch (alignment) { + case 'right': + return 1; + case 'center': + return 2; + default: + return 0; + } +} + +var Text = React.createClass({ + + render: function() { + var props = this.props; + var textPath = props.path ? new Path(props.path).toJSON() : null; + var textFrame = extractFontAndLines( + props.font, + childrenAsString(props.children) + ); + return ( + + ); + } + +}); + +// Declarative fill type objects - API design not finalized + +function LinearGradient(stops, x1, y1, x2, y2) { + var type = LINEAR_GRADIENT; + + if (arguments.length < 5) { + var angle = ((x1 == null) ? 270 : x1) * Math.PI / 180; + + var x = Math.cos(angle); + var y = -Math.sin(angle); + var l = (Math.abs(x) + Math.abs(y)) / 2; + + x *= l; y *= l; + + x1 = 0.5 - x; + x2 = 0.5 + x; + y1 = 0.5 - y; + y2 = 0.5 + y; + this._bb = true; + } else { + this._bb = false; + } + + var brushData = [type, +x1, +y1, +x2, +y2]; + insertColorStopsIntoArray(stops, brushData, 5); + this._brush = brushData; +} + +function RadialGradient(stops, fx, fy, rx, ry, cx, cy) { + if (ry == null) { + ry = rx; + } + if (cx == null) { + cx = fx; + } + if (cy == null) { + cy = fy; + } + if (fx == null) { + // As a convenience we allow the whole radial gradient to cover the + // bounding box. We should consider dropping this API. + fx = fy = rx = ry = cx = cy = 0.5; + this._bb = true; + } else { + this._bb = false; + } + // The ART API expects the radial gradient to be repeated at the edges. + // To simulate this we render the gradient twice as large and add double + // color stops. Ideally this API would become more restrictive so that this + // extra work isn't needed. + var brushData = [RADIAL_GRADIENT, +fx, +fy, +rx * 2, +ry * 2, +cx, +cy]; + insertDoubleColorStopsIntoArray(stops, brushData, 7); + this._brush = brushData; +} + +function Pattern(url, width, height, left, top) { + this._brush = [PATTERN, url, +left || 0, +top || 0, +width, +height]; +} + +var ReactART = { + + LinearGradient: LinearGradient, + RadialGradient: RadialGradient, + Pattern: Pattern, + Transform: Transform, + Path: Path, + Surface: Surface, + Group: Group, + ClippingRectangle: ClippingRectangle, + Shape: Shape, + Text: Text, + +}; + +module.exports = ReactART; diff --git a/Libraries/ART/ViewManagers/ARTGroupManager.h b/Libraries/ART/ViewManagers/ARTGroupManager.h new file mode 100644 index 00000000000000..0a90eb3d6c18c9 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTGroupManager.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTNodeManager.h" + +@interface ARTGroupManager : ARTNodeManager + +@end diff --git a/Libraries/ART/ViewManagers/ARTGroupManager.m b/Libraries/ART/ViewManagers/ARTGroupManager.m new file mode 100644 index 00000000000000..15f55d4df12d11 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTGroupManager.m @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTGroupManager.h" + +#import "ARTGroup.h" + +@implementation ARTGroupManager + +RCT_EXPORT_MODULE() + +- (ARTNode *)node +{ + return [[ARTGroup alloc] init]; +} + +@end diff --git a/Libraries/ART/ViewManagers/ARTNodeManager.h b/Libraries/ART/ViewManagers/ARTNodeManager.h new file mode 100644 index 00000000000000..1097eefde91246 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTNodeManager.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTNode.h" +#import "RCTViewManager.h" + +@interface ARTNodeManager : RCTViewManager + +- (ARTNode *)node; + +@end diff --git a/Libraries/ART/ViewManagers/ARTNodeManager.m b/Libraries/ART/ViewManagers/ARTNodeManager.m new file mode 100644 index 00000000000000..c2f0dba35ad065 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTNodeManager.m @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTNodeManager.h" + +#import "ARTNode.h" + +@implementation ARTNodeManager + +RCT_EXPORT_MODULE() + +- (ARTNode *)node +{ + return [[ARTNode alloc] init]; +} + +- (UIView *)view +{ + return [self node]; +} + +- (RCTShadowView *)shadowView +{ + return nil; +} + +RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform) + +@end diff --git a/Libraries/ART/ViewManagers/ARTRenderableManager.h b/Libraries/ART/ViewManagers/ARTRenderableManager.h new file mode 100644 index 00000000000000..376fcf518b3293 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTRenderableManager.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTNodeManager.h" +#import "ARTRenderable.h" + +@interface ARTRenderableManager : ARTNodeManager + +- (ARTRenderable *)node; + +@end diff --git a/Libraries/ART/ViewManagers/ARTRenderableManager.m b/Libraries/ART/ViewManagers/ARTRenderableManager.m new file mode 100644 index 00000000000000..01b579dca4c940 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTRenderableManager.m @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTRenderableManager.h" + +#import "RCTConvert+ART.h" + +@implementation ARTRenderableManager + +RCT_EXPORT_MODULE() + +- (ARTRenderable *)node +{ + return [[ARTRenderable alloc] init]; +} + +RCT_EXPORT_VIEW_PROPERTY(strokeWidth, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(strokeCap, CGLineCap) +RCT_EXPORT_VIEW_PROPERTY(strokeJoin, CGLineJoin) +RCT_EXPORT_VIEW_PROPERTY(fill, ARTBrush) +RCT_EXPORT_VIEW_PROPERTY(stroke, CGColor) +RCT_EXPORT_VIEW_PROPERTY(strokeDash, ARTCGFloatArray) + +@end diff --git a/Libraries/ART/ViewManagers/ARTShapeManager.h b/Libraries/ART/ViewManagers/ARTShapeManager.h new file mode 100644 index 00000000000000..d6bc76baa09fdb --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTShapeManager.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTRenderableManager.h" + +@interface ARTShapeManager : ARTRenderableManager + +@end diff --git a/Libraries/ART/ViewManagers/ARTShapeManager.m b/Libraries/ART/ViewManagers/ARTShapeManager.m new file mode 100644 index 00000000000000..426237fa75cfc8 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTShapeManager.m @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTShapeManager.h" + +#import "ARTShape.h" +#import "RCTConvert+ART.h" + +@implementation ARTShapeManager + +RCT_EXPORT_MODULE() + +- (ARTRenderable *)node +{ + return [[ARTShape alloc] init]; +} + +RCT_EXPORT_VIEW_PROPERTY(d, CGPath) + +@end diff --git a/Libraries/ART/ViewManagers/ARTSurfaceViewManager.h b/Libraries/ART/ViewManagers/ARTSurfaceViewManager.h new file mode 100644 index 00000000000000..6d8e140049e8fb --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTSurfaceViewManager.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTViewManager.h" + +@interface ARTSurfaceViewManager : RCTViewManager + +@end diff --git a/Libraries/ART/ViewManagers/ARTSurfaceViewManager.m b/Libraries/ART/ViewManagers/ARTSurfaceViewManager.m new file mode 100644 index 00000000000000..ddfba6697be52c --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTSurfaceViewManager.m @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTSurfaceViewManager.h" + +#import "ARTSurfaceView.h" + +@implementation ARTSurfaceViewManager + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + return [[ARTSurfaceView alloc] init]; +} + +@end diff --git a/Libraries/ART/ViewManagers/ARTTextManager.h b/Libraries/ART/ViewManagers/ARTTextManager.h new file mode 100644 index 00000000000000..48da9c891f4757 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTTextManager.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTRenderableManager.h" + +@interface ARTTextManager : ARTRenderableManager + +@end diff --git a/Libraries/ART/ViewManagers/ARTTextManager.m b/Libraries/ART/ViewManagers/ARTTextManager.m new file mode 100644 index 00000000000000..473d0cf4fa4a88 --- /dev/null +++ b/Libraries/ART/ViewManagers/ARTTextManager.m @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ARTTextManager.h" + +#import "ARTText.h" +#import "RCTConvert+ART.h" + +@implementation ARTTextManager + +RCT_EXPORT_MODULE() + +- (ARTRenderable *)node +{ + return [[ARTText alloc] init]; +} + +RCT_EXPORT_VIEW_PROPERTY(alignment, CTTextAlignment) +RCT_REMAP_VIEW_PROPERTY(frame, textFrame, ARTTextFrame) + +@end From 81e82d10abf5f72772a2d5535a94bc6bc760c92c Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 29 Apr 2015 15:55:36 -0700 Subject: [PATCH 147/250] Added missing features from Multiline Text commit --- Examples/UIExplorer/TextInputExample.js | 31 +++++++++++++++++++-- Libraries/Components/TextInput/TextInput.js | 4 +++ Libraries/Text/RCTTextView.h | 2 ++ Libraries/Text/RCTTextView.m | 21 ++++++++++++-- Libraries/Text/RCTTextViewManager.m | 13 +++++---- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/Examples/UIExplorer/TextInputExample.js b/Examples/UIExplorer/TextInputExample.js index a32937fa4d1590..922dd9607d4b88 100644 --- a/Examples/UIExplorer/TextInputExample.js +++ b/Examples/UIExplorer/TextInputExample.js @@ -99,12 +99,21 @@ var styles = StyleSheet.create({ fontSize: 13, height: 50, padding: 4, + marginBottom: 4, }, multilineWithFontStyles: { color: 'blue', fontWeight: 'bold', fontSize: 18, fontFamily: 'Cochin', + height: 60, + }, + multilineChild: { + width: 50, + height: 40, + position: 'absolute', + right: 5, + backgroundColor: 'red', }, eventLabel: { margin: 3, @@ -351,14 +360,32 @@ exports.examples = [ + + + + ) } diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 9de1e15e92176f..c21184b7da5130 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -38,6 +38,7 @@ var returnKeyTypeConsts = RCTUIManager.UIReturnKeyType; var RCTTextViewAttributes = merge(ReactIOSViewAttributes.UIView, { autoCorrect: true, autoCapitalize: true, + clearTextOnFocus: true, color: true, editable: true, fontFamily: true, @@ -48,6 +49,7 @@ var RCTTextViewAttributes = merge(ReactIOSViewAttributes.UIView, { returnKeyType: true, enablesReturnKeyAutomatically: true, secureTextEntry: true, + selectTextOnFocus: true, mostRecentEventCounter: true, placeholder: true, placeholderTextColor: true, @@ -499,6 +501,8 @@ var TextInput = React.createClass({ autoCapitalize={autoCapitalize} autoCorrect={this.props.autoCorrect} clearButtonMode={clearButtonMode} + selectTextOnFocus={this.props.selectTextOnFocus} + clearTextOnFocus={this.props.clearTextOnFocus} />; } diff --git a/Libraries/Text/RCTTextView.h b/Libraries/Text/RCTTextView.h index 742c815348909f..19f2fea397b8c5 100644 --- a/Libraries/Text/RCTTextView.h +++ b/Libraries/Text/RCTTextView.h @@ -17,6 +17,8 @@ @interface RCTTextView : RCTView @property (nonatomic, assign) BOOL autoCorrect; +@property (nonatomic, assign) BOOL clearTextOnFocus; +@property (nonatomic, assign) BOOL selectTextOnFocus; @property (nonatomic, assign) UIEdgeInsets contentInset; @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; @property (nonatomic, strong) UIColor *placeholderTextColor; diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index a98bbc3ea381bc..c5947f317e0bd8 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -133,8 +133,24 @@ - (BOOL)autoCorrect return _textView.autocorrectionType == UITextAutocorrectionTypeYes; } +- (BOOL)textViewShouldBeginEditing:(UITextView *)textView +{ + if (_selectTextOnFocus) { + dispatch_async(dispatch_get_main_queue(), ^{ + [textView selectAll:nil]; + }); + } + return YES; +} + - (void)textViewDidBeginEditing:(UITextView *)textView { + if (_clearTextOnFocus) { + [_textView setText:@""]; + _textView.text = @""; + [self _setPlaceholderVisibility]; + } + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus reactTag:self.reactTag text:textView.text]; @@ -159,14 +175,15 @@ - (void)textViewDidEndEditing:(UITextView *)textView - (BOOL)becomeFirstResponder { _jsRequestingFirstResponder = YES; - BOOL result = [super becomeFirstResponder]; + BOOL result = [_textView becomeFirstResponder]; _jsRequestingFirstResponder = NO; return result; } - (BOOL)resignFirstResponder { - BOOL result = [super resignFirstResponder]; + [super resignFirstResponder]; + BOOL result = [_textView resignFirstResponder]; if (result) { [_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur reactTag:self.reactTag diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m index 5218544d232fe7..570a511157bee5 100644 --- a/Libraries/Text/RCTTextViewManager.m +++ b/Libraries/Text/RCTTextViewManager.m @@ -25,16 +25,17 @@ - (UIView *)view } RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL) +RCT_REMAP_VIEW_PROPERTY(editable, textView.editable, BOOL) RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString) RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(text, NSString) -RCT_REMAP_VIEW_PROPERTY(clearTextOnFocus, clearsOnBeginEditing, BOOL) -RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType) -RCT_EXPORT_VIEW_PROPERTY(returnKeyType, UIReturnKeyType) -RCT_EXPORT_VIEW_PROPERTY(enablesReturnKeyAutomatically, BOOL) -RCT_EXPORT_VIEW_PROPERTY(secureTextEntry, BOOL) +RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL) +RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL) +RCT_REMAP_VIEW_PROPERTY(keyboardType, textView.keyboardType, UIKeyboardType) +RCT_REMAP_VIEW_PROPERTY(returnKeyType, textView.returnKeyType, UIReturnKeyType) +RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, textView.enablesReturnKeyAutomatically, BOOL) RCT_REMAP_VIEW_PROPERTY(color, textColor, UIColor) -RCT_REMAP_VIEW_PROPERTY(autoCapitalize, autocapitalizationType, UITextAutocapitalizationType) +RCT_REMAP_VIEW_PROPERTY(autoCapitalize, textView.autocapitalizationType, UITextAutocapitalizationType) RCT_CUSTOM_VIEW_PROPERTY(fontSize, CGFloat, RCTTextView) { view.font = [RCTConvert UIFont:view.font withSize:json ?: @(defaultView.font.pointSize)]; From 4ac2fcc4a664655fd76cbd95bf5711d563c0125f Mon Sep 17 00:00:00 2001 From: Hedger Wang Date: Wed, 29 Apr 2015 16:37:56 -0700 Subject: [PATCH 148/250] [madman][android]: unbreak android app. --- .../SegmentedControlIOS.android.js | 49 +++++++++++++++++++ ...ntrolIOS.js => SegmentedControlIOS.ios.js} | 0 2 files changed, 49 insertions(+) create mode 100644 Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.android.js rename Libraries/Components/SegmentedControlIOS/{SegmentedControlIOS.js => SegmentedControlIOS.ios.js} (100%) diff --git a/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.android.js b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.android.js new file mode 100644 index 00000000000000..28fbea0271dc11 --- /dev/null +++ b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.android.js @@ -0,0 +1,49 @@ + +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule SegmentedControlIOS + */ + +'use strict'; + +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); + +var Dummy = React.createClass({ + render: function() { + return ( + + + SegmentedControlIOS is not supported on this platform! + + + ); + }, +}); + +var styles = StyleSheet.create({ + dummy: { + width: 120, + height: 50, + backgroundColor: '#ffbcbc', + borderWidth: 1, + borderColor: 'red', + alignItems: 'center', + justifyContent: 'center', + }, + text: { + color: '#333333', + margin: 5, + fontSize: 10, + } +}); + +module.exports = Dummy; diff --git a/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js similarity index 100% rename from Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js rename to Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js From eff7c8018c7cd2537eef3d59c218f8cf312d1bfc Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 29 Apr 2015 18:42:00 -0700 Subject: [PATCH 149/250] [react-native] Update UIExplorer snapshot tests --- .../testLayoutExampleSnapshot_1@2x.png | Bin 74324 -> 84258 bytes .../testSliderExampleSnapshot_1@2x.png | Bin 22397 -> 22605 bytes .../testSwitchExampleSnapshot_1@2x.png | Bin 88843 -> 89902 bytes .../testTextExampleSnapshot_1@2x.png | Bin 265951 -> 270778 bytes .../testViewExampleSnapshot_1@2x.png | Bin 87552 -> 89870 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testLayoutExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testLayoutExampleSnapshot_1@2x.png index f90bf2c6a3afdda6dd7985042da6844f6ccb5daa..a1789c7914b5cd0597d9c2db5cf909d8b6bb1822 100644 GIT binary patch literal 84258 zcmcG$bzGEP*DpSUq)02Mv;j&>hm?q@q*Br--3`*Ch(#JS2!eDs3^0I7cMdgxN)KHF z4Ds6o-uL7EyzhH{pL3pb{$ZGF=e74hajw4B}{SVdCuOVkzTxZvz6o+kWr1tj5!k#RQ^g zje+`?OEnl;Z;?ADTsPium))ib>0LZmc-usfBbkLWi1=CukLLX8WMiU;>`=1NMtoyE+U#Ky81CcaANfl~|=%mSwTs$+)|`~#ts z!Tpti;GROD__)9L|53qz*7|>($p7_j|F_>YNr%|@@q-wh(yVSS9r)Mwg!1nFXc1=I zirs8}7s;b%rA2T$SHi3mLbx8({_|>Ez!A4#YU*@aR@O*=e?K0m2O|=DuYa$zq(0$o zkvc@1kj#Dft<0+OK#Y<9!2sngV%8VoJMRQq5~kFgGjpix_V=F@`!sMA+oQa!-{XqE zJbNeCk~*cKa~T#y%UimAAGDynb&;;}V>|~ZJ|CrRo<^+@3IU!GvxbD}-^bt+KI}dh z?+Wd=oG2<9=)Z`oewRjFSIZvNiouH(@%YTxf*Y3dRxGVp#29#%QE4j}A9FkxL2hS` z%5!*kaplh;mte=;60`gV52%mF!Vf?0p5>%NeCUa|^N*VkDvDp}lPb8Vy2iv1z6oFuRvJfhnllg;%IGHLw^fq`1-VFCo-zAu)rl$Fi zuYubY(g&L#!c;>q7TXTW1a`UV1`;{;3I%r)$ki(s zZenl==8kKv*yxmqlRnz*n#rqL<~L~!zBec7u`(_>RdImDxlU`_ntK59o-g!)Dy-5#L~Hk$<&LnHHMqHNTjzVPVL-DSVs>8Nnu&4mHZ zCpBSGdl};src}l0*LCpx z){y6m-CpKr6zJ(%^{B-%Y6~A9C-XZHZ_IK+LCMnxoRQjiL2uu-Yw6jYE9&GfAA7j6 zmS6GnZMa?M&5icgZiBgc7MOu<{^e4n;Y@l+(jFFC5rvrb?n94>JYt^ye8XN*bUZ%a z0p8GOxmU!G<0QS^``wT^IwmUO^IyWZ5`ETN!rl7i=?(9+=;WMXmbz(b;3gLp6{S$U z!ssz}!?Y%VT)cUI^&74k7`#bEwd*yzC$&&+%9~H$(mmf?g}YTQv?Q9=Ga^J;bh{6O1pOOr}oxK14`*0-SxuLe-Acbg_kgvTDno;C{F}`-%%aTx`+rdlNI}!ir|}n)^&rTU$QiCt<2bCQ9NjXkdcB z`cYlw_u=M~AaLyca*qo8!LdP64qq+o*+yO!Pz0k`(yi~|Kt_6f4UU+J@spQ-{R)!~ zwG7G0&Q_c|2$cRY^@2v_^&|Fusqggm6`#istjTX3D=b+^@k0o+|NfenLd(n9h3tHl zM-ZW;sc<>+Hewnka(5jzuv+g=h7+0J|)y4f1Vd8CZ*=RCPoka zW`Wl!(?G=p@8o>)Kiqx2#pJzM_?pMOrt8rKA#08Ael+LmBr#(jIB~W|781t;vmSVe zv6Ab#-LkN}xr}bU92Q)m!1IagD*rW;miJf7&?18n8p(l_GU3D7RBx(pgCi{rksa)?2CQc_*@ z)}S60YIuvpxJ0YDji;(zYRPn@#A=)A9?mYZz^u;6kTrKtHTOh1)NBo!uhd{dD<-%v zN-P!xFe)=}dY!(TW0$TJ^S;~L2<~dt-jX3-y-Vs)d$3)BMf?htNVZRpY+LP8$o=vI zL)%C#gR0Hwx}jBB!u)sdWztgA&t9uHSWsCU4?Jbv+ zs_N*(SoNhTe_eSn-T&eCLXhsxlhWnDwuvmw>~?uNgOA{3gN0!KE#imNGSeGf(qZAT zt2__LEFG6Wdjoqwe+AZRSB|1BR8Ch(xVR;D_m+^8>r-Uzx8Gu6oJa3grQB4f z{Ex5`(fm1Y3adA>hG#7N4?G!R;3Kc?Cd6c`%)nLZG z-6<7HsLD5HyUGI?1Y0o!+D4@YC2P*VLPYM|9rzkZ8mD2{LA0}lG>VfAAdIm`!DH5b zHN}~=MG9FZdfi5C&i8e2=2t&SZ}D7fpzBy1F8;8-n`cxz?)S;l)3eN5bR0&1vS>Pt zA%mLP`a8zBlLCxRL5%t4Tn|;QzgGwj#Nb11*&b-8(s_n4K~n3lx2x~It^!A&b5&F`e{YlwLvq|ADtRi*rR zpyv?b6ie$5?n@&vV4}UDn**1};I8ve#{G|cJD=dH{6B2Wn2p@ z)2W95Sw^f7sB!653l3QrD0EQaUP+MO`MQ`Uhd%dJyy!$D+sOP!X2ABVE}azeIbXx! zTpN6~oHN4OJbF|G+xI>AaAVy)I04O^>fKv$&d?IfsTsOqdb$CB16^Hvg(6E)0USu* zmv+8V`99t%K;m4ChPdv10=2WW5iy$CrrCIDs6GKCXe1-Zi$#ua!%L4>a-FZ zyF&W}c*LLx2Ek!?$npOWViw&0EHK>P|I>p1to8pmk-xeX{`58ZVX?C&B1Xdh-R=zs zDcoGY%XVJzcrRy`3dwtvDb^+Ch~dX5y-pgaXu>E7~{SML?gA|P3H z&-O1Bn+~7;V5DYy|9+eD?gGxgg}e^np;SIgIHAmSqYk@VRGNy)&2ogRlQ(R{=ynlzzqJ6+P^; z(Lw(mSgIfPN66YwD~Q9X52)gwSI+Yp8`rF?i4ZZ{@>DJI_f@)tdfMziRX0$lckv(I zxghva`G+Lv7O$d(coIOLCje&^6BdrzS5=NCdM|q}qfv0lm~-s1M~9~_?tki%KEeb0 z#IkT$v?j31za8Q<1GI9q?5ak_v;%M$Uhg4F7mMc; zMI^)?24$!p(x`|+aZWA1qdIk6b{=AcDP2k{KhshRPt;?)I(*v8-^g#P^n$o+^e$?} zkFIF>84=U@^+S1ivk2_4h9*)_5!$|u0S6r{>UlXxe$6tR&|!jsfKQ(g*qG4pE$rsf zXb#WY+sCdr@O6Nax8dNhvl#()+vM=+Ow3G9ai{PSv)>STo?6?(oxM{;Am#)#UMYr+RjxqRd_RNeQ9hToq)D%th6Gq)!yDid}EU83){Q=5eC(CUlvlmK1AR}{>T4>5J8X1VNVli}RuJhADI>QBa1 zccO|r-=5dMVb!a^iur+Z*QSG3;z@fxGNLNa{@a{4*qB! z_44)Fql1<31AWiw2<5#T29E5kj#Cs;=P~0Qz*S6++XJ`M(0(*+dPKN=v=mk zXNMwlf%zTuJt+7_PJw8u4!2|WEiq}PI2v84{>9^MG8V_$_VSG9RWpM+!}|B%MMrOL zEEbc|%9L&gl7?J{DevaQTj^F~CHGe^w@(~yi;O1IIaBScAlBK5Zw)=j@V?^P_4dOT+j z2FR;Pj@tX5w?Cb!Ab-=mqI-F}vQBQB822*(CIbBrorQe|JY0JaW(pfHT5!0t0 zKNFAEqbQxOU*qCejN%pnEkTwLlVVIhuGh`VUiLlhf0R0 z^9-cGK3s=cKMrBb0h`-DGULlT9x2Bw-*8oHlfhN$Dv3d^TK)at{c?-FvxG&$uacyRcd}Qm>EmRHt4S*adVv*XLpE=U1;j04|cnj(eSYR!fEt)ibeAzR9T%``O-sf z7#1u`a?@q$R3%YuS9LHDL)RX8U5qxNVkg-CI1j|^mG9~R5C2mHQ!nUMRN{RZ;} zwH}F5B&vZQUXKY*7wZ-nKfXKsL^3tSWi~#%65pg0!UX(2vMh@b>+R51a{rOn-%3=a zNw24;)yd+BU#P7)p6I~_?-;Ut*gP)dvU}~PE#>AbP1I5rF?ZVTUyC<8+G&y=^Ioe~ zZByGN4SM^51LnmL*P3Q;v;rYhkouZ_-3q+sR&no(u1>VqW*pOpS(MgnXnCAYSj+|w z(quf|47*b6AF!Z6?O>#*wxeWS&(T+v=6x*%-pqD;` zZ<>|WrL!GsvE$^yYvatj&PRu35y}Rw<8NZOUXX>%Y}@C*iL6;sPl-tM|3M(}RrDQZ zF2STblcK}!TH#V(dA=`xf(ImQf#Ue+6~vsLd>fIySodP@PDz%0*XUc3y;7uHq(YqG z7lp3$TZ0cUjwS?rRDywT##QB{18x%(6edQL(<425?^e&Qgx0_(gs&Yrkvzdd4;Pt2;;_KAsm_FSxdP}GZ?2fyAc^1SiV zU(!Y;j9bjIi6p;|)c3aWItPFT8W^(dFCH@vI8@ z*7NJ<<_oeF4Uq+1m6A_^Ob+Q<{i`O~L5~9Dzv?laoHKzdu=(50prcW%FWTwIb;HATotK3DD14`r`e>lTO`@RhV3a@-g|7GIP!pa2#K zZ|KZ1B7gu$fQusFzzX>I*H_$^|AyE9pQ`_EtrP+2OenAW_%ZS_4A(=*S(y{~?>La< z1HfqTztEs_Rt{BNYb!^w>+t>JwB%M_8e9W4X=iq^j{rhO2;n_aSGQSs9%uyjNiEeD zdK{UbhpPb-ZH}2zIdLnp@zr*L26*o9rM=y#pn0mpU~;?}hx#F(n{n;{=<0pM3H5#J zBJ&9aj3az;R3MHjKIXLlvj@K&zyFRT|BqPr|H9n=cO(3pm-kN^-gG6(CqbvowsQCB zx`u$`s@jGr=2IGQ%OP=R#aD>PnauM~8BoA^cI;)qf_%}LMjD0aRGVgcy6od`d<{D2 zaN#8r&JXtKt6n>_m8e*CzKzC-+m$4_;iIcj`cI;mots%7gh@M2 z%6KqG8G2duyTRR;tfN+N?DlM;|BQAhCH1=j8;q8P3Zb8E9Rf7ES*YapBLTY+!K!iT!!-(z zVW%cWi5VH zUbV%M!`z+=2)8Rs0GsobmVTb@hI>r-mREJcpr^*n7K+?-$p$nTKH3wu3oU(olE=kO}t z5GM^i3F2-COe%emKDV<1ljj!$zq!w^VoPn1cTi#Q*Q&r((4MijwjMJB{BxeK0jhqS z%K+Yw$UBh011q8WGDpB~|7p1!-(VpLYn-{cuK<9<`w#)U?bD-g>nX%!`nr!ti^$`} zcch`J%g#rL7ulE{`grUDn10^FD8FVqwrZKq$ma**uw9^Q26-AC8~?g@af}bDc}M4k0M7)tiI(lvQ8b<#yv-AsA87s_6v32>_e{@;AxMbv&B4! zF2k6h!NL!XO#YZ}XtJWY=jg?>P85ELq<-BU1A(?ZO zjTGXkPK{S-)v~&(jf`q_K+v9=pITpz|;iu;_F} zCh$GJ)m#)vN(bK{pgnswn(FeMA^!;=2g~9r9>s(q*TBxC9+Y4CNO{ZeaF)ngI+_AH z_5xqciO}Y5<7RY)T4oJ^DC>8cfk%j{;Ke;A;`U%QQzJ2*rw6}9nf*T&v@NqH+PV8Xvk>gfIM z{;Nw&D}>=QO^x&LzAfzpJPKKeFBhzph}p*&@Vm?&sHO6Waq>CG@7?dd-7snVF8N6y zv!t8B|Hm#^Be_q!IrW!bP!>I1quv-(4CugQ`^_j^VCk3xou#8Gp=00i)vj}K3F3m6 zAfy>p71CYfbuqKbcDTqZEZxdDeyK5(k$zX(&#e?YC*np#bL;i`;Vb4iZG{k#jp#iB zmIG8>Jz51hK|~`Bf(1bkVPWf-zAv3#@${@OzKHXZoqy8kyDH{#B>UB;$|qq79{*0; z+K+g@)uQ*i7}V`xUi>yh#0dm)!d++H*sI1;Jg3M~fY4ASZl!tD(s6C&O4vMz@U+`+ z+FGwqm%dUOe7ORJ#IpgG3NR+JC&9^mGHl{t=T-^+%m9C}#= z;H%}bbW%*@+$r-hrKe^fQBB6npuL(qn!VzpCH{mpvz-vSZ|)SLH_L(X;Pe||d7SOn z(JZ!hkY&^!PUQe^Qo0RMsNL#m+;Umm_cobGFlY0V7U@=m)vcbyjxJ5Eqr9M-EMhH3G z>9>DuvD+*nbw+2qB`OGI1^`N1gyAdR_51A~P>DrtnmmAY1IY>ae2NzeAACuE_ZlKK zP?cyPq80U|j3i07EFP0z&pa>1-cLZE&sie{_1SKarLP(@3hQf-{GQjhQhCUlPjR90 zb7~=)24yRjJQ1|BdqleKv3mmNIusyoVISFu(8&v+j&ZY)m?yQ|%*_FgTaSeq|@_L72UXO(ng zk@S?@(=6=?pk7i;2Rbs`ens3+$IB4!!S+F3&SGny5T#^9phwT4L6Az)Z|}@2#CDn9 z?B(&DI)pIK&e@=s@ifYrG`?v+wpf`~dQ%Od`_6~f5nAEbviCBX zj>mdD^~Tq5r8EOg&0+HIt7N=Ouv9d@dTGdVF#tX!F>#ZC}mkLKg`Sf zPJfZR_X2@O4fOJOE~e({*Q5u`LkFmhMC{KzOyhSk_ZsXWQfg`|%;FqdD#O~`#!pA@ zMFnQ>OHYQU#8PKcwVOuaZ?`?ARwpmdBv_H&D)THt2HimKk9!{>lUy#Hb8|jAVk*P- zVc(X9Mp`8tNe%i8)qVDSV{rH3V5 zUz4eey_5s58<9aKjhL*D3OrYeJ!j&kZ90&OKLvconr0g6^J5IV$O}f_%mY;mG5~~(75xlYav;%>**#}lSvplNN3KH(Z z6dq6eZ_?lF%pmD&kH%O;M;Tr)5trJoFi1c678YH|+#Y-lEx=3W7Iwq>Yrofo-lgj8 zP78N`My92+9yQ2`gRwj$I_A*)XK=%It%@h}%82C7@EPG1)x^Y){DA_-J9^uqd%@qL zB&8j{U}J7>tg8v^Eg5s7UKzzayWAM3AmKK?6XPN9!D6;*)RJ*PA#s-I=%|dh#@fhx zcc73=oc-Yg_ONN!QmnezliXj`4heF57z^3?iy~__0(?6=dnzwBw zoEoXi+iBSR>B_k}ExL;_8zfOiz=}!RMJ?MHgyMCT3&sHkZH00%my~Dtm8-ZAWg_K+1dAYoj%z9OX71 zzhlgeHR{gB(7hURGh{r- zixHQYw5L%iUsfH(TsUw+TT1O(>x>mu-+WqPkdIaOrQxZ~lQ8ff;Xxr04Dxx;A#6I3 zHtqaEW=A{J$ejm|v~0{fvIQ@*MZ~Gdz3otz#7i^lydQK}+c$ZsN(pU=YGh*9Q8R3f z>zGD;^qlHZ2*_n1?eKV(*dBlG&6=#^9;qF>dAlBC5pZp9^cAr`m(D1T%nN~s4cFg` z@Frwm9q_7Db)_hK*$;0cJi~}y_TPMQ{YKl`gH=T7k&%P**J%eX2YhzM!9sC@751QV z!^By_ifOOyY3+_tbp%-%Pp)tug1ew7jy~S{yUyXBL?T;hu-u(mI}BDMarS27ma6Ra zc@raM!JoOJiL=z@`iXwp(W^y!oo_UHQ_&x`KCyRtZtvIqU`toj?kiMh-&p6&kGWBg zC2Xq3>J+5%U$bYF7Ueq(qTdZy^S$t@#J9Rdi(*C6l1`+DiSN?6MCX9Dm-HIm zdKv0jJ4|dG#s0~)ZE$yu$NZt=&*@b+sr@I1R10(e=cQjGdz@iYBlW(cc+(*6>kYQCLcbGtpk_v-Sp?MiaDKl@rGW{O)~ zlZp*h=ecnb;YgzRK0bOU`75L=P6z z0q8Qw3ky0Wl!ncq#{NR`mtU!8?hututEAr^FmcV)d2f`kN%M03;kYdi?-xVJK83iZQ5DVS2mEpbpg5;^{9foTOjIn^B>X7B`--Y$pDbRxJKY; zU67h$$6M<6G0^>;P{bsnM0H88>F!|5Ezj!?!cla+mqBt+htBpeMS~M2B~Q)5;EjF- zgsnGM;^t02;p{j!iCGzOlD5(n^31b!Ys?+a(z8E)vBf_4#g1&Nps*06hc05iCWWAC zE1fW@ShWNU-+FG?WqYghm>=yGqnNn?hi}Y0yfwIN%0dFi{Y~dSagibG-qdTJ((#Wi zC&XWd4)oIm{$3hYNKJB5UeiM1>1Ffb zAFY;H!g^saPnT)j{ZGBDdG`?7Ut_a2#Z6VD12OVlLcKV=tA;k-Imj-K>?b%v^&rx^ zgzas%O~M8wx(NXqCvd#AN|ZbOiy%!tT;t%W_}qE%!53`%uOA*OkigB;NpRl;s)&G> zwHG|3FJ7U_E$x;#{j|5e;YK+!toV+by`{YO03BWiRqY#B5tXCDsUrovTC5caf*Jf$ z(XP_9{N<)dL67}>F52-~hdH!g>|?VG`N}~yjU=jOf!qV`F61RSQpW5jHG$Id2T>tn z<9_>M;d-qQ*Zrqn{0RWQjF z#G4xt!I;C^)xk#IfJ<3LZ}RDoY040xWaM@AYT6V!qOh@o{HEt&?4;x`?`aK#47Bs- zeS(KyUcdk8ZKbM%cHvB#O%CLa1WsuKYT-++BGTTe%HnFRD#pA1Ah>!q9{&BPmj3CT zsj@b~)ShB{P7~kDNGcrr;`YsFbUg6-l5h|*AyEhN~^=gp_}g4t$9kj zHtsLo^yqXxsFP5)o^ov|<-VkXjPnZ?Dv#X=$gwYW{0!bgfcupP<3Zg5(ZqZ#`e3n* z%FO5S#Qoa(-McX{G`dN9{io{< zhiHzO;fw%feh`sDIqSZNkL+d-pt(WnFnslG4_m8BAY?>r={X9n5>+Wc^f_(hL3YGO z?1Jqc#Z@u{5#KwPOCRto_14Gtu6*%{-RO?Nbjw~Y8c3$g^?sCF+UXUmdnd8*lg6*= zG=D{6df+=~9n%C0h$5Pw?&PM6L>f(67pUj&r&NCwiEAT$^MQupLMC?b;w4xo4fP_$ zWFzyzDzkUGnY>GiB%-i%<)|S)n{hXbKWho$ahY`_Aj?r=tt^czhF?C&yKHk3J~Qj`R!^+G}R0LV(j6 z3xY$RAzB&(G1~{E!kr(=I8rjsa0IoUMSn>;c^R@_!*qnv$mhwuYEav2a1j-eL7P=M zU$hrNw?ykhR4{y4YEaVLPkvfcB$4SxWX2Uy^{IZ57;VK9n>+hDdvDUf2HTE)G#}zfd*XIUupyi;80%_k%TDiCC!)6IJ zXd|u<-jhAi!WSFEJU13!v`uj71wr=kQFNL1{V&_uiH1imRt_5a!^x@Y%-LXpW~FHc zyvosfb8*aS5%Ojahrg;4pP1`z$NQ~XapS&;#Rj?)xwjw(u;qq6%ZIv0u_EyCzh@u- zjTg%27ky{v=gb@t@j}6_lsNL98L2euZ&Ud#lZ4;7-^N{~W@Up)Mw-&5=&_|as>T+a1Bc?mh(X&CSA<4s||te~Ft$H+H}&HXJtEns~d_ukz% zD#61OoE?-lu)sAlTZmPFsB^)%U9|RAn_NspbpM?4&4E%k^SOd|=5x^)bt!h1IyT9j z%TImh#p%UuU(_t)oN#6zu*Ot%!PM8T@vCAMY*y-1IM1^P8PviNt_kyAZBtvJ5c{tN zbt0HErkYXAU@f*H=wds|CuU>V1G*FjXs(6Jz@}Og^EeHR;GO)wxtC#?O!)lGT_Qbn zcg8nW!4aU1qI>^slh-`f(AVh9;UjKYJm`Bif5k<&RCF5!QVcKp36vq$tk3ed9-3O) z4dzwGe5zSezwX*aeM$~;QDjmM8J2h1Koy?1?+g+etfGnG@{!8>ilFzhNV@kEj5*#B zhQ0?VKq&%}_ZczX6*fP(p(|<*Fw0Y~?2Yil73$YA_*>gjCRte5NrO6Gzf0**_x&W~ zpuE@XQ&TZ=Sg<=?=mhceu8zy2Fkt_?ZMh9S?o2Gm4F;gMAl|k6uq-Z)e~HJJ87HUW z+JpM)o5tau@Cj1${vFl!kHgvRIjL?l*t(ZIJTT=8{|q`Php6V;;Pa6k%UntGTAO7n zjQdZazkarUq;#gKa-+k>WSsBdlPBv=&iG;d>~<)LRF~^pPw#bxjrvVb^WJuONF2WS zgxffJAL*8amPH1hOnu)+5-bsA-Ps~ElayUWGK7ycoXy>eE=7eS&J-p2SV z=9pQdi>B}?cY&~*d)MmN0z17Ej^O03l5_CpNSpIer2xp=Oqc$*55s?GeS~obv_718 z^INtdAU&+R$E? z(7(TT?Q)jM+#_*w6IJRH{eVki*t-R3*qNlzMUU-&bWgXM5O3VMvj6l1@DP9;k8V1= zIoI^kB~44+`Nlm{DQnIO-X(Rcih22k6Z!r<%g&H3ltRQ%i=9Y&F|B2<;DwP_zhQ3T zKK;z~W7LfYqNH=A(6cz{T3-}O`&Rb^0OKqht5rn$SA2h^Q|<n|y?{(M@wJu}@RI^4EF*Mk@L&RKF(e zYmPzrxL+ME@acC8IODRA4_vT7`K#Lj+<@f(`H{R+*PPGc^Sx<_tEyY315fI@nsq2~ z_g`?gg#H(yMcmby<60-I+R0s-|Gx(!nourYUJ*Up!oX>8q2#CS@ns>ritEIa=5coB zC)x7``fiFH&dL406pYAPCJNl)m5oR67MiDf<` zzg@HbUoThu8{VEX2l?nTjr0zJAR{;u1P%4tUAiPKK;~=HxjSH0Szw3f>hfz3M;O4J zfxPfQm+w>x(GG}RwuLf~tULqm)EJD})$T21zaUWiIbD5tfSG9G+5qWa=18%Pb^$rV zDA>Z*_R?sas`NqsB1kKtG8+MTjUBklF370WMh+~?ZPcxvE^}f8I|>%OIgMvN!^<(#&D7GTb0%{K!_()ZRW%Y;P_FwLan6 zr>t*=%Phv5qQ`d?6LcZp1bp{gJv_n1peM^lE7Fw2w{7$D zOt~klvvZ%gKRdZP(Eb`iPA}XZ5wl7w?$9YV%TUz`@;w(3xOB#fQN(7i;*E06?AT(% zmGPQBHW&z1m+DkFKJK>*6JNMi)#-;_qT4XU-3PjM%j+9~TJocbSzKa|b1E+9nK;QG zzk41pVklFdSF5$PH7A(Fq6S1CN9@FM9Kv7swPw__&Qr^j9yYusw6RRUB^Ta6?t+Xe zV`L93+!CuDWS%oOEhUCHlnXca>0n;-`c zp3YO((Q@m4AJf7JTQj1H&vT|H`g!R?gE5E!?#1d?6w>?QoVdhyS;)a<7zia*MSR5l ztsh+mF(eWP?2t)VWdKJ!57L&d?kQH`{H?1{Hgvp_IRXRn^*a)88pzSycRstDX=xdrjINbcf|VXFk2 z{F*^u1N9ustcX3+=1Of<)roEfA8?aHQ!@e!FAJfSwC>O$!bJD&_=hwc6`52*=$4}{8ZbJKOYevz9b|B+1ev~@X$_C^bygk;^;Oc_yV7`$< z6)d5t%u{TuU%dlt_C*gYxYHI3az4xRlEl;z4B(c|3=qc7eAdilpXxiLcdy}{8M_TpsMg+;cAqa(#Qx%ZA#0)iI# zb5q>ty%|8up-o($Am&7D{7)myukCJVrt5xPnA`budxRJi>!7%Lv=tjUKcTBVYN@vr zKtm6jQ_3Wg8oSvg#PFwobv=J=xJ@J%9vK@MMn^c^dD-^{$&g!VNZEi?3GWhcL67S* zpPu|F6EZKA^}SWEZR|aMDsSkS8T%pau%1-&dwxRfVxSfa%#XS*vpy1DR@3jLO*_&s zs_`7#D^=>i7(l~#(hKqA&ir(mn|;ARzT$)R@4ROK$JJdZI*iNCg0?3?RESUOv^9|` z1+iqlmnLGAYl?;8${WhC$MFAVQNVb}L^$x?>yB zG zIv2KErlb!_aJlKxm;Uw~pY7Ypm_3N8z?A^sgpUC45#Yr~wm=?YAc9Kf*eR^QC2`>j zTku3}49y%b&FoJ}EpU^By|tm@`q0`a4~lW~yhmI2n5K=Nf5qt11|B(CWJnOMSCHWa z@DC#qUFudQGnuFO-6mOieSA=Wn}{v6IyK$s&e5GvLapIH#u?(bNTrx|FcEPqu>Gs@ z>*I7a@Y=#ip2;|G!oRD}2gnfadQ#qHrNeCxsKBNNdBsn527FGg*xw(!j zAU2*M=$8%SlmEzzzhU00YI0y4U} z;5&fcu7iI=Z^tJfDI7@8RlK>>f}FwjSZ%A9s$2r#)WpPj;p^==Yw2ztc`3J*gbq%d z;(iMPpReuYX3_tU16j8SJmM!0E9V8jh(4jbn?A`x$!$W3JIE|BrBmPD;1nLQv9J=p zB3 z8Ay{VuSl*-K(3#`+xP5#C-iee3$8h_F(F`t)tYss42^a1??nKa281yF66z8O@=V7=h8fSv!LE@;~*5apH}uX`1$#6jKhT{&CPL>%JE0_T@#f z`S5IpK!AJxPc5Lc!2!s~S$50=hkH*o^n;9JG=zC@Y6~`7kA&KjvjDSSzbnfe z=e`1&E2wmbPK*iE{jHpsaSTa-%<0O%wAnD8=tnDKFa`uVw#oL~zW#PM_#XyYzb48= za3V1R=VW}FeH%YUYgMM6Xv2Tgu6>u9cvGI8D*NY({#MFqX7=}Hs{8|hCeUEx!3~6a z|98iZ{pm-*v9H59_J64P-)7*Q6{?iW1TVuR`wRyuyEJHbqU#%gWwRQs;=8AU{ zlDcM^>$0If!k7aN^{;ohfn*>8yf76H$5F$;vuEwSbylJgb@Cx~~cU6%m`feXNbUiSR`Iyeh2{ z=xw*q4?o^qBbCDr-f-noaX;=`;2MnP`K`hAHN3t{ceAE|Om`Quu{;B1FbMd4hk>i3 zY(pGTnf>^0!_gk}_&w@#kLUZ}M&jBlWf-!z`SM`mahgxQ5fqnV| z7092k)t&LfgVGM1#t7~|^#?Zi`i)#?MPC+w`hOhsXAs)Rfmrt%!5=Q49q`pafB(<| zow3>%;$MqNSB7~heogtQi{JL8$O+ukJD%rz0>`26MZxXZsOD;z+`q)T?h2`KQ@y;E z?}CEdC-Pn=HL5>szQg={R8Q@@lxLcMYMZiloq4(4(IGMhF8aSJUxI;gaIf7_O1d-i z3HrBk+G7cq^<%<+*!-8;XrZn9taJ)zoQNmWM5tUz*`6P(Y8<(FO%yx^0fkJ!RWAQ# z;*YiApQ{73Yc#Ex-vu&;f`6{)qZ6gf$L*rXKXE|M70{Z@?}R&B$NK~XHz7r9FjtW8N=(>W(q%<#KVp6x2Zivv74YWHoWWsmmCowNpXy{j zqF6JcuLFREf0Ax?NGRmo>M)(JpH- Md^5RYB~lM=2M!n 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 eB)8tE?(# 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 TJI+Gj-_OU@e@Z)QeB*xr1a)}Y literal 74324 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

i(KLg#AE%ZZK<0a{Pq2&6j-k)QJ?w>J1f6R!U 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 _8s|#`&HT8 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 diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testSwitchExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testSwitchExampleSnapshot_1@2x.png index a40558413378c64e091161c80e16d4c8d4d7151b..3ff3cef9e45888640e462e888e687cd4d3a41c09 100644 GIT binary patch literal 89902 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

&Yqb;P{5Yqy0}7UC_T!5CD)V*3T-6 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୵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 Rlwz6W?BwoPu!H{ne*h&hx+(ww diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png index 5b71135b978b713353538cd2227de2bfa33ac25b..d697ae5b5e3e17787331c45592ba574db67d4f7a 100644 GIT binary patch literal 270778 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≫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�Z!#nB&^xF0D6X{l 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

paT*3{kpEwVxRXBUOR+K^#7xp+UE^ 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 literal 265951 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 F{sme%5uyM9 diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testViewExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testViewExampleSnapshot_1@2x.png index 4d16edfb23cf5606f17a116dc9ec4338e2b3546d..74e03581c464bc6a278eda27fb8ef9724f9a6bb5 100644 GIT binary patch literal 89870 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

9q0LP}Z(rOqtHzE>bj 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

RhU9-`xJ{;Ro=#ztN?bG4oUN&9R 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 literal 87552 zcmdSBXFyZkwl17dR8T}gL{LB}(tDGRSdd;ruOgk$i*ytO5do!22k8Mq?4bn9LN9j(eBX1N9?w{)8w(`d$=^0E9POz7yZK7 z`O4U-FDfwl<9nv^GANrb22F}<7&J!5=5!7B&oyb>r!)wRyMPIBp9KHXgMY2`f7_A& z7uBXK68)n zN|WL#sIr9)zTbAVKQ4un(_PXe-^Eo$IJ7wJ?m>a z4<1W3+b-U!sE^|iuC{mF0nP=^+yU_K^voo@ z4FKWcisD_ueO|gN{bprX>O??A{8v`(@2-NL&fH`hc^`N?H|y9u81m%pB4T0(5QW{6V6J>{MCff^XOe)BCyDR3#JMxGoWt~90^ z`ybBSabC^XIVjM~SEtrHwMcTXpY=LSAw$OdA1(Y^Y#J%hj>KFBI;G_U-WbR*TMZW^ z7ql5EVd7m{3sO#gz*_6eoa{(X=}G1Ciz%M5Ul!qa{97-?&+7dhLAG4mGWl4x<@7*O zu8+Q+8>Rk?b3ivmlZ^q%IL?qs$J*yT=6N=pqcm(^Q)x5GSKM$wjdzKOK7h^bvXKWA zStXccUzH%TmKEJw7I>8pcu}FAt6v<&ft!|V%@6QuutLb7K1@fNe*&eEPv=GhhKp5L zAJTg-K-CI-v@|}aI1V?|kl*EMSp7WUIm%UH-W|t^k;HEEU%CrSe!y1>X~aE0)L2n8 zrax8UHaO_@<#{>gv?zMl>S-_i_I*14BOh!r-$Q|Ep-kT5#d8c2p4)>R8x=@< z1VinS@9tch!E$f)ta6iIYq;>_c+=6kZTX8eb$*{c$Kgir-8K<}kH00?E7E5Rk#^-p zR1X}}6L?HN&&(-sH}?3PwnkFM8vm_Fuhv(4p1@AE7Sttj7U9Zd_77DewOzmct~yw zDRR}DL@PP#*&l4Lb()KMY?6r>OLjKAGrs*27PG+3^71Xe!(Z7XfUd=*fM9Gj(0cdIeZd>DjNZ&qlIUE*t7N28+HzEcdBcK=It=p&{dwAj($2hTy1!g!rb{ONriq>1%<43 zaGv7tm$ZyDA8~&cl=)b*>{jLUbgfHo?Mg~~Jjlp>a)}kCPT|1S-FVWTybehPxHBL z1^UD~?_S1~!Q6RmTz zUzIv~n@pfe&*QS(vnW8q{5i=^^_ut2Mnah%#0^-(z9Cn4wqTHgsi& z55bzDai?`bGDt3>pSx;9o*2&RxI9pudim3E>I6V+%)6tqOll{;*_dp{n|!;(v8Zuj zjT}Wm{0mojMq!Zh;(fe3vLJ|8TthpVln}973gt?TCAoBH1$;DJQ8}e9L!#;g71d@! zRO7F#IYlXc5x2PTbUx#;=px;b=pj{oDeQq8CSPwST4g+^NI^ot_my5Hn_E4k?nivH z&rxSCE0cil!M2?8aIYu!BHjJmf;Lr1^hx+2T_lepXf#1uW?7*?zW6n=DC(YYB*7SD zL6q^~`aLwlBL-AAq%yNNst;k(QIWzFQml$?Hn5x1u=x%@@UWMF&1zL`gDpF2hjsP93*lyG~5!SYc zc~A2brAH(fQ^PPnUd$;;p*G4j7%|)YeQ(8?HxC9amB+s;a3iA1Byk6C_lrByGv74H zR=X3(AH?33iwT<2eAAdFe==R?YUW;S>$h9C$g0RAb^1eLIQ?OuYAq3Qy8TaBgj=!O z#sq5-P3niEJ!s#EB!^x7F}j;E0RB#7&q!2~%uYDcOH;NoZTq5xQPWWg8M!n~bP1bW z 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 G@c#hN{;+ib From 08ec846176c57d8c3d7e941d1f4104401745b894 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Wed, 29 Apr 2015 20:20:15 -0700 Subject: [PATCH 150/250] [ReactNative] Fix logic in popToRoute --- Libraries/CustomComponents/Navigator/Navigator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 6d3d55c316f097..538c355383f449 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -1062,7 +1062,7 @@ var Navigator = React.createClass({ indexOfRoute !== -1, 'Calling pop to route for a route that doesn\'t exist!' ); - return this.state.routeStack.length - indexOfRoute - 1; + return this.state.presentedIndex - indexOfRoute; }, popToRoute: function(route) { From 0e3d627ff0764508e69b832fbcb5b36d8b0b6fd5 Mon Sep 17 00:00:00 2001 From: James Ide Date: Thu, 30 Apr 2015 00:39:37 -0700 Subject: [PATCH 151/250] Fix some typos in NativeComponentsIOS.md Fixed some syntax errors and tidied up the sample code. --- docs/NativeComponentsIOS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/NativeComponentsIOS.md b/docs/NativeComponentsIOS.md index fa467a0cee4c92..9f70e188cbd680 100644 --- a/docs/NativeComponentsIOS.md +++ b/docs/NativeComponentsIOS.md @@ -87,7 +87,7 @@ class MapView extends React.Component { } } -var RCTMap= requireNativeComponent('RCTMap', MapView); +var RCTMap = requireNativeComponent('RCTMap', MapView); MapView.propTypes = { /** @@ -97,7 +97,7 @@ MapView.propTypes = { * angle is ignored and the map is always displayed as if the user * is looking straight down onto it. */ - pitchEnabled = React.PropTypes.bool, + pitchEnabled: React.PropTypes.bool, }; module.exports = MapView; @@ -171,7 +171,7 @@ MapView.propTypes = { * angle is ignored and the map is always displayed as if the user * is looking straight down onto it. */ - pitchEnabled = React.PropTypes.bool, + pitchEnabled: React.PropTypes.bool, /** * The region to be displayed by the map. @@ -246,7 +246,7 @@ RCT_EXPORT_MODULE() { MKCoordinateRegion region = mapView.region; NSDictionary *event = @{ - @"target": [mapView reactTag], + @"target": mapView.reactTag, @"region": @{ @"latitude": @(region.center.latitude), @"longitude": @(region.center.longitude), From 648cdfeb1829496e73b2669f5ac199a3ac1742f0 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 29 Apr 2015 23:33:19 -0700 Subject: [PATCH 152/250] [react-native] Fix example pages requiring React directly --- Examples/UIExplorer/createExamplePage.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Examples/UIExplorer/createExamplePage.js b/Examples/UIExplorer/createExamplePage.js index ab725e84465fb6..4bc933ebaa11b9 100644 --- a/Examples/UIExplorer/createExamplePage.js +++ b/Examples/UIExplorer/createExamplePage.js @@ -17,6 +17,7 @@ 'use strict'; var React = require('react-native'); +var ReactIOS = require('ReactIOS'); var UIExplorerBlock = require('./UIExplorerBlock'); var UIExplorerPage = require('./UIExplorerPage'); @@ -46,20 +47,28 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule) getBlock: function(example, i) { // Hack warning: This is a hack because the www UI explorer requires // renderComponent to be called. - var originalRenderComponent = React.renderComponent; var originalRender = React.render; + var originalRenderComponent = React.renderComponent; + var originalIOSRender = ReactIOS.render; + var originalIOSRenderComponent = ReactIOS.renderComponent; var renderedComponent; // TODO remove typecasts when Flow bug #6560135 is fixed // and workaround is removed from react-native.js - (React: Object).render = (React: Object).renderComponent = function(element, container) { - renderedComponent = element; - }; + (React: Object).render = + (React: Object).renderComponent = + (ReactIOS: Object).render = + (ReactIOS: Object).renderComponent = + function(element, container) { + renderedComponent = element; + }; var result = example.render(null); if (result) { renderedComponent = result; } - (React: Object).renderComponent = originalRenderComponent; (React: Object).render = originalRender; + (React: Object).renderComponent = originalRenderComponent; + (ReactIOS: Object).render = originalIOSRender; + (ReactIOS: Object).renderComponent = originalIOSRenderComponent; return ( Date: Thu, 30 Apr 2015 02:45:00 -0700 Subject: [PATCH 153/250] [react-native] Fix Chrome debugging Summary: Requiring ExceptionsManager in renderApplication (added in D2023119) led to a transitive require of ExecutionEnvironment, which has to run after InitializeJavaScriptAppEngine. InitializeJavaScriptAppEngine is the right place for this sort of logic because we control the order that things are loaded, so move the console.error hook initialization there. @public Test Plan: Loaded shell app in simulator with Chrome debugging with no errors. --- .../Initialization/InitializeJavaScriptAppEngine.js | 7 +++++++ Libraries/ReactIOS/renderApplication.ios.js | 2 -- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index 51f6809ccd9b28..73493aaf6fb029 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -79,6 +79,12 @@ function setupRedBoxErrorHandler() { ErrorUtils.setGlobalHandler(handleErrorWithRedBox); } +function setupRedBoxConsoleErrorHandler() { + // ExceptionsManager transitively requires Promise so we install it after + var ExceptionsManager = require('ExceptionsManager'); + ExceptionsManager.installConsoleErrorReporter(); +} + /** * Sets up a set of window environment wrappers that ensure that the * BatchedBridge is flushed after each tick. In both the case of the @@ -139,4 +145,5 @@ setupTimers(); setupAlert(); setupPromise(); setupXHR(); +setupRedBoxConsoleErrorHandler(); setupGeolocation(); diff --git a/Libraries/ReactIOS/renderApplication.ios.js b/Libraries/ReactIOS/renderApplication.ios.js index 39e5720f520eb9..16052c6fa69605 100644 --- a/Libraries/ReactIOS/renderApplication.ios.js +++ b/Libraries/ReactIOS/renderApplication.ios.js @@ -11,8 +11,6 @@ */ 'use strict'; -require('ExceptionsManager').installConsoleErrorReporter(); - var React = require('React'); var StyleSheet = require('StyleSheet'); var View = require('View'); From d82aaa7fb3a69c1ee736359ac53f3b8fd583b78b Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Thu, 30 Apr 2015 08:03:04 -0700 Subject: [PATCH 154/250] [React Native] Remove RKCustomTabBarController Summary: @public Closes GitHub issue #1064 Test Plan: @nicklockwood approves. --- React/Views/RCTTabBar.m | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/React/Views/RCTTabBar.m b/React/Views/RCTTabBar.m index 11ad47e320da75..8a7fb4a43278d1 100644 --- a/React/Views/RCTTabBar.m +++ b/React/Views/RCTTabBar.m @@ -18,24 +18,6 @@ #import "RCTWrapperViewController.h" #import "UIView+React.h" -@interface RKCustomTabBarController : UITabBarController - -@end - -@implementation RKCustomTabBarController - -@synthesize currentTopLayoutGuide = _currentTopLayoutGuide; -@synthesize currentBottomLayoutGuide = _currentBottomLayoutGuide; - -- (void)viewWillLayoutSubviews -{ - [super viewWillLayoutSubviews]; - _currentTopLayoutGuide = self.topLayoutGuide; - _currentBottomLayoutGuide = self.bottomLayoutGuide; -} - -@end - @interface RCTTabBar() @end @@ -53,7 +35,7 @@ - (id)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher if ((self = [super initWithFrame:CGRectZero])) { _eventDispatcher = eventDispatcher; _tabViews = [[NSMutableArray alloc] init]; - _tabController = [[RKCustomTabBarController alloc] init]; + _tabController = [[UITabBarController alloc] init]; _tabController.delegate = self; [self addSubview:_tabController.view]; } From eb0476074f0b0a2c8d47f858946792046e5f94e9 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Thu, 30 Apr 2015 09:50:22 -0700 Subject: [PATCH 155/250] Improved debug and fixed macros --- .../UIExplorer.xcodeproj/project.pbxproj | 1 + .../xcschemes/UIExplorer.xcscheme | 2 +- .../project.pbxproj | 7 ++-- Libraries/RCTTest/RCTTestRunner.m | 10 ----- React/Base/RCTBridge.m | 33 +--------------- React/Base/RCTDevMenu.h | 6 +-- React/Base/RCTDevMenu.m | 39 ++++++++++++++++++- React/Base/RCTRedBox.h | 6 --- React/Base/RCTRedBox.m | 22 +++++++++-- React/Modules/RCTExceptionsManager.m | 10 ++--- React/React.xcodeproj/project.pbxproj | 12 +++--- 11 files changed, 74 insertions(+), 74 deletions(-) diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index 348d04f0d03999..cf9440c05e72e9 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -632,6 +632,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme index b231b77ee84fc9..488c0077dcdbd3 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme @@ -1,6 +1,6 @@ 0 && ![testModule isDone] && error == nil) { @@ -98,13 +95,6 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictiona } else { RCTAssert([testModule isDone], @"Test didn't finish within %d seconds", TIMEOUT_SECONDS); } - -#else - - expectErrorBlock(@"RCTRedBox unavailable. Set RCT_DEBUG=1 for testing."); - -#endif - } @end diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 5b6a92682afbdf..5dd3bac5a89359 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -934,8 +934,6 @@ - (void)setUp _loading = NO; if (error != nil) { -#if RCT_DEBUG // Red box is only available in debug mode - NSArray *stack = [[error userInfo] objectForKey:@"stack"]; if (stack) { [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription] @@ -945,8 +943,6 @@ - (void)setUp withDetails:[error localizedFailureReason]]; } -#endif - } else { [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification @@ -980,33 +976,6 @@ - (void)bindKeys action:^(UIKeyCommand *command) { [weakSelf reload]; }]; - // reset to normal mode - [commands registerKeyCommandWithInput:@"n" - modifierFlags:UIKeyModifierCommand - action:^(UIKeyCommand *command) { - __strong RCTBridge *strongSelf = weakSelf; - strongSelf.executorClass = Nil; - [strongSelf reload]; - }]; - -#if RCT_DEV // Debug executors are only available in dev mode - - // reload in debug mode - [commands registerKeyCommandWithInput:@"d" - modifierFlags:UIKeyModifierCommand - action:^(UIKeyCommand *command) { - __strong RCTBridge *strongSelf = weakSelf; - strongSelf.executorClass = NSClassFromString(@"RCTWebSocketExecutor"); - if (!strongSelf.executorClass) { - strongSelf.executorClass = NSClassFromString(@"RCTWebViewExecutor"); - } - if (!strongSelf.executorClass) { - RCTLogError(@"WebSocket debugger is not available. " - "Did you forget to include RCTWebSocketExecutor?"); - } - [strongSelf reload]; - }]; -#endif #endif } @@ -1091,7 +1060,7 @@ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args [self _invokeAndProcessModule:@"BatchedBridge" method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, args ?: @[]] + arguments:@[moduleID ?: @0, methodID ?: @0, args ?: @[]] context:RCTGetExecutorID(_javaScriptExecutor)]; } diff --git a/React/Base/RCTDevMenu.h b/React/Base/RCTDevMenu.h index 8057e570869383..537675576c3204 100644 --- a/React/Base/RCTDevMenu.h +++ b/React/Base/RCTDevMenu.h @@ -16,10 +16,10 @@ /** * Developer menu, useful for exposing extra functionality when debugging. */ -@interface RCTDevMenu : NSObject +@interface RCTDevMenu : NSObject /** - * Is the menu enabled. The menu is enabled by default in debug mode, but + * Is the menu enabled. The menu is enabled by default if RCT_DEV=1, but * you may wish to disable it so that you can provide your own shake handler. */ @property (nonatomic, assign) BOOL shakeToShow; @@ -41,7 +41,7 @@ @property (nonatomic, assign) NSTimeInterval liveReloadPeriod; /** - * Manually show the menu. This will. + * Manually show the dev menu. */ - (void)show; diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 4af9d4e62c118a..529840e0a4fafb 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -10,12 +10,16 @@ #import "RCTDevMenu.h" #import "RCTBridge.h" +#import "RCTDefines.h" +#import "RCTKeyCommands.h" #import "RCTLog.h" #import "RCTProfile.h" #import "RCTRootView.h" #import "RCTSourceCode.h" #import "RCTUtils.h" +#if RCT_DEV + @interface RCTBridge (Profiling) - (void)startProfiling; @@ -36,7 +40,7 @@ - (void)RCT_motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event @end -@interface RCTDevMenu () +@interface RCTDevMenu () @end @@ -68,6 +72,28 @@ - (instancetype)init selector:@selector(showOnShake) name:RCTShowDevMenuNotification object:nil]; + +#if TARGET_IPHONE_SIMULATOR + + __weak RCTDevMenu *weakSelf = self; + RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; + + // Workaround around the first cmd+D not working: http://openradar.appspot.com/19613391 + // You can register just the cmd key and do nothing. This will trigger the bug and cmd+R + // will work like a charm! + [commands registerKeyCommandWithInput:@"" + modifierFlags:UIKeyModifierCommand + action:NULL]; + + // reload in debug mode + [commands registerKeyCommandWithInput:@"d" + modifierFlags:UIKeyModifierCommand + action:^(UIKeyCommand *command) { + __strong RCTDevMenu *strongSelf = weakSelf; + [strongSelf show]; + }]; +#endif + } return self; } @@ -104,6 +130,7 @@ - (void)show actionSheet.actionSheetStyle = UIBarStyleBlack; [actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view]; + _actionSheet = actionSheet; } - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex @@ -218,6 +245,16 @@ - (void)invalidate @end +#else // Unvailable + +@implementation RCTDevMenu + +- (void)show {} + +@end + +#endif + @implementation RCTBridge (RCTDevMenu) - (RCTDevMenu *)devMenu diff --git a/React/Base/RCTRedBox.h b/React/Base/RCTRedBox.h index 058759da7a0a5f..9a3a9b49a872ee 100644 --- a/React/Base/RCTRedBox.h +++ b/React/Base/RCTRedBox.h @@ -7,10 +7,6 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTDefines.h" - -#if RCT_DEBUG // Red box is only available in debug mode - #import @interface RCTRedBox : NSObject @@ -27,5 +23,3 @@ - (void)dismiss; @end - -#endif diff --git a/React/Base/RCTRedBox.m b/React/Base/RCTRedBox.m index 6268402526315e..b54d18aa3692bf 100644 --- a/React/Base/RCTRedBox.m +++ b/React/Base/RCTRedBox.m @@ -7,15 +7,14 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTDefines.h" - -#if RCT_DEBUG // Red box is only available in debug mode - #import "RCTRedBox.h" #import "RCTBridge.h" +#import "RCTDefines.h" #import "RCTUtils.h" +#if RCT_DEBUG + @interface RCTRedBoxWindow : UIWindow @property (nonatomic, copy) NSString *lastErrorMessage; @@ -310,4 +309,19 @@ - (void)dismiss @end +#else // Disabled + +@implementation RCTRedBox + ++ (instancetype)sharedInstance { return nil; } +- (void)showErrorMessage:(NSString *)message {} +- (void)showErrorMessage:(NSString *)message withDetails:(NSString *)details {} +- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack {} +- (void)updateErrorMessage:(NSString *)message withStack:(NSArray *)stack {} +- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow {} +- (NSString *)currentErrorMessage { return nil; } +- (void)dismiss {} + +@end + #endif diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index 878138282f9d1f..f391e6af0dbf88 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -61,12 +61,8 @@ - (instancetype)init } else { - // Filter out numbers so the same base errors are mapped to the same categories independent of incorrect values. - NSString *pattern = @"[+-]?\\d+[,.]?\\d*"; - NSString *sanitizedMessage = [message stringByReplacingOccurrencesOfString:pattern withString:@"" options:NSRegularExpressionSearch range:(NSRange){0, message.length}]; - - if (sanitizedMessage.length > maxMessageLength) { - sanitizedMessage = [[sanitizedMessage substringToIndex:maxMessageLength] stringByAppendingString:@"..."]; + if (message.length > maxMessageLength) { + message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."]; } NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"]; @@ -74,7 +70,7 @@ - (instancetype)init [prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]]; } - NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage]; + NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:message]; [NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack]; } diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 10867e9cbc5aa9..c415cb87ae1e97 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -582,8 +582,9 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", + "RCT_DEBUG=1", + "RCT_DEV=1", + "RCT_NSASSERT=1", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -621,6 +622,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ""; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -638,10 +640,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_STATIC_ANALYZER_MODE = deep; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -658,6 +657,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_STATIC_ANALYZER_MODE = deep; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", From b6646d1c4cba256713a2ec38a112a9a50e9ad633 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Thu, 30 Apr 2015 12:21:06 -0700 Subject: [PATCH 156/250] [ReactNative] Honor fontWeight once again --- React/Base/RCTConvert.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 465d1f0bb65413..5802a80f6fe1bf 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -733,11 +733,6 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family isItalic = [self RCTFontStyle:style]; } - // Get font weight - if (weight) { - fontWeight = [self RCTFontWeight:weight]; - } - // Gracefully handle being given a font name rather than font family, for // example: "Helvetica Light Oblique" rather than just "Helvetica". if ([UIFont fontNamesForFamilyName:familyName].count == 0) { @@ -756,6 +751,11 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family } } + // Get font weight + if (weight) { + fontWeight = [self RCTFontWeight:weight]; + } + // Get the closest font that matches the given weight for the fontFamily UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize]; CGFloat closestWeight; From 8fe6626d5f809369f219401ff2f5ee210fc17c71 Mon Sep 17 00:00:00 2001 From: Andrei Coman Date: Thu, 30 Apr 2015 11:33:40 -0700 Subject: [PATCH 157/250] [react_native] JS files from D2028144: [react_native] Expose android version to JS --- .../JavaScriptAppEngine/Initialization/ExceptionsManager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index b5868139c7c135..2677a0029c5dfe 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -11,7 +11,6 @@ */ 'use strict'; -var Platform = require('Platform'); var RCTExceptionsManager = require('NativeModules').ExceptionsManager; var loadSourceMap = require('loadSourceMap'); From 4f70e58b37d51529712e479fd8ad6f400497ce3b Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Thu, 30 Apr 2015 10:45:10 -0700 Subject: [PATCH 158/250] [react-native] Only intercept console.error on iOS --- .../Initialization/InitializeJavaScriptAppEngine.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index 73493aaf6fb029..1b67c4940eba05 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -82,7 +82,11 @@ function setupRedBoxErrorHandler() { function setupRedBoxConsoleErrorHandler() { // ExceptionsManager transitively requires Promise so we install it after var ExceptionsManager = require('ExceptionsManager'); - ExceptionsManager.installConsoleErrorReporter(); + var Platform = require('Platform'); + // TODO (#6925182): Enable console.error redbox on Android + if (Platform.OS === 'ios') { + ExceptionsManager.installConsoleErrorReporter(); + } } /** From 63ab6e82811c7bbc67b61da23b9e1310b4182840 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Thu, 30 Apr 2015 13:18:52 -0700 Subject: [PATCH 159/250] [react-native] Remove iOS-specific attributes from ART text --- Libraries/ART/RCTConvert+ART.h | 1 - Libraries/ART/RCTConvert+ART.m | 15 ++------------- Libraries/ART/ReactIOSART.js | 28 +--------------------------- 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/Libraries/ART/RCTConvert+ART.h b/Libraries/ART/RCTConvert+ART.h index 24944fb1298b42..3cbd0e787a24ea 100644 --- a/Libraries/ART/RCTConvert+ART.h +++ b/Libraries/ART/RCTConvert+ART.h @@ -17,7 +17,6 @@ @interface RCTConvert (ART) + (CGPathRef)CGPath:(id)json; -+ (CTFontRef)CTFont:(id)json; + (CTTextAlignment)CTTextAlignment:(id)json; + (ARTTextFrame)ARTTextFrame:(id)json; + (ARTCGFloatArray)ARTCGFloatArray:(id)json; diff --git a/Libraries/ART/RCTConvert+ART.m b/Libraries/ART/RCTConvert+ART.m index 4cd11bd3fbba9d..7a607a12c23691 100644 --- a/Libraries/ART/RCTConvert+ART.m +++ b/Libraries/ART/RCTConvert+ART.m @@ -64,18 +64,6 @@ + (CGPathRef)CGPath:(id)json return (CGPathRef)CFAutorelease(path); } -+ (CTFontRef)CTFont:(id)json -{ - NSDictionary *dict = [self NSDictionary:json]; - if (!dict) { - return nil; - } - CTFontDescriptorRef fontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)dict); - CTFontRef font = CTFontCreateWithFontDescriptor(fontDescriptor, 0.0, NULL); - CFRelease(fontDescriptor); - return (CTFontRef)CFAutorelease(font); -} - RCT_ENUM_CONVERTER(CTTextAlignment, (@{ @"auto": @(kCTTextAlignmentNatural), @"left": @(kCTTextAlignmentLeft), @@ -98,7 +86,8 @@ + (ARTTextFrame)ARTTextFrame:(id)json return frame; } - CTFontRef font = [self CTFont:dict[@"font"]]; + NSDictionary *fontDict = dict[@"font"]; + CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"]]; if (!font) { return frame; } diff --git a/Libraries/ART/ReactIOSART.js b/Libraries/ART/ReactIOSART.js index 9ef2f8843bf7e1..7eb0184717a52f 100644 --- a/Libraries/ART/ReactIOSART.js +++ b/Libraries/ART/ReactIOSART.js @@ -50,18 +50,11 @@ function fontAndLinesDiffer(a, b) { return true; } - var aTraits = a.font.NSCTFontTraitsAttribute; - var bTraits = b.font.NSCTFontTraitsAttribute; - if ( a.font.fontFamily !== b.font.fontFamily || a.font.fontSize !== b.font.fontSize || a.font.fontWeight !== b.font.fontWeight || - a.font.fontStyle !== b.font.fontStyle || - // TODO(6364240): remove iOS-specific attrs - a.font.NSFontFamilyAttribute !== b.font.NSFontFamilyAttribute || - a.font.NSFontSizeAttribute !== b.font.NSFontSizeAttribute || - aTraits.NSCTFontSymbolicTrait !== bTraits.NSCTFontSymbolicTrait + a.font.fontStyle !== b.font.fontStyle ) { return true; } @@ -418,14 +411,6 @@ var Shape = React.createClass({ var cachedFontObjectsFromString = {}; -function extractFontTraits(isBold, isItalic) { - var italic = isItalic ? 1 : 0; - var bold = isBold ? 2 : 0; - return { - NSCTFontSymbolicTrait: italic | bold - }; -} - var fontFamilyPrefix = /^[\s"']*/; var fontFamilySuffix = /[\s"']*$/; @@ -456,10 +441,6 @@ function parseFontString(font) { fontSize: fontSize, fontWeight: isBold ? 'bold' : 'normal', fontStyle: isItalic ? 'italic' : 'normal', - // TODO(6364240): remove iOS-specific attrs - NSFontFamilyAttribute: fontFamily, - NSFontSizeAttribute: fontSize, - NSCTFontTraitsAttribute: extractFontTraits(isBold, isItalic) }; return cachedFontObjectsFromString[font]; } @@ -479,13 +460,6 @@ function extractFont(font) { fontSize: fontSize, fontWeight: font.fontWeight, fontStyle: font.fontStyle, - // TODO(6364240): remove iOS-specific attrs - NSFontFamilyAttribute: fontFamily, - NSFontSizeAttribute: fontSize, - NSCTFontTraitsAttribute: extractFontTraits( - font.fontWeight === 'bold', - font.fontStyle === 'italic' - ) }; } From 824da1badaceccfb3009af79343422670e4f3ac0 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Thu, 30 Apr 2015 14:09:55 -0700 Subject: [PATCH 160/250] [ReactNative] pass in launchOptions to relevant bridged modules --- .../PushNotificationIOS/RCTPushNotificationManager.h | 2 -- .../PushNotificationIOS/RCTPushNotificationManager.m | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index b60a646e416f0f..ef1ba1496e8c62 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -13,8 +13,6 @@ @interface RCTPushNotificationManager : NSObject -- (instancetype)initWithInitialNotification:(NSDictionary *)initialNotification NS_DESIGNATED_INITIALIZER; - + (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; + (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification; diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index c0bedee5ec8952..4846c885e302e8 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -24,14 +24,8 @@ @implementation RCTPushNotificationManager @synthesize bridge = _bridge; - (instancetype)init -{ - return [self initWithInitialNotification:nil]; -} - -- (instancetype)initWithInitialNotification:(NSDictionary *)initialNotification { if ((self = [super init])) { - _initialNotification = [initialNotification copy]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRemoteNotificationReceived:) name:RCTRemoteNotificationReceived @@ -45,6 +39,12 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (void)setBridge:(RCTBridge *)bridge +{ + _bridge = bridge; + _initialNotification = [bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy]; +} + + (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { if ([application respondsToSelector:@selector(registerForRemoteNotifications)]) { From e5f47731c6b094fa1776eb313fd39bbc2ccd6463 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 30 Apr 2015 14:18:00 -0700 Subject: [PATCH 161/250] [ReactNative] Only report console.error()s as exceptions in dev mode --- .../Initialization/InitializeJavaScriptAppEngine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index 1b67c4940eba05..bb0aa263c96470 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -84,7 +84,7 @@ function setupRedBoxConsoleErrorHandler() { var ExceptionsManager = require('ExceptionsManager'); var Platform = require('Platform'); // TODO (#6925182): Enable console.error redbox on Android - if (Platform.OS === 'ios') { + if (__DEV__ && Platform.OS === 'ios') { ExceptionsManager.installConsoleErrorReporter(); } } From 76dc14684d9a3cab0e8efcac6f61e9b6b96278a1 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Thu, 30 Apr 2015 15:53:27 -0700 Subject: [PATCH 162/250] [ReactNative] Navigator block touches to non-active scenes Summary: When tapping a link quickly, it will cause two scenes to be pushed on. This prevents against that case by swallowing touches for all non-active scenes. @public Test Plan: Can no longer double-push scenes --- .../CustomComponents/Navigator/Navigator.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 538c355383f449..86d068f9f7e06e 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -1176,12 +1176,18 @@ var Navigator = React.createClass({ var scene = shouldRenderScene ? this._renderScene(route, i, sceneNavigatorContext) : null; return ( - - {scene} - + ( + i !== this.state.presentedIndex + )}> + + {scene} + + ); }, From 6cb717809879854fc9c31c627af6444fedf48889 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Thu, 30 Apr 2015 17:23:46 -0700 Subject: [PATCH 163/250] [ReactNative] Suggest un-pausing debugger when there are issues Summary: @public The most common problem I've noticed is that the debugger is paused, so we shouldn't ask the user if Chrome is open, because it usually is. Test Plan: Try to reload with debugger paused, see new error message. --- Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m index 753c8d76d38324..16a378ccfb6496 100644 --- a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m +++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m @@ -66,7 +66,8 @@ - (instancetype)initWithURL:(NSURL *)URL retries--; } if (!runtimeIsReady) { - RCTLogError(@"Runtime is not ready. Do you have Chrome open?"); + RCTLogError(@"Runtime is not ready. Make sure Chrome is running and not " + "paused on a breakpoint or exception and try reloading again."); [self invalidate]; return nil; } From 67196b36bb3a4d880c4397ec48c84f1eb8d500c0 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Fri, 1 May 2015 06:11:24 -0700 Subject: [PATCH 164/250] [ReactNative] Fix WebView executor --- React/Executors/RCTWebViewExecutor.m | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m index 09628850fb3aa8..a180bae2cf3c7e 100644 --- a/React/Executors/RCTWebViewExecutor.m +++ b/React/Executors/RCTWebViewExecutor.m @@ -42,13 +42,17 @@ @implementation RCTWebViewExecutor { UIWebView *_webView; NSMutableDictionary *_objectsToInject; + NSRegularExpression *_commentsRegex; } +@synthesize valid = _valid; + - (instancetype)initWithWebView:(UIWebView *)webView { if ((self = [super init])) { _objectsToInject = [[NSMutableDictionary alloc] init]; _webView = webView ?: [[UIWebView alloc] init]; + _commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]+?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL]; _webView.delegate = self; } return self; @@ -59,13 +63,9 @@ - (id)init return [self initWithWebView:nil]; } -- (BOOL)isValid -{ - return _webView != nil; -} - - (void)invalidate { + _valid = NO; _webView.delegate = nil; _webView = nil; } @@ -129,10 +129,21 @@ - (void)executeApplicationScript:(NSString *)script } RCTAssert(onComplete != nil, @""); - _onApplicationScriptLoaded = onComplete; + __weak RCTWebViewExecutor *weakSelf = self; + _onApplicationScriptLoaded = ^(NSError *error){ + RCTWebViewExecutor *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_valid = error == nil; + onComplete(error); + }; + + script = [_commentsRegex stringByReplacingMatchesInString:script + options:0 + range:NSMakeRange(0, script.length) + withTemplate:@""]; - script = [script stringByReplacingOccurrencesOfString:@"" withString:@""]; if (_objectsToInject.count > 0) { NSMutableString *scriptWithInjections = [[NSMutableString alloc] initWithString:@"/* BEGIN NATIVELY INJECTED OBJECTS */\n"]; [_objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *blockScript, BOOL *stop) { From ba501a1bf50bb0ed9e16637e68965ef13cbcc6b3 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Fri, 1 May 2015 06:21:03 -0700 Subject: [PATCH 165/250] Upgraded dev menu --- Examples/UIExplorer/TabBarIOSExample.js | 2 +- Libraries/Components/TextInput/TextInput.js | 2 +- React/Base/RCTBridge.m | 7 +- React/Base/RCTDevMenu.h | 9 +- React/Base/RCTDevMenu.m | 270 ++++++++++++++------ React/Base/RCTKeyCommands.m | 11 + React/Base/RCTRedBox.m | 2 +- React/React.xcodeproj/project.pbxproj | 1 + 8 files changed, 212 insertions(+), 92 deletions(-) diff --git a/Examples/UIExplorer/TabBarIOSExample.js b/Examples/UIExplorer/TabBarIOSExample.js index a8f913a07de228..9b748ee33695a6 100644 --- a/Examples/UIExplorer/TabBarIOSExample.js +++ b/Examples/UIExplorer/TabBarIOSExample.js @@ -78,7 +78,7 @@ var TabBarExample = React.createClass({ this.setState({ selectedTab: 'greenTab', presses: this.state.presses + 1 - }); + }); }}> {this._renderContent('#21551C', 'Green Tab')} diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index c21184b7da5130..fa87beefc7b513 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -582,7 +582,7 @@ var TextInput = React.createClass({ var counter = event.nativeEvent.eventCounter; if (counter > this.state.mostRecentEventCounter) { this.setState({mostRecentEventCounter: counter}); - } + } }, }); diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 5dd3bac5a89359..85486ddc1d82c1 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -964,18 +964,13 @@ - (void)bindKeys __weak RCTBridge *weakSelf = self; RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; - // Workaround around the first cmd+R not working: http://openradar.appspot.com/19613391 - // You can register just the cmd key and do nothing. This will trigger the bug and cmd+R - // will work like a charm! - [commands registerKeyCommandWithInput:@"" - modifierFlags:UIKeyModifierCommand - action:NULL]; // reload in current mode [commands registerKeyCommandWithInput:@"r" modifierFlags:UIKeyModifierCommand action:^(UIKeyCommand *command) { [weakSelf reload]; }]; + #endif } diff --git a/React/Base/RCTDevMenu.h b/React/Base/RCTDevMenu.h index 537675576c3204..bb80ac208d7ffc 100644 --- a/React/Base/RCTDevMenu.h +++ b/React/Base/RCTDevMenu.h @@ -36,14 +36,15 @@ @property (nonatomic, assign) BOOL liveReloadEnabled; /** - * The time between checks for code changes. Defaults to 1 second. + * Manually show the dev menu (can be called from JS). */ -@property (nonatomic, assign) NSTimeInterval liveReloadPeriod; +- (void)show; /** - * Manually show the dev menu. + * Manually reload the application. Equivalent to calling [bridge reload] + * directly, but can be called from JS. */ -- (void)show; +- (void)reload; @end diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 529840e0a4fafb..82b4fa968ae03b 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -28,6 +28,7 @@ - (void)stopProfiling; @end static NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification"; +static NSString *const RCTDevMenuSettingsKey = @"RCTDevMenu"; @implementation UIWindow (RCTDevMenu) @@ -40,14 +41,20 @@ - (void)RCT_motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event @end -@interface RCTDevMenu () +@interface RCTDevMenu () + +@property (nonatomic, strong) Class executorClass; @end @implementation RCTDevMenu { - NSTimer *_updateTimer; UIActionSheet *_actionSheet; + NSUserDefaults *_defaults; + NSMutableDictionary *_settings; + NSURLSessionDataTask *_updateTask; + NSURL *_liveReloadURL; + BOOL _jsLoaded; } @synthesize bridge = _bridge; @@ -66,31 +73,43 @@ - (instancetype)init { if ((self = [super init])) { - _shakeToShow = YES; - _liveReloadPeriod = 1.0; // 1 second - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(showOnShake) - name:RCTShowDevMenuNotification - object:nil]; + _defaults = [NSUserDefaults standardUserDefaults]; + [self updateSettings]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self + selector:@selector(showOnShake) + name:RCTShowDevMenuNotification + object:nil]; + + [notificationCenter addObserver:self + selector:@selector(updateSettings) + name:NSUserDefaultsDidChangeNotification + object:nil]; + + [notificationCenter addObserver:self + selector:@selector(jsLoaded) + name:RCTJavaScriptDidLoadNotification + object:nil]; #if TARGET_IPHONE_SIMULATOR __weak RCTDevMenu *weakSelf = self; RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; - // Workaround around the first cmd+D not working: http://openradar.appspot.com/19613391 - // You can register just the cmd key and do nothing. This will trigger the bug and cmd+R - // will work like a charm! - [commands registerKeyCommandWithInput:@"" + // toggle debug menu + [commands registerKeyCommandWithInput:@"d" modifierFlags:UIKeyModifierCommand - action:NULL]; + action:^(UIKeyCommand *command) { + [weakSelf toggle]; + }]; - // reload in debug mode - [commands registerKeyCommandWithInput:@"d" + // reload in normal mode + [commands registerKeyCommandWithInput:@"n" modifierFlags:UIKeyModifierCommand action:^(UIKeyCommand *command) { - __strong RCTDevMenu *strongSelf = weakSelf; - [strongSelf show]; + weakSelf.executorClass = Nil; }]; #endif @@ -98,11 +117,58 @@ - (instancetype)init return self; } +- (void)updateSettings +{ + _settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]]; + + self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue]; + self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue]; + self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue]; + self.executorClass = NSClassFromString(_settings[@"executorClass"]); +} + +- (void)jsLoaded +{ + _jsLoaded = YES; + + // Check if live reloading is available + _liveReloadURL = nil; + RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; + if (!sourceCodeModule.scriptURL) { + if (!sourceCodeModule) { + RCTLogWarn(@"RCTSourceCode module not found"); + } else { + RCTLogWarn(@"RCTSourceCode module scriptURL has not been set"); + } + } else if (![sourceCodeModule.scriptURL isFileURL]) { + // Live reloading is disabled when running from bundled JS file + _liveReloadURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL]; + } + + // Hit these setters again after bridge has finished loading + self.profilingEnabled = _profilingEnabled; + self.liveReloadEnabled = _liveReloadEnabled; + self.executorClass = _executorClass; +} + - (void)dealloc { + [_updateTask cancel]; + [_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (void)updateSetting:(NSString *)name value:(id)value +{ + if (value) { + _settings[name] = value; + } else { + [_settings removeObjectForKey:name]; + } + [_defaults setObject:_settings forKey:RCTDevMenuSettingsKey]; + [_defaults synchronize]; +} + - (void)showOnShake { if (_shakeToShow) { @@ -110,48 +176,73 @@ - (void)showOnShake } } -- (void)show +- (void)toggle { if (_actionSheet) { + [_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES]; + _actionSheet = nil; + } else { + [self show]; + } +} + +RCT_EXPORT_METHOD(show) +{ + if (_actionSheet || !_bridge) { return; } - NSString *debugTitleChrome = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging"; - NSString *debugTitleSafari = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Enable Safari Debugging"; - NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload"; - NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling"; + NSString *debugTitleChrome = _executorClass && _executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Debug in Chrome"; + NSString *debugTitleSafari = _executorClass && _executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Debug in Safari"; UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development" delegate:self - cancelButtonTitle:@"Cancel" + cancelButtonTitle:nil destructiveButtonTitle:nil - otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, liveReloadTitle, profilingTitle, nil]; + otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, nil]; + + if (_liveReloadURL) { + + NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload"; + NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling"; + + [actionSheet addButtonWithTitle:liveReloadTitle]; + [actionSheet addButtonWithTitle:profilingTitle]; + } + + [actionSheet addButtonWithTitle:@"Cancel"]; + actionSheet.cancelButtonIndex = [actionSheet numberOfButtons] - 1; actionSheet.actionSheetStyle = UIBarStyleBlack; [actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view]; _actionSheet = actionSheet; } +RCT_EXPORT_METHOD(reload) +{ + _jsLoaded = NO; + _liveReloadURL = nil; + [_bridge reload]; +} + - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { _actionSheet = nil; switch (buttonIndex) { case 0: { - [_bridge reload]; + [self reload]; break; } case 1: { Class cls = NSClassFromString(@"RCTWebSocketExecutor"); - _bridge.executorClass = (_bridge.executorClass != cls) ? cls : nil; - [_bridge reload]; + self.executorClass = (_executorClass == cls) ? Nil : cls; break; } case 2: { Class cls = NSClassFromString(@"RCTWebViewExecutor"); - _bridge.executorClass = (_bridge.executorClass != cls) ? cls : Nil; - [_bridge reload]; + self.executorClass = (_executorClass == cls) ? Nil : cls; break; } case 3: { @@ -167,89 +258,110 @@ - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger } } +- (void)setShakeToShow:(BOOL)shakeToShow +{ + if (_shakeToShow != shakeToShow) { + _shakeToShow = shakeToShow; + [self updateSetting:@"shakeToShow" value: @(_shakeToShow)]; + } +} + - (void)setProfilingEnabled:(BOOL)enabled { - if (_profilingEnabled == enabled) { - return; + if (_profilingEnabled != enabled) { + _profilingEnabled = enabled; + [self updateSetting:@"profilingEnabled" value: @(_profilingEnabled)]; } - _profilingEnabled = enabled; - if (RCTProfileIsProfiling()) { - [_bridge stopProfiling]; - } else { - [_bridge startProfiling]; + if (_liveReloadURL && enabled != RCTProfileIsProfiling()) { + if (enabled) { + [_bridge startProfiling]; + } else { + [_bridge stopProfiling]; + } } } - (void)setLiveReloadEnabled:(BOOL)enabled { - if (_liveReloadEnabled == enabled) { - return; + if (_liveReloadEnabled != enabled) { + _liveReloadEnabled = enabled; + [self updateSetting:@"liveReloadEnabled" value: @(_liveReloadEnabled)]; } - _liveReloadEnabled = enabled; if (_liveReloadEnabled) { - - _updateTimer = [NSTimer scheduledTimerWithTimeInterval:_liveReloadPeriod - target:self - selector:@selector(pollForUpdates) - userInfo:nil - repeats:YES]; + [self checkForUpdates]; } else { - - [_updateTimer invalidate]; - _updateTimer = nil; + [_updateTask cancel]; + _updateTask = nil; } } -- (void)setLiveReloadPeriod:(NSTimeInterval)liveReloadPeriod +- (void)setExecutorClass:(Class)executorClass { - _liveReloadPeriod = liveReloadPeriod; - if (_liveReloadEnabled) { - self.liveReloadEnabled = NO; - self.liveReloadEnabled = YES; + if (_executorClass != executorClass) { + _executorClass = executorClass; + [self updateSetting:@"executorClass" value: NSStringFromClass(executorClass)]; } -} -- (void)pollForUpdates -{ - RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; - if (!sourceCodeModule) { - RCTLogError(@"RCTSourceCode module not found"); - self.liveReloadEnabled = NO; - } + if (_bridge.executorClass != executorClass) { - NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL]; - [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:longPollURL] - queue:[[NSOperationQueue alloc] init] - completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { + // TODO (6929129): we can remove this special case test once we have better + // support for custom executors in the dev menu. But right now this is + // needed to prevent overriding a custom executor with the default if a + // custom executor has been set directly on the bridge + if (executorClass == Nil && + (_bridge.executorClass != NSClassFromString(@"RCTWebSocketExecutor") && + _bridge.executorClass != NSClassFromString(@"RCTWebViewExecutor"))) { + return; + } - NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response; - if (_liveReloadEnabled && HTTPResponse.statusCode == 205) { - [_bridge reload]; - } - }]; + _bridge.executorClass = executorClass; + [self reload]; + } } -- (BOOL)isValid +- (void)checkForUpdates { - return !_liveReloadEnabled || _updateTimer != nil; -} + if (!_jsLoaded || !_liveReloadEnabled || !_liveReloadURL) { + return; + } -- (void)invalidate -{ - [_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES]; - [_updateTimer invalidate]; - _updateTimer = nil; + if (_updateTask) { + [_updateTask cancel]; + _updateTask = nil; + return; + } + + __weak RCTDevMenu *weakSelf = self; + _updateTask = [[NSURLSession sharedSession] dataTaskWithURL:_liveReloadURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + + dispatch_async(dispatch_get_main_queue(), ^{ + __strong RCTDevMenu *strongSelf = weakSelf; + if (strongSelf && strongSelf->_liveReloadEnabled) { + NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response; + if (!error && HTTPResponse.statusCode == 205) { + [strongSelf reload]; + } else { + strongSelf->_updateTask = nil; + [strongSelf checkForUpdates]; + } + } + }); + + }]; + + [_updateTask resume]; } @end -#else // Unvailable +#else // Unavailable when not in dev mode @implementation RCTDevMenu - (void)show {} +- (void)reload {} @end diff --git a/React/Base/RCTKeyCommands.m b/React/Base/RCTKeyCommands.m index 9141dd31d9348b..823acb2418659e 100644 --- a/React/Base/RCTKeyCommands.m +++ b/React/Base/RCTKeyCommands.m @@ -90,6 +90,17 @@ - (void)registerKeyCommandWithInput:(NSString *)input { RCTAssertMainThread(); + if (input.length && flags) { + + // Workaround around the first cmd not working: http://openradar.appspot.com/19613391 + // You can register just the cmd key and do nothing. This ensures that + // command-key modified commands will work first time. + + [self registerKeyCommandWithInput:@"" + modifierFlags:flags + action:nil]; + } + UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(RCT_handleKeyCommand:)]; diff --git a/React/Base/RCTRedBox.m b/React/Base/RCTRedBox.m index b54d18aa3692bf..0de61d1721c487 100644 --- a/React/Base/RCTRedBox.m +++ b/React/Base/RCTRedBox.m @@ -91,7 +91,7 @@ - (void)openStackFrameInEditor:(NSDictionary *)stackFrame [request setValue:postLength forHTTPHeaderField:@"Content-Length"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:nil]; + [[[NSURLSession sharedSession] dataTaskWithRequest:request] resume]; } - (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index c415cb87ae1e97..42954d36e55092 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -582,6 +582,7 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", "RCT_DEBUG=1", "RCT_DEV=1", "RCT_NSASSERT=1", From 4bd56ca6d1c0fdfe7c0ac89ad184aa4f13de8ed4 Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Fri, 1 May 2015 06:54:40 -0700 Subject: [PATCH 166/250] [react_native] JS files from D2038898: Move view specific constants out of UIManager to the cooresponding view manager class. --- Libraries/Components/ScrollView/ScrollView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index e66612b22886c0..76419bce0aa0e4 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -364,7 +364,7 @@ var validAttributes = { if (Platform.OS === 'android') { var AndroidScrollView = createReactIOSNativeComponentClass({ validAttributes: validAttributes, - uiViewClassName: 'AndroidScrollView', + uiViewClassName: 'RCTScrollView', }); var AndroidHorizontalScrollView = createReactIOSNativeComponentClass({ validAttributes: validAttributes, From 6abf37b47ee35aedfa7838b3eba98342eabc5cf3 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Fri, 1 May 2015 14:17:02 -0700 Subject: [PATCH 167/250] [ReactNative] Navigator.Interceptor handler arg fix --- Libraries/CustomComponents/Navigator/NavigatorInterceptor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js b/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js index dcc5d43efa9c25..e70820435ddaef 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js +++ b/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js @@ -78,9 +78,9 @@ var NavigatorInterceptor = React.createClass({ } switch (action) { case 'pop': - return this.props.onPopRequest && this.props.onPopRequest(action, arg1, arg2); + return this.props.onPopRequest && this.props.onPopRequest(arg1, arg2); case 'push': - return this.props.onPushRequest && this.props.onPushRequest(action, arg1, arg2); + return this.props.onPushRequest && this.props.onPushRequest(arg1, arg2); default: return false; } From 5453be2ab239f6e5ccb4aa61c9bf0223a107221e Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Fri, 1 May 2015 16:19:27 -0700 Subject: [PATCH 168/250] [ReactNative] Fix touch issue caused by D2036644 after swiping back --- .../CustomComponents/Navigator/Navigator.js | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 86d068f9f7e06e..280d8c4dd4f430 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -1176,18 +1176,12 @@ var Navigator = React.createClass({ var scene = shouldRenderScene ? this._renderScene(route, i, sceneNavigatorContext) : null; return ( - ( - i !== this.state.presentedIndex - )}> - - {scene} - - + + {scene} + ); }, @@ -1202,6 +1196,9 @@ var Navigator = React.createClass({ { + return i !== this.state.presentedIndex; + }} style={[initialSceneStyle, this.props.sceneStyle]}> {React.cloneElement(child, { ref: this._handleItemRef.bind(null, this.state.idStack[i]), From 48b281de76170a63d046e21f9180fbae3a9c9eea Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 1 May 2015 16:30:47 -0700 Subject: [PATCH 169/250] [ReactNative] Remove padding restriction on images Summary: @public Padding actually works fine on images. Test Plan: Nested text inside an image is properly positioned with padding on the image. --- Libraries/Image/ImageStylePropTypes.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Libraries/Image/ImageStylePropTypes.js b/Libraries/Image/ImageStylePropTypes.js index 4e361d9dea1293..d46807ce7c2a14 100644 --- a/Libraries/Image/ImageStylePropTypes.js +++ b/Libraries/Image/ImageStylePropTypes.js @@ -29,19 +29,4 @@ var ImageStylePropTypes = { opacity: ReactPropTypes.number, }; -// Image doesn't support padding correctly (#4841912) -var unsupportedProps = Object.keys({ - padding: null, - paddingTop: null, - paddingLeft: null, - paddingRight: null, - paddingBottom: null, - paddingVertical: null, - paddingHorizontal: null, -}); - -for (var i = 0; i < unsupportedProps.length; i++) { - delete ImageStylePropTypes[unsupportedProps[i]]; -} - module.exports = ImageStylePropTypes; From 43e038887d720a11382f8818a52277553af24f21 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Fri, 1 May 2015 17:05:24 -0700 Subject: [PATCH 170/250] [react-packager] Combine source maps coming from transformer Summary: @public Fixes [#393](https://github.com/facebook/react-native/issues/393). Currently the transformer assumes line-preserving compilers and defaults to a super-fast source map generation process. However, we need to support compilers that aren't preserving lines. I had feared this wuold slow down the server but I came about a little known peace of the spec that defines an "indexed source map" just for the purpose of concating files: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit Test Plan: 1. runJestTests.sh 2. run server and click around example apps 3. add a custom transporter like babel 4. add a custom file and a debugger statement 5. debug in chrome and make sure it works redbox still works --- .../__tests__/Transformer-test.js | 4 +- .../react-packager/src/JSTransformer/index.js | 8 +- .../react-packager/src/Packager/Package.js | 112 ++++++++++++--- .../src/Packager/__tests__/Package-test.js | 132 ++++++++++++++++-- .../src/Packager/__tests__/Packager-test.js | 56 ++++---- packager/react-packager/src/Packager/index.js | 37 ++--- .../react-packager/src/lib/ModuleTransport.js | 38 +++++ 7 files changed, 308 insertions(+), 79 deletions(-) create mode 100644 packager/react-packager/src/lib/ModuleTransport.js diff --git a/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js b/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js index 72845a2e175460..eb307a02eb558a 100644 --- a/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js +++ b/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js @@ -11,6 +11,7 @@ jest .dontMock('worker-farm') .dontMock('os') + .dontMock('../../lib/ModuleTransport') .dontMock('../index'); var OPTIONS = { @@ -37,7 +38,7 @@ describe('Transformer', function() { pit('should loadFileAndTransform', function() { workers.mockImpl(function(data, callback) { - callback(null, { code: 'transformed' }); + callback(null, { code: 'transformed', map: 'sourceMap' }); }); require('fs').readFile.mockImpl(function(file, callback) { callback(null, 'content'); @@ -47,6 +48,7 @@ describe('Transformer', function() { .then(function(data) { expect(data).toEqual({ code: 'transformed', + map: 'sourceMap', sourcePath: 'file', sourceCode: 'content' }); diff --git a/packager/react-packager/src/JSTransformer/index.js b/packager/react-packager/src/JSTransformer/index.js index 962eb7fe9b9dfb..33e0170377eab4 100644 --- a/packager/react-packager/src/JSTransformer/index.js +++ b/packager/react-packager/src/JSTransformer/index.js @@ -14,6 +14,7 @@ var Cache = require('./Cache'); var workerFarm = require('worker-farm'); var declareOpts = require('../lib/declareOpts'); var util = require('util'); +var ModuleTransport = require('../lib/ModuleTransport'); var readFile = Promise.promisify(fs.readFile); @@ -100,11 +101,12 @@ Transformer.prototype.loadFileAndTransform = function(filePath) { throw formatError(res.error, filePath, sourceCode); } - return { + return new ModuleTransport({ code: res.code, + map: res.map, sourcePath: filePath, - sourceCode: sourceCode - }; + sourceCode: sourceCode, + }); } ); }); diff --git a/packager/react-packager/src/Packager/Package.js b/packager/react-packager/src/Packager/Package.js index 67e31e47efef45..c621f74700b0c9 100644 --- a/packager/react-packager/src/Packager/Package.js +++ b/packager/react-packager/src/Packager/Package.js @@ -11,6 +11,7 @@ var _ = require('underscore'); var base64VLQ = require('./base64-vlq'); var UglifyJS = require('uglify-js'); +var ModuleTransport = require('../lib/ModuleTransport'); module.exports = Package; @@ -19,22 +20,25 @@ function Package(sourceMapUrl) { this._modules = []; this._assets = []; this._sourceMapUrl = sourceMapUrl; + this._shouldCombineSourceMaps = false; } Package.prototype.setMainModuleId = function(moduleId) { this._mainModuleId = moduleId; }; -Package.prototype.addModule = function( - transformedCode, - sourceCode, - sourcePath -) { - this._modules.push({ - transformedCode: transformedCode, - sourceCode: sourceCode, - sourcePath: sourcePath - }); +Package.prototype.addModule = function(module) { + if (!(module instanceof ModuleTransport)) { + throw new Error('Expeceted a ModuleTransport object'); + } + + // If we get a map from the transformer we'll switch to a mode + // were we're combining the source maps as opposed to + if (!this._shouldCombineSourceMaps && module.map != null) { + this._shouldCombineSourceMaps = true; + } + + this._modules.push(module); }; Package.prototype.addAsset = function(asset) { @@ -45,11 +49,12 @@ Package.prototype.finalize = function(options) { options = options || {}; if (options.runMainModule) { var runCode = ';require("' + this._mainModuleId + '");'; - this.addModule( - runCode, - runCode, - 'RunMainModule.js' - ); + this.addModule(new ModuleTransport({ + code: runCode, + virtual: true, + sourceCode: runCode, + sourcePath: 'RunMainModule.js' + })); } Object.freeze(this._modules); @@ -67,7 +72,7 @@ Package.prototype._assertFinalized = function() { Package.prototype._getSource = function() { if (this._source == null) { - this._source = _.pluck(this._modules, 'transformedCode').join('\n'); + this._source = _.pluck(this._modules, 'code').join('\n'); } return this._source; }; @@ -136,10 +141,50 @@ Package.prototype.getMinifiedSourceAndMap = function() { } }; +/** + * I found a neat trick in the sourcemap spec that makes it easy + * to concat sourcemaps. The `sections` field allows us to combine + * the sourcemap easily by adding an offset. Tested on chrome. + * Seems like it's not yet in Firefox but that should be fine for + * now. + */ +Package.prototype._getCombinedSourceMaps = function(options) { + var result = { + version: 3, + file: 'bundle.js', + sections: [], + }; + + var line = 0; + this._modules.forEach(function(module) { + var map = module.map; + if (module.virtual) { + map = generateSourceMapForVirtualModule(module); + } + + if (options.excludeSource) { + map = _.extend({}, map, {sourcesContent: []}); + } + + result.sections.push({ + offset: { line: line, column: 0 }, + map: map, + }); + line += module.code.split('\n').length; + }); + + return result; +}; + Package.prototype.getSourceMap = function(options) { this._assertFinalized(); options = options || {}; + + if (this._shouldCombineSourceMaps) { + return this._getCombinedSourceMaps(options); + } + var mappings = this._getMappings(); var map = { file: 'bundle.js', @@ -168,13 +213,14 @@ Package.prototype._getMappings = function() { // except for the lineno mappinp: curLineno - prevLineno = 1; Which is C. var line = 'AACA'; + var moduleLines = Object.create(null); var mappings = ''; for (var i = 0; i < modules.length; i++) { var module = modules[i]; - var transformedCode = module.transformedCode; + var code = module.code; var lastCharNewLine = false; - module.lines = 0; - for (var t = 0; t < transformedCode.length; t++) { + moduleLines[module.sourcePath] = 0; + for (var t = 0; t < code.length; t++) { if (t === 0 && i === 0) { mappings += firstLine; } else if (t === 0) { @@ -183,13 +229,15 @@ Package.prototype._getMappings = function() { // This is the only place were we actually don't know the mapping ahead // of time. When it's a new module (and not the first) the lineno // mapping is 0 (current) - number of lines in prev module. - mappings += base64VLQ.encode(0 - modules[i - 1].lines); + mappings += base64VLQ.encode( + 0 - moduleLines[modules[i - 1].sourcePath] + ); mappings += 'A'; } else if (lastCharNewLine) { - module.lines++; + moduleLines[module.sourcePath]++; mappings += line; } - lastCharNewLine = transformedCode[t] === '\n'; + lastCharNewLine = code[t] === '\n'; if (lastCharNewLine) { mappings += ';'; } @@ -218,7 +266,25 @@ Package.prototype.getDebugInfo = function() { this._modules.map(function(m) { return '

Path:

' + m.sourcePath + '

Source:

' + '
'; + _.escape(m.code) + '
'; }).join('\n'), ].join('\n'); }; + +function generateSourceMapForVirtualModule(module) { + // All lines map 1-to-1 + var mappings = 'AAAA;'; + + for (var i = 1; i < module.code.split('\n').length; i++) { + mappings += 'AACA;'; + } + + return { + version: 3, + sources: [ module.sourcePath ], + names: [], + mappings: mappings, + file: module.sourcePath, + sourcesContent: [ module.code ], + }; +} diff --git a/packager/react-packager/src/Packager/__tests__/Package-test.js b/packager/react-packager/src/Packager/__tests__/Package-test.js index db596a7bc4beb4..0aaa3971cd8423 100644 --- a/packager/react-packager/src/Packager/__tests__/Package-test.js +++ b/packager/react-packager/src/Packager/__tests__/Package-test.js @@ -13,11 +13,13 @@ jest.autoMockOff(); var SourceMapGenerator = require('source-map').SourceMapGenerator; describe('Package', function() { + var ModuleTransport; var Package; var ppackage; beforeEach(function() { Package = require('../Package'); + ModuleTransport = require('../../lib/ModuleTransport'); ppackage = new Package('test_url'); ppackage.getSourceMap = jest.genMockFn().mockImpl(function() { return 'test-source-map'; @@ -26,8 +28,17 @@ describe('Package', function() { describe('source package', function() { it('should create a package and get the source', function() { - ppackage.addModule('transformed foo;', 'source foo', 'foo path'); - ppackage.addModule('transformed bar;', 'source bar', 'bar path'); + ppackage.addModule(new ModuleTransport({ + code: 'transformed foo;', + sourceCode: 'source foo', + sourcePath: 'foo path', + })); + ppackage.addModule(new ModuleTransport({ + code: 'transformed bar;', + sourceCode: 'source bar', + sourcePath: 'bar path', + })); + ppackage.finalize({}); expect(ppackage.getSource()).toBe([ 'transformed foo;', @@ -37,8 +48,18 @@ describe('Package', function() { }); it('should create a package and add run module code', function() { - ppackage.addModule('transformed foo;', 'source foo', 'foo path'); - ppackage.addModule('transformed bar;', 'source bar', 'bar path'); + ppackage.addModule(new ModuleTransport({ + code: 'transformed foo;', + sourceCode: 'source foo', + sourcePath: 'foo path' + })); + + ppackage.addModule(new ModuleTransport({ + code: 'transformed bar;', + sourceCode: 'source bar', + sourcePath: 'bar path' + })); + ppackage.setMainModuleId('foo'); ppackage.finalize({runMainModule: true}); expect(ppackage.getSource()).toBe([ @@ -59,7 +80,11 @@ describe('Package', function() { return minified; }; - ppackage.addModule('transformed foo;', 'source foo', 'foo path'); + ppackage.addModule(new ModuleTransport({ + code: 'transformed foo;', + sourceCode: 'source foo', + sourcePath: 'foo path' + })); ppackage.finalize(); expect(ppackage.getMinifiedSourceAndMap()).toBe(minified); }); @@ -68,13 +93,104 @@ describe('Package', function() { describe('sourcemap package', function() { it('should create sourcemap', function() { var p = new Package('test_url'); - p.addModule('transformed foo;\n', 'source foo', 'foo path'); - p.addModule('transformed bar;\n', 'source bar', 'bar path'); + p.addModule(new ModuleTransport({ + code: [ + 'transformed foo', + 'transformed foo', + 'transformed foo', + ].join('\n'), + sourceCode: [ + 'source foo', + 'source foo', + 'source foo', + ].join('\n'), + sourcePath: 'foo path', + })); + p.addModule(new ModuleTransport({ + code: [ + 'transformed bar', + 'transformed bar', + 'transformed bar', + ].join('\n'), + sourceCode: [ + 'source bar', + 'source bar', + 'source bar', + ].join('\n'), + sourcePath: 'bar path', + })); + p.setMainModuleId('foo'); p.finalize({runMainModule: true}); var s = p.getSourceMap(); expect(s).toEqual(genSourceMap(p._modules)); }); + + it('should combine sourcemaps', function() { + var p = new Package('test_url'); + + p.addModule(new ModuleTransport({ + code: 'transformed foo;\n', + map: {name: 'sourcemap foo'}, + sourceCode: 'source foo', + sourcePath: 'foo path' + })); + + p.addModule(new ModuleTransport({ + code: 'transformed foo;\n', + map: {name: 'sourcemap bar'}, + sourceCode: 'source foo', + sourcePath: 'foo path' + })); + + p.addModule(new ModuleTransport({ + code: 'image module;\nimage module;', + virtual: true, + sourceCode: 'image module;\nimage module;', + sourcePath: 'image.png', + })); + + p.setMainModuleId('foo'); + p.finalize({runMainModule: true}); + + var s = p.getSourceMap(); + expect(s).toEqual({ + file: 'bundle.js', + version: 3, + sections: [ + { offset: { line: 0, column: 0 }, map: { name: 'sourcemap foo' } }, + { offset: { line: 2, column: 0 }, map: { name: 'sourcemap bar' } }, + { + offset: { + column: 0, + line: 4 + }, + map: { + file: 'image.png', + mappings: 'AAAA;AACA;', + names: {}, + sources: [ 'image.png' ], + sourcesContent: ['image module;\nimage module;'], + version: 3, + } + }, + { + offset: { + column: 0, + line: 6 + }, + map: { + file: 'RunMainModule.js', + mappings: 'AAAA;', + names: {}, + sources: [ 'RunMainModule.js' ], + sourcesContent: [';require("foo");'], + version: 3, + } + } + ], + }); + }); }); describe('getAssets()', function() { @@ -95,7 +211,7 @@ describe('Package', function() { var packageLineNo = 0; for (var i = 0; i < modules.length; i++) { var module = modules[i]; - var transformedCode = module.transformedCode; + var transformedCode = module.code; var sourcePath = module.sourcePath; var sourceCode = module.sourceCode; var transformedLineCount = 0; diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index 8e1420a3a6c0dd..3f09344624df16 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -13,6 +13,7 @@ jest .dontMock('path') .dontMock('os') .dontMock('underscore') + .dontMock('../../lib/ModuleTransport') .setMock('uglify-js') .dontMock('../'); @@ -93,6 +94,7 @@ describe('Packager', function() { .mockImpl(function(path) { return Promise.resolve({ code: 'transformed ' + path, + map: 'sourcemap ' + path, sourceCode: 'source ' + path, sourcePath: path }); @@ -117,16 +119,19 @@ describe('Packager', function() { return packager.package('/root/foo.js', true, 'source_map_url') .then(function(p) { - expect(p.addModule.mock.calls[0]).toEqual([ - 'lol transformed /root/foo.js lol', - 'source /root/foo.js', - '/root/foo.js' - ]); - expect(p.addModule.mock.calls[1]).toEqual([ - 'lol transformed /root/bar.js lol', - 'source /root/bar.js', - '/root/bar.js' - ]); + expect(p.addModule.mock.calls[0][0]).toEqual({ + code: 'lol transformed /root/foo.js lol', + map: 'sourcemap /root/foo.js', + sourceCode: 'source /root/foo.js', + sourcePath: '/root/foo.js', + }); + + expect(p.addModule.mock.calls[1][0]).toEqual({ + code: 'lol transformed /root/bar.js lol', + map: 'sourcemap /root/bar.js', + sourceCode: 'source /root/bar.js', + sourcePath: '/root/bar.js' + }); var imgModule_DEPRECATED = { __packager_asset: true, @@ -138,15 +143,15 @@ describe('Packager', function() { deprecated: true, }; - expect(p.addModule.mock.calls[2]).toEqual([ - 'lol module.exports = ' + + expect(p.addModule.mock.calls[2][0]).toEqual({ + code: 'lol module.exports = ' + JSON.stringify(imgModule_DEPRECATED) + '; lol', - 'module.exports = ' + + sourceCode: 'module.exports = ' + JSON.stringify(imgModule_DEPRECATED) + ';', - '/root/img/img.png' - ]); + sourcePath: '/root/img/img.png' + }); var imgModule = { __packager_asset: true, @@ -160,21 +165,21 @@ describe('Packager', function() { type: 'png', }; - expect(p.addModule.mock.calls[3]).toEqual([ - 'lol module.exports = ' + + expect(p.addModule.mock.calls[3][0]).toEqual({ + code: 'lol module.exports = ' + JSON.stringify(imgModule) + '; lol', - 'module.exports = ' + + sourceCode: 'module.exports = ' + JSON.stringify(imgModule) + ';', - '/root/img/new_image.png' - ]); + sourcePath: '/root/img/new_image.png' + }); - expect(p.addModule.mock.calls[4]).toEqual([ - 'lol module.exports = {"json":true}; lol', - 'module.exports = {"json":true};', - '/root/file.json' - ]); + expect(p.addModule.mock.calls[4][0]).toEqual({ + code: 'lol module.exports = {"json":true}; lol', + sourceCode: 'module.exports = {"json":true};', + sourcePath: '/root/file.json' + }); expect(p.finalize.mock.calls[0]).toEqual([ {runMainModule: true} @@ -189,5 +194,4 @@ describe('Packager', function() { ]); }); }); - }); diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index 8563e27280f3c3..c03a0432c20c26 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -14,9 +14,9 @@ var path = require('path'); var Promise = require('bluebird'); var Transformer = require('../JSTransformer'); var DependencyResolver = require('../DependencyResolver'); -var _ = require('underscore'); var Package = require('./Package'); var Activity = require('../Activity'); +var ModuleTransport = require('../lib/ModuleTransport'); var declareOpts = require('../lib/declareOpts'); var imageSize = require('image-size'); @@ -125,12 +125,8 @@ Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) { .then(function(transformedModules) { Activity.endEvent(transformEventId); - transformedModules.forEach(function(transformed) { - ppackage.addModule( - transformed.code, - transformed.sourceCode, - transformed.sourcePath - ); + transformedModules.forEach(function(moduleTransport) { + ppackage.addModule(moduleTransport); }); ppackage.finalize({ runMainModule: runModule }); @@ -163,11 +159,13 @@ Packager.prototype._transformModule = function(ppackage, module) { var resolver = this._resolver; return transform.then(function(transformed) { - return _.extend( - {}, - transformed, - {code: resolver.wrapModule(module, transformed.code)} - ); + var code = resolver.wrapModule(module, transformed.code); + return new ModuleTransport({ + code: code, + map: transformed.map, + sourceCode: transformed.sourceCode, + sourcePath: transformed.sourcePath, + }); }); }; @@ -191,11 +189,12 @@ Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) { var code = 'module.exports = ' + JSON.stringify(img) + ';'; - return { + return new ModuleTransport({ code: code, sourceCode: code, sourcePath: module.path, - }; + virtual: true, + }); }); }; @@ -222,11 +221,12 @@ Packager.prototype.generateAssetModule = function(ppackage, module) { var code = 'module.exports = ' + JSON.stringify(img) + ';'; - return { + return new ModuleTransport({ code: code, sourceCode: code, sourcePath: module.path, - }; + virtual: true, + }); }); }; @@ -234,11 +234,12 @@ function generateJSONModule(module) { return readFile(module.path).then(function(data) { var code = 'module.exports = ' + data.toString('utf8') + ';'; - return { + return new ModuleTransport({ code: code, sourceCode: code, sourcePath: module.path, - }; + virtual: true, + }); }); } diff --git a/packager/react-packager/src/lib/ModuleTransport.js b/packager/react-packager/src/lib/ModuleTransport.js new file mode 100644 index 00000000000000..a5f1d5689106df --- /dev/null +++ b/packager/react-packager/src/lib/ModuleTransport.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +function ModuleTransport(data) { + assertExists(data, 'code'); + this.code = data.code; + + assertExists(data, 'sourceCode'); + this.sourceCode = data.sourceCode; + + assertExists(data, 'sourcePath'); + this.sourcePath = data.sourcePath; + + this.virtual = data.virtual; + + if (this.virtual && data.map) { + throw new Error('Virtual modules cannot have source maps'); + } + + this.map = data.map; + + Object.freeze(this); +} + +module.exports = ModuleTransport; + +function assertExists(obj, field) { + if (obj[field] == null) { + throw new Error('Modules must have `' + field + '`'); + } +} From 28e6e993c6c536a198eb2d841aae21cda4f1df08 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Fri, 1 May 2015 17:50:20 -0700 Subject: [PATCH 171/250] [ReactNative] Navigator focus handler context fix --- Libraries/CustomComponents/Navigator/Navigator.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 280d8c4dd4f430..f6fd60888287e0 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -231,16 +231,13 @@ var Navigator = React.createClass({ initialRouteStack: PropTypes.arrayOf(PropTypes.object), /** - * Will emit the target route upon mounting and before each nav transition, - * overriding the handler in this.props.navigator. This overrides the onDidFocus - * handler that would be found in this.props.navigator + * Will emit the target route upon mounting and before each nav transition */ onWillFocus: PropTypes.func, /** * Will be called with the new route of each scene after the transition is - * complete or after the initial mounting. This overrides the onDidFocus - * handler that would be found in this.props.navigator + * complete or after the initial mounting */ onDidFocus: PropTypes.func, @@ -601,7 +598,8 @@ var Navigator = React.createClass({ this._lastDidFocus = route; if (this.props.onDidFocus) { this.props.onDidFocus(route); - } else if (this.parentNavigator && this.parentNavigator.onDidFocus) { + } + if (this.parentNavigator && this.parentNavigator.onDidFocus) { this.parentNavigator.onDidFocus(route); } }, @@ -617,7 +615,8 @@ var Navigator = React.createClass({ } if (this.props.onWillFocus) { this.props.onWillFocus(route); - } else if (this.parentNavigator && this.parentNavigator.onWillFocus) { + } + if (this.parentNavigator && this.parentNavigator.onWillFocus) { this.parentNavigator.onWillFocus(route); } }, From c0d77dbe9c43e9ad27e4c39e2f6fa48fb9a6ca3b Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Fri, 1 May 2015 18:59:59 -0700 Subject: [PATCH 172/250] [ReactNative] Navigator pop specify route to pop Summary: Now you can pass a route to the pop function in the navigator context, and the Navigator will pop that scene off the stack and every scene that follows. This changes the request API that bubbles up to the top-level navigator. The `pop` request had previously taken a route for `popToRoute`, but that is a more obscure use case. This makes the request API more closely match the context method naming. @public Test Plan: Verified this works by popping several routes in AdsManager. Experimented with UIExplorer Navigator example to make sure popToRoute still works as expected --- .../CustomComponents/Navigator/Navigator.js | 36 +++++++++++++++---- .../Navigator/NavigatorInterceptor.js | 2 ++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index f6fd60888287e0..b740cfaead244d 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -387,12 +387,12 @@ var Navigator = React.createClass({ return this._handleRequest.apply(null, arguments); }, - requestPop: function() { - return this.request('pop'); + requestPop: function(popToBeforeRoute) { + return this.request('pop', popToBeforeRoute); }, requestPopTo: function(route) { - return this.request('pop', route); + return this.request('popTo', route); }, _handleRequest: function(action, arg1, arg2) { @@ -403,6 +403,8 @@ var Navigator = React.createClass({ switch (action) { case 'pop': return this._handlePop(arg1); + case 'popTo': + return this._handlePopTo(arg1); case 'push': return this._handlePush(arg1); default: @@ -411,11 +413,31 @@ var Navigator = React.createClass({ } }, - _handlePop: function(route) { - if (route) { - var hasRoute = this.state.routeStack.indexOf(route) !== -1; + _handlePop: function(popToBeforeRoute) { + if (popToBeforeRoute) { + var popToBeforeRouteIndex = this.state.routeStack.indexOf(popToBeforeRoute); + if (popToBeforeRouteIndex === -1) { + return false; + } + invariant( + popToBeforeRouteIndex <= this.state.presentedIndex, + 'Cannot pop past a route that is forward in the navigator' + ); + this._popN(this.state.presentedIndex - popToBeforeRouteIndex + 1); + return true; + } + if (this.state.presentedIndex === 0) { + return false; + } + this.pop(); + return true; + }, + + _handlePopTo: function(destRoute) { + if (destRoute) { + var hasRoute = this.state.routeStack.indexOf(destRoute) !== -1; if (hasRoute) { - this.popToRoute(route); + this.popToRoute(destRoute); return true; } else { return false; diff --git a/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js b/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js index e70820435ddaef..efff4c9dc729e1 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js +++ b/Libraries/CustomComponents/Navigator/NavigatorInterceptor.js @@ -79,6 +79,8 @@ var NavigatorInterceptor = React.createClass({ switch (action) { case 'pop': return this.props.onPopRequest && this.props.onPopRequest(arg1, arg2); + case 'popTo': + return this.props.onPopToRequest && this.props.onPopToRequest(arg1, arg2); case 'push': return this.props.onPushRequest && this.props.onPushRequest(arg1, arg2); default: From 30a479db8b9d9fa8b09030201cd533f9bd175400 Mon Sep 17 00:00:00 2001 From: Emily Eisenberg Date: Fri, 1 May 2015 18:29:43 -0700 Subject: [PATCH 173/250] Make AsyncStorage types match the implementations Summary: The `getAllKeys` callback type was completely missing the return value, and all of the callbacks are optional. This just fixes the types to match what the implementations are doing. Closes https://github.com/facebook/react-native/pull/1079 Github Author: Emily Eisenberg Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Storage/AsyncStorage.ios.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Libraries/Storage/AsyncStorage.ios.js b/Libraries/Storage/AsyncStorage.ios.js index 5ee2dc5e355b1e..fe92f5c58c5b96 100644 --- a/Libraries/Storage/AsyncStorage.ios.js +++ b/Libraries/Storage/AsyncStorage.ios.js @@ -37,7 +37,7 @@ var AsyncStorage = { */ getItem: function( key: string, - callback: (error: ?Error, result: ?string) => void + callback?: ?(error: ?Error, result: ?string) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiGet([key], function(errors, result) { @@ -60,7 +60,7 @@ var AsyncStorage = { setItem: function( key: string, value: string, - callback: ?(error: ?Error) => void + callback?: ?(error: ?Error) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiSet([[key,value]], function(errors) { @@ -78,7 +78,7 @@ var AsyncStorage = { */ removeItem: function( key: string, - callback: ?(error: ?Error) => void + callback?: ?(error: ?Error) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiRemove([key], function(errors) { @@ -100,7 +100,7 @@ var AsyncStorage = { mergeItem: function( key: string, value: string, - callback: ?(error: ?Error) => void + callback?: ?(error: ?Error) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiMerge([[key,value]], function(errors) { @@ -119,7 +119,7 @@ var AsyncStorage = { * don't want to call this - use removeItem or multiRemove to clear only your * own keys instead. Returns a `Promise` object. */ - clear: function(callback: ?(error: ?Error) => void): Promise { + clear: function(callback?: ?(error: ?Error) => void): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.clear(function(error) { callback && callback(convertError(error)); @@ -135,7 +135,7 @@ var AsyncStorage = { /** * Gets *all* keys known to the system, for all callers, libraries, etc. Returns a `Promise` object. */ - getAllKeys: function(callback: (error: ?Error) => void): Promise { + getAllKeys: function(callback?: ?(error: ?Error, keys: ?Array) => void): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.getAllKeys(function(error, keys) { callback && callback(convertError(error), keys); @@ -166,7 +166,7 @@ var AsyncStorage = { */ multiGet: function( keys: Array, - callback: (errors: ?Array, result: ?Array>) => void + callback?: ?(errors: ?Array, result: ?Array>) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiGet(keys, function(errors, result) { @@ -189,7 +189,7 @@ var AsyncStorage = { */ multiSet: function( keyValuePairs: Array>, - callback: ?(errors: ?Array) => void + callback?: ?(errors: ?Array) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiSet(keyValuePairs, function(errors) { @@ -209,7 +209,7 @@ var AsyncStorage = { */ multiRemove: function( keys: Array, - callback: ?(errors: ?Array) => void + callback?: ?(errors: ?Array) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiRemove(keys, function(errors) { @@ -232,7 +232,7 @@ var AsyncStorage = { */ multiMerge: function( keyValuePairs: Array>, - callback: ?(errors: ?Array) => void + callback?: ?(errors: ?Array) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiMerge(keyValuePairs, function(errors) { From a4616bff3fd397d5d37efe15596b439ed0e96185 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Sat, 2 May 2015 10:09:36 -0700 Subject: [PATCH 174/250] [ReactNative] Test case for text update bug --- Libraries/Text/TextUpdateTest.js | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Libraries/Text/TextUpdateTest.js diff --git a/Libraries/Text/TextUpdateTest.js b/Libraries/Text/TextUpdateTest.js new file mode 100644 index 00000000000000..367ccd1fcda9da --- /dev/null +++ b/Libraries/Text/TextUpdateTest.js @@ -0,0 +1,64 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @providesModule TextUpdateTest + * @flow + */ +'use strict'; + +var React = require('react-native'); +var TimerMixin = require('react-timer-mixin'); +var { + NativeModules, + StyleSheet, + Text, +} = React; + +var MIX_TYPES = false; // TODO(#6916648): fix bug and set true + +var TestManager = NativeModules.TestManager || NativeModules.SnapshotTestManager; + +var TextUpdateTest = React.createClass({ + mixins: [TimerMixin], + getInitialState: function() { + return {seeMore: true}; + }, + componentDidMount: function() { + this.requestAnimationFrame( + () => this.setState( + {seeMore: false}, + TestManager.markTestCompleted + ) + ); + }, + render: function() { + var extraText = MIX_TYPES ? 'raw text' : wrapped text; + return ( + this.setState({seeMore: !this.state.seeMore})}> + Tap to see more (bugs)... + {this.state.seeMore && extraText} + + ); + }, +}); + +var styles = StyleSheet.create({ + container: { + margin: 10, + marginTop: 100, + }, +}); + +module.exports = TextUpdateTest; From 5e110d277651e9b5f7d70f31d4807451b3959871 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Sat, 2 May 2015 10:09:37 -0700 Subject: [PATCH 175/250] [ReactNative] Fix Text Updating Crash Summary: @public {D1953613} added an optimization that allowed for shadow nodes that are not backed by views, but didn't actually work robustly in the remove case because the indices can get out of sync. That diff also started returning nil for raw text nodes, which triggered this bug and broke "see more" functionality in the `FBTextWithEntities` and `ExpandingText` components, leading to crashes in the Groups app. This diff fixes the issue by simply returning `UIView` placeholders again. Slight perf/ memory cost but no more crashes and there should be no other adverse affects. We'll need to think up something more clever in order to properly support `nil` views in the future, probably something that uses the shadow hierarchy to build the View hierarchy, rather than mirroring identical commands to both - see #1102. Test Plan: - TextUpdateTest fails without native changes, now passes with them. - ExpandingText example no longer crashes. - See More in Groups app no longer crashes. --- Libraries/Text/RCTRawTextManager.m | 2 +- Libraries/Text/TextUpdateTest.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Libraries/Text/RCTRawTextManager.m b/Libraries/Text/RCTRawTextManager.m index 221b8daebb6b59..b6ad9b1101dbb0 100644 --- a/Libraries/Text/RCTRawTextManager.m +++ b/Libraries/Text/RCTRawTextManager.m @@ -17,7 +17,7 @@ @implementation RCTRawTextManager - (UIView *)view { - return nil; + return [[UIView alloc] init]; // TODO(#1102) Remove useless views. } - (RCTShadowView *)shadowView diff --git a/Libraries/Text/TextUpdateTest.js b/Libraries/Text/TextUpdateTest.js index 367ccd1fcda9da..c4218f73d5a40a 100644 --- a/Libraries/Text/TextUpdateTest.js +++ b/Libraries/Text/TextUpdateTest.js @@ -24,8 +24,6 @@ var { Text, } = React; -var MIX_TYPES = false; // TODO(#6916648): fix bug and set true - var TestManager = NativeModules.TestManager || NativeModules.SnapshotTestManager; var TextUpdateTest = React.createClass({ @@ -42,13 +40,12 @@ var TextUpdateTest = React.createClass({ ); }, render: function() { - var extraText = MIX_TYPES ? 'raw text' : wrapped text; return ( this.setState({seeMore: !this.state.seeMore})}> Tap to see more (bugs)... - {this.state.seeMore && extraText} + {this.state.seeMore && 'raw text'} ); }, From 59997df1c15e33b08afe098a2d2d0d59358c82e4 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Sat, 2 May 2015 10:09:58 -0700 Subject: [PATCH 176/250] [ReactNative] Fix warnings w/h => width/height --- Libraries/Components/ScrollResponder.js | 2 +- Libraries/Components/View/ViewStylePropTypes.js | 2 +- Libraries/Utilities/differ/sizesDiffer.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index 1f3d493f3c5167..74be17a46ea104 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -355,7 +355,7 @@ var ScrollResponderMixin = { /** * A helper function to zoom to a specific rect in the scrollview. - * @param {object} rect Should have shape {x, y, w, h} + * @param {object} rect Should have shape {x, y, width, height} */ scrollResponderZoomTo: function(rect: { x: number; y: number; width: number; height: number; }) { RCTUIManagerDeprecated.zoomToRect(this.getNodeHandle(), rect); diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index 7bb795f160972b..a5b591a61f8ef6 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -30,7 +30,7 @@ var ViewStylePropTypes = { overflow: ReactPropTypes.oneOf(['visible', 'hidden']), shadowColor: ReactPropTypes.string, shadowOffset: ReactPropTypes.shape( - {h: ReactPropTypes.number, w: ReactPropTypes.number} + {width: ReactPropTypes.number, height: ReactPropTypes.number} ), shadowOpacity: ReactPropTypes.number, shadowRadius: ReactPropTypes.number, diff --git a/Libraries/Utilities/differ/sizesDiffer.js b/Libraries/Utilities/differ/sizesDiffer.js index 3bdc72acb23384..a431cd072273c8 100644 --- a/Libraries/Utilities/differ/sizesDiffer.js +++ b/Libraries/Utilities/differ/sizesDiffer.js @@ -5,14 +5,14 @@ */ 'use strict'; -var dummySize = {w: undefined, h: undefined}; +var dummySize = {width: undefined, height: undefined}; var sizesDiffer = function(one, two) { one = one || dummySize; two = two || dummySize; return one !== two && ( - one.w !== two.w || - one.h !== two.h + one.width !== two.width || + one.height !== two.height ); }; From 8c2f44d64014daf60483fb60eca995cdbb3e01d4 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Sat, 2 May 2015 09:46:49 -0700 Subject: [PATCH 177/250] [react-native] Don't mutate props in TouchableBounce --- Libraries/Components/Touchable/TouchableBounce.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 7cba2216472945..30d05210fb4a2f 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -11,14 +11,13 @@ */ 'use strict'; +var AnimationExperimental = require('AnimationExperimental'); var NativeMethodsMixin = require('NativeMethodsMixin'); -var React = require('React'); var POPAnimation = require('POPAnimation'); -var AnimationExperimental = require('AnimationExperimental'); +var React = require('React'); var Touchable = require('Touchable'); var merge = require('merge'); -var copyProperties = require('copyProperties'); var onlyChild = require('onlyChild'); type State = { @@ -123,9 +122,8 @@ var TouchableBounce = React.createClass({ }, render: function() { - // Note(vjeux): use cloneWithProps once React has been upgraded var child = onlyChild(this.props.children); - copyProperties(child.props, { + return React.cloneElement(child, { accessible: true, testID: this.props.testID, onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, @@ -135,7 +133,6 @@ var TouchableBounce = React.createClass({ onResponderRelease: this.touchableHandleResponderRelease, onResponderTerminate: this.touchableHandleResponderTerminate }); - return child; } }); From 17262db5a986430991bc15e44ef879aa8ab061c2 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Sat, 2 May 2015 13:12:12 -0700 Subject: [PATCH 178/250] [ReactNative] Fix JS calls being lost --- React/Base/RCTBridge.m | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 85486ddc1d82c1..134b3659db974e 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -1146,15 +1146,12 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg { #if BATCHED_BRIDGE - __weak NSMutableArray *weakScheduledCalls = _scheduledCalls; - __weak RCTSparseArray *weakScheduledCallbacks = _scheduledCallbacks; - + __weak RCTBridge *weakSelf = self; [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ RCTProfileBeginEvent(); - NSMutableArray *scheduledCalls = weakScheduledCalls; - RCTSparseArray *scheduledCallbacks = weakScheduledCallbacks; - if (!scheduledCalls || !scheduledCallbacks) { + RCTBridge *strongSelf = weakSelf; + if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) { return; } @@ -1170,7 +1167,7 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg * Keep going if it any event emmiter, e.g. RCT(Device|NativeApp)?EventEmitter */ if ([moduleName hasSuffix:@"EventEmitter"]) { - for (NSDictionary *call in [scheduledCalls copy]) { + for (NSDictionary *call in [strongSelf->_scheduledCalls copy]) { NSArray *callArgs = call[@"args"]; /** * If it's the same module && method call on the bridge && @@ -1193,7 +1190,7 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg [args[2][0] isEqual:callArgs[2][0]] && ([moduleName isEqualToString:@"RCTEventEmitter"] ? [args[2][1] isEqual:callArgs[2][1]] : YES) ) { - [scheduledCalls removeObject:call]; + [strongSelf->_scheduledCalls removeObject:call]; } } } @@ -1208,9 +1205,9 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg }; if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) { - scheduledCallbacks[args[0]] = call; + strongSelf->_scheduledCallbacks[args[0]] = call; } else { - [scheduledCalls addObject:call]; + [strongSelf->_scheduledCalls addObject:call]; } RCTProfileEndEvent(@"enqueue_call", @"objc_call", call); From 09460cf21b7b796af169cf45910d6fb461706a29 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Sat, 2 May 2015 14:11:09 -0700 Subject: [PATCH 179/250] [ReactNative] Use explicit doubles on RCTLocationOptions to avoid NSInvocation bug --- Libraries/Geolocation/RCTLocationObserver.m | 6 +++--- React/Base/RCTBridge.m | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index 3e864657b82c5d..f21233dbfa0576 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -28,9 +28,9 @@ typedef NS_ENUM(NSInteger, RCTPositionErrorCode) { #define RCT_DEFAULT_LOCATION_ACCURACY kCLLocationAccuracyHundredMeters typedef struct { - NSTimeInterval timeout; - NSTimeInterval maximumAge; - CLLocationAccuracy accuracy; + double timeout; + double maximumAge; + double accuracy; } RCTLocationOptions; @implementation RCTConvert (RCTLocationOptions) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 134b3659db974e..9ce2e037048c0c 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -381,10 +381,8 @@ - (instancetype)initWithReactMethodName:(NSString *)reactMethodName case '{': { [argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) { - NSUInteger size; - NSGetSizeAndAlignment(argumentType, &size, NULL); - void *returnValue = malloc(size); NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector]; + void *returnValue = malloc(methodSignature.methodReturnLength); NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [_invocation setTarget:[RCTConvert class]]; [_invocation setSelector:selector]; From d29a0c67685e19fb7a3c5e2fb030c5e29011af7f Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sat, 2 May 2015 18:43:34 -0700 Subject: [PATCH 180/250] Fix for nil array crash --- React/Base/RCTBridge.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 9ce2e037048c0c..22fce74fca07bb 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -49,7 +49,7 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { * Temporarily allow to turn on and off the call batching in case someone wants * to profile both */ -#define BATCHED_BRIDGE 1 +#define BATCHED_BRIDGE 0 #ifdef __LP64__ typedef uint64_t RCTHeaderValue; @@ -206,10 +206,14 @@ - (void)_invokeAndProcessModule:(NSString *)module arguments:(NSArray *)args context:(NSNumber *)context; +#if BATCHED_BRIDGE + - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context; +#endif + @end /** From 2f9cc9723fa63ebc86f1df5d999ca4968757c95a Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Sat, 2 May 2015 19:51:18 -0700 Subject: [PATCH 181/250] Fix scrollview doc type - canimprove -> can improve --- Libraries/Components/ScrollView/ScrollView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index e66612b22886c0..72e8ffa5c53159 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -186,7 +186,7 @@ var ScrollView = React.createClass({ /** * Experimental: When true, offscreen child views (whose `overflow` value is * `hidden`) are removed from their native backing superview when offscreen. - * This canimprove scrolling performance on long lists. The default value is + * This can improve scrolling performance on long lists. The default value is * false. */ removeClippedSubviews: PropTypes.bool, From c5a6ec5b53f333db58e35d08c5c2cad22b5b3b42 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sun, 3 May 2015 15:39:27 -0700 Subject: [PATCH 182/250] Disable React Native dev menu in release mode --- React/Base/RCTDevMenu.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 82b4fa968ae03b..c6063caa2301b2 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -66,7 +66,9 @@ + (void)initialize // We're swizzling here because it's poor form to override methods in a category, // however UIWindow doesn't actually implement motionEnded:withEvent:, so there's // no need to call the original implementation. +#if RCT_DEV RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:)); +#endif } - (instancetype)init From 693ad91f860dfd0fd9faebed1e24b66b71d20f14 Mon Sep 17 00:00:00 2001 From: Zhao Han Date: Mon, 4 May 2015 00:27:33 -0500 Subject: [PATCH 183/250] Update website header link github url HTTPS ``` $ curl -I http://github.com/facebook/react-native HTTP/1.1 301 Moved Permanently Content-length: 0 Location: https://github.com/facebook/react-native Connection: close ``` This saves some network time. --- website/core/HeaderLinks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/core/HeaderLinks.js b/website/core/HeaderLinks.js index 7a481ed2959571..32c371a7dcf2f3 100644 --- a/website/core/HeaderLinks.js +++ b/website/core/HeaderLinks.js @@ -17,7 +17,7 @@ var HeaderLinks = React.createClass({ {section: 'support', href: '/react-native/support.html', text: 'Support'}, ], linksExternal: [ - {section: 'github', href: 'http://github.com/facebook/react-native', text: 'GitHub'}, + {section: 'github', href: 'https://github.com/facebook/react-native', text: 'GitHub'}, {section: 'react', href: 'http://facebook.github.io/react', text: 'React'}, ], From 548a0a6a4fc8df4c528e244fe3bf939291a73961 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Mon, 4 May 2015 02:40:45 -0700 Subject: [PATCH 184/250] [ReactNative] Fix Navigator scene hiding logic Summary: Scenes with 0 opacity are being rendered on top of other scenes with full absolute positioning. On iOS this is fine, because the platform will not send touch events to a view with no opacity. On Android is poses a problem because the view on top, even with no opacity, is catching the touch events for the presented scene below. This change enhances the scene enabling and hiding logic, has better naming, and improves the documentation of it. @public Test Plan: Tested transitions and gestures in slow motion on iOS and Android --- .../CustomComponents/Navigator/Navigator.js | 99 +++++++++++++++---- 1 file changed, 80 insertions(+), 19 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index b740cfaead244d..6d09b656e38b7d 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -28,6 +28,7 @@ var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule; var BackAndroid = require('BackAndroid'); +var Dimensions = require('Dimensions'); var InteractionMixin = require('InteractionMixin'); var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar'); var NavigatorInterceptor = require('NavigatorInterceptor'); @@ -39,6 +40,7 @@ var Platform = require('Platform'); var React = require('React'); var StaticContainer = require('StaticContainer.react'); var StyleSheet = require('StyleSheet'); +var StyleSheetRegistry = require('StyleSheetRegistry'); var Subscribable = require('Subscribable'); var TimerMixin = require('react-timer-mixin'); var View = require('View'); @@ -52,13 +54,37 @@ var rebound = require('rebound'); var PropTypes = React.PropTypes; -var OFF_SCREEN = {style: {opacity: 0}}; +var SCREEN_WIDTH = Dimensions.get('window').width; +var SCENE_DISABLED_NATIVE_PROPS = { + style: { + left: SCREEN_WIDTH, + opacity: 0, + }, +}; var __uid = 0; function getuid() { return __uid++; } +function resolveStyle(styles) { + // The styles for a scene are a prop in the style format, which can be an object, + // a number (which refers to the StyleSheetRegistry), or an arry of objects/numbers. + // This function resolves the actual style values so we can call setNativeProps with + // matching styles. + var resolvedStyle = {}; + if (!Array.isArray(styles)) { + styles = [styles]; + } + styles.forEach((style) => { + if (typeof style === 'number') { + style = StyleSheetRegistry.getStyleByID(style); + } + resolvedStyle = merge(resolvedStyle, style); + }); + return resolvedStyle; +} + // styles moved to the top of the file so getDefaultProps can refer to it var styles = StyleSheet.create({ container: { @@ -72,7 +98,7 @@ var styles = StyleSheet.create({ bottom: 0, top: 0, }, - currentScene: { + baseScene: { position: 'absolute', overflow: 'hidden', left: 0, @@ -80,11 +106,8 @@ var styles = StyleSheet.create({ bottom: 0, top: 0, }, - futureScene: { - overflow: 'hidden', - position: 'absolute', - left: 0, - opacity: 0, + disabledScene: { + left: SCREEN_WIDTH, }, transitioner: { flex: 1, @@ -571,10 +594,12 @@ var Navigator = React.createClass({ }, /** - * This happens at the end of a transition started by transitionTo + * This happens at the end of a transition started by transitionTo, and when the spring catches up to a pending gesture */ _completeTransition: function() { if (this.spring.getCurrentValue() !== 1) { + // The spring has finished catching up to a gesture in progress. Remove the pending progress + // and we will be in a normal activeGesture state if (this.state.pendingGestureProgress) { this.state.pendingGestureProgress = null; } @@ -599,11 +624,16 @@ var Navigator = React.createClass({ this._interactionHandle = null; } if (this.state.pendingGestureProgress) { + // A transition completed, but there is already another gesture happening. + // Enable the scene and set the spring to catch up with the new gesture + var gestureToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); + this._enableScene(gestureToIndex); this.spring.setEndValue(this.state.pendingGestureProgress); return; } if (this.state.transitionQueue.length) { var queuedTransition = this.state.transitionQueue.shift(); + this._enableScene(queuedTransition.destIndex); this._transitionTo( queuedTransition.destIndex, queuedTransition.velocity, @@ -644,21 +674,44 @@ var Navigator = React.createClass({ }, /** - * Does not delete the scenes - merely hides them. + * Hides scenes that we are not currently on or transitioning from */ _hideScenes: function() { for (var i = 0; i < this.state.routeStack.length; i++) { - // This gets called when we detach a gesture, so there will not be a - // current gesture, but there might be a transition in progress if (i === this.state.presentedIndex || i === this.state.transitionFromIndex) { continue; } - var sceneRef = 'scene_' + i; - this.refs[sceneRef] && - this.refs['scene_' + i].setNativeProps(OFF_SCREEN); + this._disableScene(i); } }, + /** + * Push a scene off the screen, so that opacity:0 scenes will not block touches sent to the presented scenes + */ + _disableScene: function(sceneIndex) { + this.refs['scene_' + sceneIndex] && + this.refs['scene_' + sceneIndex].setNativeProps(SCENE_DISABLED_NATIVE_PROPS); + }, + + /** + * Put the scene back into the state as defined by props.sceneStyle, so transitions can happen normally + */ + _enableScene: function(sceneIndex) { + // First, determine what the defined styles are for scenes in this navigator + var sceneStyle = resolveStyle(this.props.sceneStyle); + // Then restore the left value for this scene + var enabledSceneNativeProps = { + left: sceneStyle.left, + }; + if (sceneIndex !== this.state.transitionFromIndex) { + // If we are not in a transition from this index, make sure opacity is 0 + // to prevent the enabled scene from flashing over the presented scene + enabledSceneNativeProps.opacity = 0; + } + this.refs['scene_' + sceneIndex] && + this.refs['scene_' + sceneIndex].setNativeProps(enabledSceneNativeProps); + }, + _onAnimationStart: function() { var fromIndex = this.state.presentedIndex; var toIndex = this.state.presentedIndex; @@ -669,7 +722,6 @@ var Navigator = React.createClass({ } this._setRenderSceneToHarwareTextureAndroid(fromIndex, true); this._setRenderSceneToHarwareTextureAndroid(toIndex, true); - var navBar = this._navBar; if (navBar && navBar.onAnimationStart) { navBar.onAnimationStart(fromIndex, toIndex); @@ -801,6 +853,8 @@ var Navigator = React.createClass({ _attachGesture: function(gestureId) { this.state.activeGesture = gestureId; + var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); + this._enableScene(gesturingToIndex); }, _detachGesture: function() { @@ -831,6 +885,7 @@ var Navigator = React.createClass({ (gesture.fullDistance - gestureDetectMovement); if (nextProgress < 0 && gesture.isDetachable) { this._detachGesture(); + this.spring.setCurrentValue(0); } if (this._doesGestureOverswipe(this.state.activeGesture)) { var frictionConstant = gesture.overswipe.frictionConstant; @@ -945,6 +1000,7 @@ var Navigator = React.createClass({ _jumpN: function(n) { var destIndex = this._getDestIndexWithinBounds(n); var requestTransitionAndResetUpdatingRange = () => { + this._enableScene(destIndex); this._transitionTo(destIndex); this._resetUpdatingRange(); }; @@ -978,12 +1034,14 @@ var Navigator = React.createClass({ var activeIDStack = this.state.idStack.slice(0, activeLength); var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength); var nextStack = activeStack.concat([route]); + var destIndex = nextStack.length - 1; var nextIDStack = activeIDStack.concat([getuid()]); var nextAnimationConfigStack = activeAnimationConfigStack.concat([ this.props.configureScene(route), ]); var requestTransitionAndResetUpdatingRange = () => { - this._transitionTo(nextStack.length - 1); + this._enableScene(destIndex); + this._transitionTo(destIndex); this._resetUpdatingRange(); }; this.setState({ @@ -1004,6 +1062,7 @@ var Navigator = React.createClass({ 'Cannot pop below zero' ); var popIndex = this.state.presentedIndex - n; + this._enableScene(popIndex); this._transitionTo( popIndex, null, // default velocity @@ -1211,8 +1270,10 @@ var Navigator = React.createClass({ route, sceneNavigatorContext ); - var initialSceneStyle = i === this.state.presentedIndex ? - styles.currentScene : styles.futureScene; + var disabledSceneStyle = null; + if (i !== this.state.presentedIndex) { + disabledSceneStyle = styles.disabledScene; + } return ( { return i !== this.state.presentedIndex; }} - style={[initialSceneStyle, this.props.sceneStyle]}> + style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}> {React.cloneElement(child, { ref: this._handleItemRef.bind(null, this.state.idStack[i]), })} From b532ec000fad08f7c4e66b94e276a7dbcf142cfe Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Mon, 4 May 2015 10:41:38 -0700 Subject: [PATCH 185/250] [react-packager] Update sane to use watch-project --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 40a3e9b517d0a7..54d7ada11c2288 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native", - "version": "0.3.7", + "version": "0.4.1", "description": "A framework for building native apps using React", "repository": { "type": "git", @@ -57,7 +57,7 @@ "react-timer-mixin": "^0.13.1", "react-tools": "0.13.2", "rebound": "^0.0.12", - "sane": "1.0.3", + "sane": "^1.1.1", "source-map": "0.1.31", "stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638", "uglify-js": "~2.4.16", From 132a9170f12b262fd15226d38b7ba83285d76da9 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 4 May 2015 10:35:49 -0700 Subject: [PATCH 186/250] [ReactNative] Create private underlying bridge to prevent retain cycles --- Libraries/RCTTest/RCTTestRunner.m | 10 +- Libraries/Utilities/MessageQueue.js | 33 +- React/Base/RCTBridge.h | 19 - React/Base/RCTBridge.m | 561 +++++++++++++++++----------- React/Base/RCTDevMenu.m | 20 +- React/Base/RCTJavaScriptLoader.h | 2 +- React/Base/RCTJavaScriptLoader.m | 18 +- React/Base/RCTRootView.h | 2 +- React/Base/RCTRootView.m | 130 +++++-- React/Modules/RCTTiming.m | 7 +- React/Modules/RCTUIManager.m | 31 +- React/Views/RCTNavigator.m | 8 +- 12 files changed, 507 insertions(+), 334 deletions(-) diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index 75a8118318ee10..0aa148fbc889bb 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -17,6 +17,12 @@ #define TIMEOUT_SECONDS 240 +@interface RCTBridge (RCTTestRunner) + +@property (nonatomic, weak) RCTBridge *batchedBridge; + +@end + @implementation RCTTestRunner { FBSnapshotTestController *_testController; @@ -66,7 +72,7 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictiona rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]); - RCTTestModule *testModule = rootView.bridge.modules[testModuleName]; + RCTTestModule *testModule = rootView.bridge.batchedBridge.modules[testModuleName]; testModule.controller = _testController; testModule.testSelector = test; testModule.view = rootView; @@ -83,8 +89,6 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictiona error = [[RCTRedBox sharedInstance] currentErrorMessage]; } [rootView removeFromSuperview]; - [rootView.bridge invalidate]; - [rootView invalidate]; RCTAssert(vc.view.subviews.count == 0, @"There shouldn't be any other views: %@", vc.view); vc.view = nil; [[RCTRedBox sharedInstance] dismiss]; diff --git a/Libraries/Utilities/MessageQueue.js b/Libraries/Utilities/MessageQueue.js index 97658c2356b3d6..f15dd70e0d9b84 100644 --- a/Libraries/Utilities/MessageQueue.js +++ b/Libraries/Utilities/MessageQueue.js @@ -322,23 +322,24 @@ var MessageQueueMixin = { processBatch: function(batch) { var self = this; - ReactUpdates.batchedUpdates(function() { - batch.forEach(function(call) { - invariant( - call.module === 'BatchedBridge', - 'All the calls should pass through the BatchedBridge module' - ); - if (call.method === 'callFunctionReturnFlushedQueue') { - self.callFunction.apply(self, call.args); - } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { - self.invokeCallback.apply(self, call.args); - } else { - throw new Error( - 'Unrecognized method called on BatchedBridge: ' + call.method); - } + return guardReturn(function () { + ReactUpdates.batchedUpdates(function() { + batch.forEach(function(call) { + invariant( + call.module === 'BatchedBridge', + 'All the calls should pass through the BatchedBridge module' + ); + if (call.method === 'callFunctionReturnFlushedQueue') { + self._callFunction.apply(self, call.args); + } else if (call.method === 'invokeCallbackAndReturnFlushedQueue') { + self._invokeCallback.apply(self, call.args); + } else { + throw new Error( + 'Unrecognized method called on BatchedBridge: ' + call.method); + } + }); }); - }); - return this.flushedQueue(); + }, null, this._flushedQueueUnguarded, this); }, setLoggingEnabled: function(enabled) { diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 6dfbe2414557f8..77cbb215ec6c24 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -80,15 +80,6 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); __attribute__((used, section("__DATA,RCTImport"))) \ static const char *__rct_import_##module##_##method##__ = #module"."#method; -/** - * This method is used to execute a new application script. It is called - * internally whenever a JS application bundle is loaded/reloaded, but should - * probably not be used at any other time. - */ -- (void)enqueueApplicationScript:(NSString *)script - url:(NSURL *)url - onComplete:(RCTJavaScriptCompleteBlock)onComplete; - /** * URL of the script that was loaded into the bridge. */ @@ -122,14 +113,4 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method; */ - (void)reload; -/** - * Add a new observer that will be called on every screen refresh. - */ -- (void)addFrameUpdateObserver:(id)observer; - -/** - * Stop receiving screen refresh updates for the given observer. - */ -- (void)removeFrameUpdateObserver:(id)observer; - @end diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 22fce74fca07bb..349cc725e13a2b 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -25,6 +25,7 @@ #import "RCTProfile.h" #import "RCTRedBox.h" #import "RCTRootView.h" +#import "RCTSourceCode.h" #import "RCTSparseArray.h" #import "RCTUtils.h" @@ -45,12 +46,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { RCTBridgeFieldFlushDateMillis }; -/** - * Temporarily allow to turn on and off the call batching in case someone wants - * to profile both - */ -#define BATCHED_BRIDGE 0 - #ifdef __LP64__ typedef uint64_t RCTHeaderValue; typedef struct section_64 RCTHeaderSection; @@ -61,6 +56,11 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { #define RCTGetSectByNameFromHeader getsectbynamefromheader #endif +#define RCTAssertJSThread() \ + RCTAssert(![NSStringFromClass([_javaScriptExecutor class]) isEqualToString:@"RCTContextExecutor"] || \ + [[[NSThread currentThread] name] isEqualToString:@"com.facebook.React.JavaScript"], \ + @"This method must be called on JS thread") + NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification"; NSString *const RCTDequeueNotification = @"RCTDequeueNotification"; @@ -122,6 +122,7 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { * RTCBridgeModule protocol to ensure they've been exported. This scanning * functionality is disabled in release mode to improve startup performance. */ +static NSDictionary *RCTModuleIDsByName; static NSArray *RCTModuleNamesByID; static NSArray *RCTModuleClassesByID; static NSArray *RCTBridgeModuleClassesByModuleID(void) @@ -129,8 +130,9 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - RCTModuleNamesByID = [NSMutableArray array]; - RCTModuleClassesByID = [NSMutableArray array]; + RCTModuleIDsByName = [[NSMutableDictionary alloc] init]; + RCTModuleNamesByID = [[NSMutableArray alloc] init]; + RCTModuleClassesByID = [[NSMutableArray alloc] init]; Dl_info info; dladdr(&RCTBridgeModuleClassesByModuleID, &info); @@ -162,7 +164,9 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { NSStringFromClass(cls)); // Register module - [(NSMutableArray *)RCTModuleNamesByID addObject:RCTBridgeModuleNameForClass(cls)]; + NSString *moduleName = RCTBridgeModuleNameForClass(cls); + ((NSMutableDictionary *)RCTModuleIDsByName)[moduleName] = @(RCTModuleNamesByID.count); + [(NSMutableArray *)RCTModuleNamesByID addObject:moduleName]; [(NSMutableArray *)RCTModuleClassesByID addObject:cls]; } } @@ -199,20 +203,31 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { return RCTModuleClassesByID; } +@class RCTBatchedBridge; + @interface RCTBridge () +@property (nonatomic, strong) RCTBatchedBridge *batchedBridge; +@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider; +@property (nonatomic, strong, readwrite) RCTEventDispatcher *eventDispatcher; + - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context; -#if BATCHED_BRIDGE +@end + +@interface RCTBatchedBridge : RCTBridge + +@property (nonatomic, weak) RCTBridge *parentBridge; + +- (instancetype)initWithParentBridge:(RCTBridge *)bridge; - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context; -#endif @end @@ -238,8 +253,6 @@ @implementation RCTModuleMethod dispatch_block_t _methodQueue; } -static Class _globalExecutorClass; - static NSString *RCTStringUpToFirstArgument(NSString *methodName) { NSRange colonRange = [methodName rangeOfString:@":"]; @@ -702,6 +715,7 @@ - (NSString *)description @"methods": [[NSMutableDictionary alloc] init] }; localModules[moduleName] = module; + [RCTLocalModuleNames addObject:moduleName]; } // Add method if it doesn't already exist @@ -712,145 +726,265 @@ - (NSString *)description @"methodID": @(methods.count), @"type": @"local" }; + [RCTLocalMethodNames addObject:methodName]; } // Add module and method lookup RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"]; RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"]; - [RCTLocalModuleNames addObject:moduleName]; - [RCTLocalMethodNames addObject:methodName]; } }); return localModules; } -@interface RCTDisplayLink : NSObject +@interface RCTFrameUpdate (Private) -- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink; @end -@interface RCTBridge (RCTDisplayLink) +@implementation RCTFrameUpdate -- (void)_update:(CADisplayLink *)displayLink; +- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink +{ + if ((self = [super init])) { + _timestamp = displayLink.timestamp; + _deltaTime = displayLink.duration; + } + return self; +} @end -@implementation RCTDisplayLink -{ - __weak RCTBridge *_bridge; - CADisplayLink *_displayLink; - SEL _selector; -} +@implementation RCTBridge -- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector +static id _latestJSExecutor; + +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + moduleProvider:(RCTBridgeModuleProviderBlock)block + launchOptions:(NSDictionary *)launchOptions { + RCTAssertMainThread(); + if ((self = [super init])) { - _bridge = bridge; - _selector = selector; - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update:)]; - [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + /** + * Pre register modules + */ + RCTLocalModulesConfig(); + + _bundleURL = bundleURL; + _moduleProvider = block; + _launchOptions = [launchOptions copy]; + [self bindKeys]; + [self setUp]; } return self; } -- (BOOL)isValid +- (void)dealloc { - return _displayLink != nil; + /** + * This runs only on the main thread, but crashes the subclass + * RCTAssertMainThread(); + */ + [self invalidate]; } -- (void)invalidate +- (void)bindKeys { - if (self.isValid) { - [_displayLink invalidate]; - _displayLink = nil; - } + RCTAssertMainThread(); + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(reload) + name:RCTReloadNotification + object:nil]; + +#if TARGET_IPHONE_SIMULATOR + + __weak RCTBridge *weakSelf = self; + RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; + + // reload in current mode + [commands registerKeyCommandWithInput:@"r" + modifierFlags:UIKeyModifierCommand + action:^(UIKeyCommand *command) { + [weakSelf reload]; + }]; + +#endif + } -- (void)_update:(CADisplayLink *)displayLink +- (void)reload { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [_bridge performSelector:_selector withObject:displayLink]; -#pragma clang diagnostic pop +/** + * AnyThread + */ + dispatch_async(dispatch_get_main_queue(), ^{ + [self invalidate]; + [self setUp]; + }); } -@end +- (void)setUp +{ + RCTAssertMainThread(); -@interface RCTFrameUpdate (Private) + _batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self]; +} -- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink; +- (BOOL)isValid +{ + return _batchedBridge.isValid; +} -@end +- (void)invalidate +{ + RCTAssertMainThread(); -@implementation RCTFrameUpdate + [_batchedBridge invalidate]; + _batchedBridge = nil; +} -- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink ++ (void)logMessage:(NSString *)message level:(NSString *)level { - if ((self = [super init])) { - _timestamp = displayLink.timestamp; - _deltaTime = displayLink.duration; - } - return self; + dispatch_async(dispatch_get_main_queue(), ^{ + if (!_latestJSExecutor.isValid) { + return; + } + + [_latestJSExecutor executeJSCall:@"RCTLog" + method:@"logIfNoNativeHook" + arguments:@[level, message] + context:RCTGetExecutorID(_latestJSExecutor) + callback:^(id json, NSError *error) {}]; + }); +} + +- (NSDictionary *)modules +{ + return _batchedBridge.modules; } +#define RCT_BRIDGE_WARN(...) \ +- (void)__VA_ARGS__ \ +{ \ + RCTLogMustFix(@"Called method \"%@\" on top level bridge. This method should \ + only be called from bridge instance in a bridge module", @(__func__)); \ +} + +RCT_BRIDGE_WARN(enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args) +RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context) + @end -@implementation RCTBridge +@implementation RCTBatchedBridge { + BOOL _loading; + id _javaScriptExecutor; RCTSparseArray *_modulesByID; RCTSparseArray *_queuesByID; dispatch_queue_t _methodQueue; NSDictionary *_modulesByName; - id _javaScriptExecutor; - Class _executorClass; - NSURL *_bundleURL; - RCTBridgeModuleProviderBlock _moduleProvider; - RCTDisplayLink *_displayLink; - RCTDisplayLink *_vsyncDisplayLink; + CADisplayLink *_mainDisplayLink; + CADisplayLink *_jsDisplayLink; NSMutableSet *_frameUpdateObservers; NSMutableArray *_scheduledCalls; RCTSparseArray *_scheduledCallbacks; - BOOL _loading; } -static id _latestJSExecutor; +@synthesize valid = _valid; -- (instancetype)initWithBundleURL:(NSURL *)bundleURL - moduleProvider:(RCTBridgeModuleProviderBlock)block - launchOptions:(NSDictionary *)launchOptions +- (instancetype)initWithParentBridge:(RCTBridge *)bridge { - if ((self = [super init])) { - _bundleURL = bundleURL; - _moduleProvider = block; - _launchOptions = [launchOptions copy]; + if (self = [super init]) { + RCTAssertMainThread(); - [self setUp]; - [self bindKeys]; + _parentBridge = bridge; + + /** + * Set Initial State + */ + _valid = YES; + _loading = YES; + _frameUpdateObservers = [[NSMutableSet alloc] init]; + _scheduledCalls = [[NSMutableArray alloc] init]; + _scheduledCallbacks = [[RCTSparseArray alloc] init]; + _queuesByID = [[RCTSparseArray alloc] init]; + _jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)]; + + /** + * Initialize executor to allow enqueueing calls + */ + Class executorClass = self.executorClass ?: [RCTContextExecutor class]; + _javaScriptExecutor = RCTCreateExecutor(executorClass); + _latestJSExecutor = _javaScriptExecutor; + + /** + * Setup event dispatcher before initializing modules to allow init calls + */ + self.eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; + + /** + * Initialize and register bridge modules *before* adding the display link + * so we don't have threading issues + */ + _methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL); + [self registerModules]; + + /** + * Start the application script + */ + [self initJS]; } return self; } -- (void)setUp +- (NSDictionary *)launchOptions { - Class executorClass = _executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class]; - _javaScriptExecutor = RCTCreateExecutor(executorClass); - _latestJSExecutor = _javaScriptExecutor; - _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; - _methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL); - _frameUpdateObservers = [[NSMutableSet alloc] init]; - _scheduledCalls = [[NSMutableArray alloc] init]; - _scheduledCallbacks = [[RCTSparseArray alloc] init]; + return _parentBridge.launchOptions; +} - [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ - _displayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_jsThreadUpdate:)]; - }]; - _vsyncDisplayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_mainThreadUpdate:)]; +/** + * Override to ensure that we won't create another nested bridge + */ +- (void)setUp {} + +- (void)reload +{ + [_parentBridge reload]; +} + +- (Class)executorClass +{ + return _parentBridge.executorClass; +} + +- (void)setExecutorClass:(Class)executorClass +{ + RCTAssertMainThread(); + + _parentBridge.executorClass = executorClass; +} + +- (BOOL)isLoading +{ + return _loading; +} + +- (BOOL)isValid +{ + return _valid; +} + +- (void)registerModules +{ + RCTAssertMainThread(); // Register passed-in module instances NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init]; - for (id module in _moduleProvider ? _moduleProvider() : nil) { + for (id module in _parentBridge.moduleProvider ? _parentBridge.moduleProvider() : nil) { preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module; } @@ -897,7 +1031,6 @@ - (void)setUp } // Get method queues - _queuesByID = [[RCTSparseArray alloc] init]; [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { if ([module respondsToSelector:@selector(methodQueue)]) { dispatch_queue_t queue = [module methodQueue]; @@ -907,7 +1040,16 @@ - (void)setUp _queuesByID[moduleID] = [NSNull null]; } } + + if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) { + [_frameUpdateObservers addObject:module]; + } }]; +} + +- (void)initJS +{ + RCTAssertMainThread(); // Inject module data into JS context NSString *configJSON = RCTJSONStringify(@{ @@ -920,7 +1062,9 @@ - (void)setUp dispatch_semaphore_signal(semaphore); }]; - _loading = YES; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW); + + NSURL *bundleURL = _parentBridge.bundleURL; if (_javaScriptExecutor == nil) { /** @@ -929,11 +1073,17 @@ - (void)setUp */ _loading = NO; - } else if (_bundleURL) { // Allow testing without a script + } else if (bundleURL) { // Allow testing without a script RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self]; - [loader loadBundleAtURL:_bundleURL onComplete:^(NSError *error) { + [loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) { _loading = NO; + if (!self.isValid) { + return; + } + RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; + sourceCodeModule.scriptURL = bundleURL; + sourceCodeModule.scriptText = script; if (error != nil) { NSArray *stack = [[error userInfo] objectForKey:@"stack"]; @@ -946,37 +1096,25 @@ - (void)setUp } } else { + [self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) { - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification - object:self]; + if (!loadError) { + /** + * Register the display link to start sending js calls after everything + * is setup + */ + [_jsDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification + object:_parentBridge + userInfo:@{ @"bridge": self }]; + } + }]; } - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(reload) - name:RCTReloadNotification - object:nil]; }]; } } -- (void)bindKeys -{ - -#if TARGET_IPHONE_SIMULATOR - - __weak RCTBridge *weakSelf = self; - RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; - - // reload in current mode - [commands registerKeyCommandWithInput:@"r" - modifierFlags:UIKeyModifierCommand - action:^(UIKeyCommand *command) { - [weakSelf reload]; - }]; - -#endif - -} - - (NSDictionary *)modules { RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. " @@ -985,53 +1123,48 @@ - (NSDictionary *)modules return _modulesByName; } -- (void)dealloc -{ - [self invalidate]; -} - #pragma mark - RCTInvalidating -- (BOOL)isValid -{ - return _javaScriptExecutor != nil; -} - - (void)invalidate { - if (!self.isValid && _modulesByID == nil) { - return; - } - - if (![NSThread isMainThread]) { - [self performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES]; + if (!self.isValid) { return; } - [[NSNotificationCenter defaultCenter] removeObserver:self]; + RCTAssertMainThread(); - // Release executor + _valid = NO; if (_latestJSExecutor == _javaScriptExecutor) { _latestJSExecutor = nil; } - [_javaScriptExecutor invalidate]; - _javaScriptExecutor = nil; - [_displayLink invalidate]; - [_vsyncDisplayLink invalidate]; - _frameUpdateObservers = nil; + /** + * Main Thread deallocations + */ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_mainDisplayLink invalidate]; - // Invalidate modules - for (id target in _modulesByID.allObjects) { - if ([target respondsToSelector:@selector(invalidate)]) { - [(id)target invalidate]; + [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ + /** + * JS Thread deallocations + */ + [_javaScriptExecutor invalidate]; + [_jsDisplayLink invalidate]; + + // Invalidate modules + for (id target in _modulesByID.allObjects) { + if ([target respondsToSelector:@selector(invalidate)]) { + [(id)target invalidate]; + } } - } - // Release modules (breaks retain cycle if module has strong bridge reference) - _modulesByID = nil; - _queuesByID = nil; - _modulesByName = nil; + // Release modules (breaks retain cycle if module has strong bridge reference) + _javaScriptExecutor = nil; + _frameUpdateObservers = nil; + _modulesByID = nil; + _queuesByID = nil; + _modulesByName = nil; + }]; } /** @@ -1066,6 +1199,8 @@ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args */ - (void)_immediatelyCallTimer:(NSNumber *)timer { + RCTAssertJSThread(); + NSString *moduleDotMethod = @"RCTJSTimers.callTimers"; NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod]; RCTAssert(moduleID != nil, @"Module '%@' not registered.", @@ -1074,35 +1209,29 @@ - (void)_immediatelyCallTimer:(NSNumber *)timer NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod]; RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod); - if (!_loading) { -#if BATCHED_BRIDGE - dispatch_block_t block = ^{ - [self _actuallyInvokeAndProcessModule:@"BatchedBridge" - method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, @[@[timer]]] - context:RCTGetExecutorID(_javaScriptExecutor)]; - }; - if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) { - [_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block]; - } else { - [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; - } - -#else + dispatch_block_t block = ^{ + [self _actuallyInvokeAndProcessModule:@"BatchedBridge" + method:@"callFunctionReturnFlushedQueue" + arguments:@[moduleID, methodID, @[@[timer]]] + context:RCTGetExecutorID(_javaScriptExecutor)]; + }; - [self _invokeAndProcessModule:@"BatchedBridge" - method:@"callFunctionReturnFlushedQueue" - arguments:@[moduleID, methodID, @[@[timer]]] - context:RCTGetExecutorID(_javaScriptExecutor)]; -#endif + if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) { + [_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block]; + } else { + [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; } } - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete { RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil"); + RCTProfileBeginEvent(); + [_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) { + RCTAssertJSThread(); + RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError); if (scriptLoadError) { onComplete(scriptLoadError); @@ -1132,7 +1261,13 @@ - (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete: - (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID { - id queue = _queuesByID[moduleID]; + RCTAssertJSThread(); + + id queue = nil; + if (moduleID) { + queue = _queuesByID[moduleID]; + } + if (queue == [NSNull null]) { [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; } else { @@ -1146,13 +1281,15 @@ - (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID */ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context { -#if BATCHED_BRIDGE + /** + * AnyThread + */ - __weak RCTBridge *weakSelf = self; + __weak RCTBatchedBridge *weakSelf = self; [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ RCTProfileBeginEvent(); - RCTBridge *strongSelf = weakSelf; + RCTBatchedBridge *strongSelf = weakSelf; if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) { return; } @@ -1190,7 +1327,7 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg */ if ( [args[2][0] isEqual:callArgs[2][0]] && - ([moduleName isEqualToString:@"RCTEventEmitter"] ? [args[2][1] isEqual:callArgs[2][1]] : YES) + (![moduleName isEqualToString:@"RCTEventEmitter"] || [args[2][1] isEqual:callArgs[2][1]]) ) { [strongSelf->_scheduledCalls removeObject:call]; } @@ -1218,10 +1355,14 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context { -#endif + RCTAssertJSThread(); + [[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil]; RCTJavaScriptCallback processResponse = ^(id json, NSError *error) { + if (!self.isValid) { + return; + } [[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil]; [self _handleBuffer:json context:context]; }; @@ -1237,6 +1378,8 @@ - (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)me - (void)_handleBuffer:(id)buffer context:(NSNumber *)context { + RCTAssertJSThread(); + if (buffer == nil || buffer == (id)kCFNull) { return; } @@ -1307,6 +1450,11 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i params:(NSArray *)params context:(NSNumber *)context { + RCTAssertJSThread(); + + if (!self.isValid) { + return NO; + } if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) { RCTLogError(@"Invalid module/method/params tuple for request #%zd", i); @@ -1330,10 +1478,10 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i return NO; } - __weak RCTBridge *weakSelf = self; + __weak RCTBatchedBridge *weakSelf = self; [self dispatchBlock:^{ RCTProfileBeginEvent(); - __strong RCTBridge *strongSelf = weakSelf; + RCTBatchedBridge *strongSelf = weakSelf; if (!strongSelf.isValid) { // strongSelf has been invalidated since the dispatch_async call and this @@ -1367,18 +1515,21 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i - (void)_jsThreadUpdate:(CADisplayLink *)displayLink { + RCTAssertJSThread(); + RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g"); + RCTProfileBeginEvent(); RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink]; for (id observer in _frameUpdateObservers) { if (![observer respondsToSelector:@selector(isPaused)] || ![observer isPaused]) { - [observer didUpdateFrame:frameUpdate]; + [self dispatchBlock:^{ + [observer didUpdateFrame:frameUpdate]; + } forModule:RCTModuleIDsByName[RCTBridgeModuleNameForClass([observer class])]]; } } -#if BATCHED_BRIDGE - NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls]; NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor); calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) { @@ -1393,67 +1544,41 @@ - (void)_jsThreadUpdate:(CADisplayLink *)displayLink context:RCTGetExecutorID(_javaScriptExecutor)]; } -#endif - RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil); } - (void)_mainThreadUpdate:(CADisplayLink *)displayLink { - RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); -} - -- (void)addFrameUpdateObserver:(id)observer -{ - [_frameUpdateObservers addObject:observer]; -} + RCTAssertMainThread(); -- (void)removeFrameUpdateObserver:(id)observer -{ - [_frameUpdateObservers removeObject:observer]; + RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); } -- (void)reload +- (void)startProfiling { - dispatch_async(dispatch_get_main_queue(), ^{ - if (!_loading) { - // If the bridge has not loaded yet, the context will be already invalid at - // the time the javascript gets executed. - // It will crash the javascript, and even the next `load` won't render. - [self invalidate]; - [self setUp]; - } - }); -} + RCTAssertMainThread(); -+ (void)logMessage:(NSString *)message level:(NSString *)level -{ - if (![_latestJSExecutor isValid]) { + if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) { + RCTLogError(@"To run the profiler you must be running from the dev server"); return; } - // Note: the js executor could get invalidated while we're trying to call - // this...need to watch out for that. - [_latestJSExecutor executeJSCall:@"RCTLog" - method:@"logIfNoNativeHook" - arguments:@[level, message] - context:RCTGetExecutorID(_latestJSExecutor) - callback:^(id json, NSError *error) {}]; -} + [_mainDisplayLink invalidate]; + _mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)]; + [_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; -- (void)startProfiling -{ - if (![_bundleURL.scheme isEqualToString:@"http"]) { - RCTLogError(@"To run the profiler you must be running from the dev server"); - return; - } RCTProfileInit(); } - (void)stopProfiling { + RCTAssertMainThread(); + + [_mainDisplayLink invalidate]; + NSString *log = RCTProfileEnd(); - NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", _bundleURL.scheme, _bundleURL.host, _bundleURL.port]; + NSURL *bundleURL = _parentBridge.bundleURL; + NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port]; NSURL *URL = [NSURL URLWithString:URLString]; NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL]; URLRequest.HTTPMethod = @"POST"; diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index c6063caa2301b2..6c64e467754ab6 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -123,10 +123,12 @@ - (void)updateSettings { _settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]]; - self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue]; - self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue]; - self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue]; - self.executorClass = NSClassFromString(_settings[@"executorClass"]); + dispatch_async(dispatch_get_main_queue(), ^{ + self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue]; + self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue]; + self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue]; + self.executorClass = NSClassFromString(_settings[@"executorClass"]); + }); } - (void)jsLoaded @@ -147,10 +149,12 @@ - (void)jsLoaded _liveReloadURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL]; } - // Hit these setters again after bridge has finished loading - self.profilingEnabled = _profilingEnabled; - self.liveReloadEnabled = _liveReloadEnabled; - self.executorClass = _executorClass; + dispatch_async(dispatch_get_main_queue(), ^{ + // Hit these setters again after bridge has finished loading + self.profilingEnabled = _profilingEnabled; + self.liveReloadEnabled = _liveReloadEnabled; + self.executorClass = _executorClass; + }); } - (void)dealloc diff --git a/React/Base/RCTJavaScriptLoader.h b/React/Base/RCTJavaScriptLoader.h index 8d52529e67acfb..01f51d7e99e05c 100755 --- a/React/Base/RCTJavaScriptLoader.h +++ b/React/Base/RCTJavaScriptLoader.h @@ -22,6 +22,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; -- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(RCTJavaScriptCompleteBlock)onComplete; +- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(void (^)(NSError *, NSString *))onComplete; @end diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index 2e7d21b9442fe7..0210986dceab03 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -27,7 +27,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge return self; } -- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onComplete +- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *, NSString *))onComplete { // Sanitize the script URL scriptURL = [RCTConvert NSURL:scriptURL.absoluteString]; @@ -37,7 +37,7 @@ - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onCom NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{ NSLocalizedDescriptionKey: scriptURL ? [NSString stringWithFormat:@"Script at '%@' could not be found.", scriptURL] : @"No script URL provided" }]; - onComplete(error); + onComplete(error, nil); return; } @@ -57,7 +57,7 @@ - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onCom code:error.code userInfo:userInfo]; } - onComplete(error); + onComplete(error, nil); return; } @@ -96,18 +96,10 @@ - (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onCom code:[(NSHTTPURLResponse *)response statusCode] userInfo:userInfo]; - onComplete(error); + onComplete(error, nil); return; } - RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; - sourceCodeModule.scriptURL = scriptURL; - sourceCodeModule.scriptText = rawText; - - [_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) { - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(scriptError); - }); - }]; + onComplete(nil, rawText); }]; [task resume]; diff --git a/React/Base/RCTRootView.h b/React/Base/RCTRootView.h index ee5a35d7f0be96..d55094c3727328 100644 --- a/React/Base/RCTRootView.h +++ b/React/Base/RCTRootView.h @@ -11,7 +11,7 @@ #import "RCTBridge.h" -@interface RCTRootView : UIView +@interface RCTRootView : UIView /** * - Designated initializer - diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 54556d41810ba4..9ee09d495ee447 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -20,25 +20,37 @@ #import "RCTTouchHandler.h" #import "RCTUIManager.h" #import "RCTUtils.h" +#import "RCTView.h" #import "RCTWebViewExecutor.h" #import "UIView+React.h" +@interface RCTBridge (RCTRootView) + +@property (nonatomic, weak, readonly) RCTBridge *batchedBridge; + +@end + @interface RCTUIManager (RCTRootView) - (NSNumber *)allocateRootTag; @end +@interface RCTRootContentView : RCTView + +- (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge; + +@end + @implementation RCTRootView { RCTBridge *_bridge; - RCTTouchHandler *_touchHandler; NSString *_moduleName; NSDictionary *_launchOptions; - UIView *_contentView; + RCTRootContentView *_contentView; } -- (instancetype)initWithBridge:(RCTBridge *)bridge + - (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName { RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView"); @@ -52,11 +64,11 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _moduleName = moduleName; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(bundleFinishedLoading) + selector:@selector(javaScriptDidLoad:) name:RCTJavaScriptDidLoadNotification object:_bridge]; - if (!_bridge.loading) { - [self bundleFinishedLoading]; + if (!_bridge.batchedBridge.isLoading) { + [self bundleFinishedLoading:_bridge.batchedBridge]; } } return self; @@ -73,25 +85,6 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL return [self initWithBridge:bridge moduleName:moduleName]; } -- (BOOL)isValid -{ - return _contentView.userInteractionEnabled; -} - -- (void)invalidate -{ - _contentView.userInteractionEnabled = NO; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - if (_contentView) { - [_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer" - args:@[_contentView.reactTag]]; - } -} - - (UIViewController *)backingViewController { return _backingViewController ?: [super backingViewController]; @@ -105,9 +98,19 @@ - (BOOL)canBecomeFirstResponder RCT_IMPORT_METHOD(AppRegistry, runApplication) RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer) -- (void)bundleFinishedLoading + +- (void)javaScriptDidLoad:(NSNotification *)notification +{ + RCTBridge *bridge = notification.userInfo[@"bridge"]; + [self bundleFinishedLoading:bridge]; +} + +- (void)bundleFinishedLoading:(RCTBridge *)bridge { dispatch_async(dispatch_get_main_queue(), ^{ + if (!bridge.isValid) { + return; + } /** * Every root view that is created must have a unique React tag. @@ -117,19 +120,16 @@ - (void)bundleFinishedLoading * the React tag is assigned every time we load new content. */ [_contentView removeFromSuperview]; - _contentView = [[UIView alloc] initWithFrame:self.bounds]; - _contentView.reactTag = [_bridge.uiManager allocateRootTag]; - _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge]; - [_contentView addGestureRecognizer:_touchHandler]; + _contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds + bridge:bridge]; [self addSubview:_contentView]; NSString *moduleName = _moduleName ?: @""; NSDictionary *appParameters = @{ @"rootTag": _contentView.reactTag, - @"initialProps": self.initialProperties ?: @{}, + @"initialProps": _initialProperties ?: @{}, }; - [_bridge.uiManager registerRootView:_contentView]; - [_bridge enqueueJSCall:@"AppRegistry.runApplication" + [bridge enqueueJSCall:@"AppRegistry.runApplication" args:@[moduleName, appParameters]]; }); } @@ -139,7 +139,6 @@ - (void)layoutSubviews [super layoutSubviews]; if (_contentView) { _contentView.frame = self.bounds; - [_bridge.uiManager setFrame:self.bounds forRootView:_contentView]; } } @@ -148,6 +147,12 @@ - (NSNumber *)reactTag return _contentView.reactTag; } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_contentView removeFromSuperview]; +} + @end @implementation RCTUIManager (RCTRootView) @@ -160,3 +165,60 @@ - (NSNumber *)allocateRootTag } @end + +@implementation RCTRootContentView +{ + __weak RCTBridge *_bridge; + RCTTouchHandler *_touchHandler; +} + +- (instancetype)initWithFrame:(CGRect)frame + bridge:(RCTBridge *)bridge +{ + if ((self = [super init])) { + _bridge = bridge; + [self setUp]; + self.frame = frame; + } + return self; +} + +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; + if (self.reactTag && _bridge.isValid) { + [_bridge.uiManager setFrame:self.bounds forRootView:self]; + } +} + +- (void)setUp +{ + /** + * Every root view that is created must have a unique react tag. + * Numbering of these tags goes from 1, 11, 21, 31, etc + * + * NOTE: Since the bridge persists, the RootViews might be reused, so now + * the react tag is assigned every time we load new content. + */ + self.reactTag = [_bridge.uiManager allocateRootTag]; + [self addGestureRecognizer:[[RCTTouchHandler alloc] initWithBridge:_bridge]]; + [_bridge.uiManager registerRootView:self]; +} + +- (BOOL)isValid +{ + return self.userInteractionEnabled; +} + +- (void)invalidate +{ + self.userInteractionEnabled = NO; +} + +- (void)dealloc +{ + [_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer" + args:@[self.reactTag]]; +} + +@end diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index e2df5befca18eb..e21c9d16fe4732 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -70,6 +70,7 @@ @implementation RCTTiming } @synthesize bridge = _bridge; +@synthesize paused = _paused; RCT_EXPORT_MODULE() @@ -78,7 +79,7 @@ @implementation RCTTiming - (instancetype)init { if ((self = [super init])) { - + _paused = YES; _timers = [[RCTSparseArray alloc] init]; for (NSString *name in @[UIApplicationWillResignActiveNotification, @@ -126,7 +127,7 @@ - (void)invalidate - (void)stopTimers { - [_bridge removeFrameUpdateObserver:self]; + _paused = YES; } - (void)startTimers @@ -135,7 +136,7 @@ - (void)startTimers return; } - [_bridge addFrameUpdateObserver:self]; + _paused = NO; } - (void)didUpdateFrame:(RCTFrameUpdate *)update diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 496b9c3513a677..d35cae03a1e4e9 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -267,11 +267,6 @@ - (instancetype)init return self; } -- (void)dealloc -{ - RCTAssert(!self.valid, @"must call -invalidate before -dealloc"); -} - - (BOOL)isValid { return _viewRegistry != nil; @@ -279,20 +274,24 @@ - (BOOL)isValid - (void)invalidate { - RCTAssertMainThread(); + /** + * Called on the JS Thread since all modules are invalidated on the JS thread + */ - for (NSNumber *rootViewTag in _rootViewTags) { - ((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO; - } + dispatch_async(dispatch_get_main_queue(), ^{ + for (NSNumber *rootViewTag in _rootViewTags) { + ((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO; + } - _rootViewTags = nil; - _shadowViewRegistry = nil; - _viewRegistry = nil; - _bridge = nil; + _rootViewTags = nil; + _shadowViewRegistry = nil; + _viewRegistry = nil; + _bridge = nil; - [_pendingUIBlocksLock lock]; - _pendingUIBlocks = nil; - [_pendingUIBlocksLock unlock]; + [_pendingUIBlocksLock lock]; + _pendingUIBlocks = nil; + [_pendingUIBlocksLock unlock]; + }); } - (void)setBridge:(RCTBridge *)bridge diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index a4cb338fb3c88b..f947a6128e23ba 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -264,9 +264,13 @@ @implementation RCTNavigator NSInteger _numberOfViewControllerMovesToIgnore; } +@synthesize paused = _paused; + - (id)initWithBridge:(RCTBridge *)bridge { if ((self = [super initWithFrame:CGRectZero])) { + _paused = YES; + _bridge = bridge; _mostRecentProgress = kNeverProgressed; _dummyView = [[UIView alloc] initWithFrame:CGRectZero]; @@ -341,14 +345,14 @@ - (void)navigationController:(UINavigationController *)navigationController _dummyView.frame = (CGRect){{destination}}; _currentlyTransitioningFrom = indexOfFrom; _currentlyTransitioningTo = indexOfTo; - [_bridge addFrameUpdateObserver:self]; + _paused = NO; } completion:^(id context) { [weakSelf freeLock]; _currentlyTransitioningFrom = 0; _currentlyTransitioningTo = 0; _dummyView.frame = CGRectZero; - [_bridge removeFrameUpdateObserver:self]; + _paused = YES; // Reset the parallel position tracker }]; } From aaaca7fa2dbd7b7fa324c1db90fe3c3a872500ed Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Mon, 4 May 2015 11:53:07 -0700 Subject: [PATCH 187/250] Disable watchman on Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 35a0f0ef105db3..603e924bb5b5cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ cache: before_install: - brew update install: - - brew reinstall flow watchman xctool + - brew reinstall flow xctool - npm config set spin=false - npm install From 50de4a67b0c06a5a3f54c81fadf56b34fe8ccb52 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Mon, 4 May 2015 14:33:05 -0700 Subject: [PATCH 188/250] [ReactNative] Navigator use flattenStyle Summary: A hack was implemented in Navigator to do this manually, but there is a proper function to get these styles for setting native props @public Test Plan: Testing Navigator behavior on iOS and Android --- .../CustomComponents/Navigator/Navigator.js | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 6d09b656e38b7d..26c6a46bdc6a3d 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -40,13 +40,13 @@ var Platform = require('Platform'); var React = require('React'); var StaticContainer = require('StaticContainer.react'); var StyleSheet = require('StyleSheet'); -var StyleSheetRegistry = require('StyleSheetRegistry'); var Subscribable = require('Subscribable'); var TimerMixin = require('react-timer-mixin'); var View = require('View'); -var getNavigatorContext = require('getNavigatorContext'); var clamp = require('clamp'); +var flattenStyle = require('flattenStyle'); +var getNavigatorContext = require('getNavigatorContext'); var invariant = require('invariant'); var keyMirror = require('keyMirror'); var merge = require('merge'); @@ -67,24 +67,6 @@ function getuid() { return __uid++; } -function resolveStyle(styles) { - // The styles for a scene are a prop in the style format, which can be an object, - // a number (which refers to the StyleSheetRegistry), or an arry of objects/numbers. - // This function resolves the actual style values so we can call setNativeProps with - // matching styles. - var resolvedStyle = {}; - if (!Array.isArray(styles)) { - styles = [styles]; - } - styles.forEach((style) => { - if (typeof style === 'number') { - style = StyleSheetRegistry.getStyleByID(style); - } - resolvedStyle = merge(resolvedStyle, style); - }); - return resolvedStyle; -} - // styles moved to the top of the file so getDefaultProps can refer to it var styles = StyleSheet.create({ container: { @@ -698,7 +680,7 @@ var Navigator = React.createClass({ */ _enableScene: function(sceneIndex) { // First, determine what the defined styles are for scenes in this navigator - var sceneStyle = resolveStyle(this.props.sceneStyle); + var sceneStyle = flattenStyle(this.props.sceneStyle); // Then restore the left value for this scene var enabledSceneNativeProps = { left: sceneStyle.left, From 66d2f600dddf724bfd84d6eb2d35ee8622f60446 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 4 May 2015 18:34:29 -0700 Subject: [PATCH 189/250] [ReactNative] improve console logging a little bit --- Libraries/Utilities/stringifySafe.js | 11 ++++++-- .../haste/polyfills/console.js | 27 ++++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Libraries/Utilities/stringifySafe.js b/Libraries/Utilities/stringifySafe.js index 053ea69849e821..750a932b644970 100644 --- a/Libraries/Utilities/stringifySafe.js +++ b/Libraries/Utilities/stringifySafe.js @@ -17,12 +17,19 @@ */ function stringifySafe(arg: any): string { var ret; + var type = typeof arg; if (arg === undefined) { ret = 'undefined'; } else if (arg === null) { ret = 'null'; - } else if (typeof arg === 'string') { + } else if (type === 'string') { ret = '"' + arg + '"'; + } else if (type === 'function') { + try { + ret = arg.toString(); + } catch (e) { + ret = '[function unknown]'; + } } else { // Perform a try catch, just in case the object has a circular // reference or stringify throws for some other reason. @@ -36,7 +43,7 @@ function stringifySafe(arg: any): string { } } } - return ret || '["' + typeof arg + '" failed to stringify]'; + return ret || '["' + type + '" failed to stringify]'; } module.exports = stringifySafe; diff --git a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js index 91fb970f885521..57576961243cac 100644 --- a/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js +++ b/packager/react-packager/src/DependencyResolver/haste/polyfills/console.js @@ -35,25 +35,34 @@ function getNativeLogFunction(level) { return function() { var str = Array.prototype.map.call(arguments, function(arg) { - if (arg == null) { - return arg === null ? 'null' : 'undefined'; - } else if (typeof arg === 'string') { - return '"' + arg + '"'; + var ret; + var type = typeof arg; + if (arg === null) { + ret = 'null'; + } else if (arg === undefined) { + ret = 'undefined'; + } else if (type === 'string') { + ret = '"' + arg + '"'; + } else if (type === 'function') { + try { + ret = arg.toString(); + } catch (e) { + ret = '[function unknown]'; + } } else { // Perform a try catch, just in case the object has a circular // reference or stringify throws for some other reason. try { - return JSON.stringify(arg); + ret = JSON.stringify(arg); } catch (e) { if (typeof arg.toString === 'function') { try { - return arg.toString(); - } catch (E) { - return 'unknown'; - } + ret = arg.toString(); + } catch (E) {} } } } + return ret || '["' + type + '" failed to stringify]'; }).join(', '); global.nativeLoggingHook(str, level); }; From af921542b5f0be2a96f169cf397c530e4968a3f8 Mon Sep 17 00:00:00 2001 From: Andrew Rasmussen Date: Mon, 4 May 2015 19:59:25 -0700 Subject: [PATCH 190/250] [ReactNative] update Layout Summary: I made some changes to css-layout that changes how layout is computed for absolutely positioned nodes inside absolutely positioned parents that have borders/padding. There were also some other changes made to css-layout that haven't been merged in yet. @public Test Plan: Made a node as described above, saw that the layout is computed differently than in the browser, updated Layout, saw that the Layout is not computed correctly. --- React/Layout/Layout.c | 234 +++++++++++++++++++++++++++-------------- React/Layout/Layout.h | 18 ++-- React/Layout/import.sh | 15 +++ 3 files changed, 175 insertions(+), 92 deletions(-) create mode 100755 React/Layout/import.sh diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c index 2b168e44a29e19..50d69b522b54d8 100644 --- a/React/Layout/Layout.c +++ b/React/Layout/Layout.c @@ -1,21 +1,15 @@ /** - * @generated SignedSource<<24fa633b4dd81b7fb40c2b2b0b7c97d0>> - * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in from github! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Go to https://github.com/facebook/css-layout !! - * !! 2) Make a pull request and get it merged !! - * !! 3) Execute ./import.sh to pull in the latest version !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * WARNING: You should not modify this file directly. Instead: + * 1) Go to https://github.com/facebook/css-layout + * 2) Make a pull request and get it merged + * 3) Run import.sh to copy Layout.* to react-native-github */ #include @@ -44,6 +38,12 @@ void init_css_node(css_node_t *node) { node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->style.position[CSS_LEFT] = CSS_UNDEFINED; node->style.position[CSS_TOP] = CSS_UNDEFINED; node->style.position[CSS_RIGHT] = CSS_UNDEFINED; @@ -249,6 +249,10 @@ static float getPaddingAndBorder(css_node_t *node, int location) { return getPadding(node, location) + getBorder(node, location); } +static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) { + return getBorder(node, leading[axis]) + getBorder(node, trailing[axis]); +} + static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) { return getMargin(node, leading[axis]) + getMargin(node, trailing[axis]); } @@ -298,7 +302,8 @@ static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) { } static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) { - return !isUndefined(node->style.dimensions[dim[axis]]); + float value = node->style.dimensions[dim[axis]]; + return !isUndefined(value) && value > 0.0; } static bool isPosDefined(css_node_t *node, css_position_t position) { @@ -317,6 +322,30 @@ static float getPosition(css_node_t *node, css_position_t position) { return 0; } +static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) { + float min = CSS_UNDEFINED; + float max = CSS_UNDEFINED; + + if (axis == CSS_FLEX_DIRECTION_COLUMN) { + min = node->style.minDimensions[CSS_HEIGHT]; + max = node->style.maxDimensions[CSS_HEIGHT]; + } else if (axis == CSS_FLEX_DIRECTION_ROW) { + min = node->style.minDimensions[CSS_WIDTH]; + max = node->style.maxDimensions[CSS_WIDTH]; + } + + float boundValue = value; + + if (!isUndefined(max) && max >= 0.0 && boundValue > max) { + boundValue = max; + } + if (!isUndefined(min) && min >= 0.0 && boundValue < min) { + boundValue = min; + } + + return boundValue; +} + // When the user specifically sets a value for width or height static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { // The parent already computed us a width or height. We just skip it @@ -330,7 +359,7 @@ static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { // The dimensions can never be smaller than the padding and border node->layout.dimensions[dim[axis]] = fmaxf( - node->style.dimensions[dim[axis]], + boundAxis(node, axis, node->style.dimensions[dim[axis]]), getPaddingAndBorderAxis(node, axis) ); } @@ -347,6 +376,7 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { /** START_GENERATED **/ + css_flex_direction_t mainAxis = getFlexDirection(node); css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ? CSS_FLEX_DIRECTION_COLUMN : @@ -385,25 +415,31 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // Let's not measure the text if we already know both dimensions if (isRowUndefined || isColumnUndefined) { - css_dim_t measure_dim = node->measure( + css_dim_t measureDim = node->measure( node->context, + width ); if (isRowUndefined) { - node->layout.dimensions[CSS_WIDTH] = measure_dim.dimensions[CSS_WIDTH] + + node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); } if (isColumnUndefined) { - node->layout.dimensions[CSS_HEIGHT] = measure_dim.dimensions[CSS_HEIGHT] + + node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); } } return; } + int i; + int ii; + css_node_t* child; + css_flex_direction_t axis; + // Pre-fill some dimensions straight from the parent - for (int i = 0; i < node->children_count; ++i) { - css_node_t* child = node->get_child(node->context, i); + for (i = 0; i < node->children_count; ++i) { + child = node->get_child(node->context, i); // Pre-fill cross axis dimensions when the child is using stretch before // we call the recursive layout pass if (getAlignItem(node, child) == CSS_ALIGN_STRETCH && @@ -411,27 +447,27 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { !isUndefined(node->layout.dimensions[dim[crossAxis]]) && !isDimDefined(child, crossAxis)) { child->layout.dimensions[dim[crossAxis]] = fmaxf( - node->layout.dimensions[dim[crossAxis]] - + boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - getPaddingAndBorderAxis(node, crossAxis) - - getMarginAxis(child, crossAxis), + getMarginAxis(child, crossAxis)), // You never want to go smaller than padding getPaddingAndBorderAxis(child, crossAxis) ); } else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both // left and right or top and bottom). - for (int ii = 0; ii < 2; ii++) { - css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; if (!isUndefined(node->layout.dimensions[dim[axis]]) && !isDimDefined(child, axis) && isPosDefined(child, leading[axis]) && isPosDefined(child, trailing[axis])) { child->layout.dimensions[dim[axis]] = fmaxf( - node->layout.dimensions[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis]), + boundAxis(child, axis, node->layout.dimensions[dim[axis]] - + getPaddingAndBorderAxis(node, axis) - + getMarginAxis(child, axis) - + getPosition(child, leading[axis]) - + getPosition(child, trailing[axis])), // You never want to go smaller than padding getPaddingAndBorderAxis(child, axis) ); @@ -449,11 +485,12 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // We want to execute the next two loops one per line with flex-wrap int startLine = 0; int endLine = 0; - int nextLine = 0; + // int nextOffset = 0; + int alreadyComputedNextLayout = 0; // We aggregate the total dimensions of the container in those two variables float linesCrossDim = 0; float linesMainDim = 0; - while (endLine != node->children_count) { + while (endLine < node->children_count) { // Layout non flexible children and count children by type // mainContentDim is accumulation of the dimensions and margin of all the @@ -467,8 +504,10 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { int flexibleChildrenCount = 0; float totalFlexible = 0; int nonFlexibleChildrenCount = 0; - for (int i = startLine; i < node->children_count; ++i) { - css_node_t* child = node->get_child(node->context, i); + + float maxWidth; + for (i = startLine; i < node->children_count; ++i) { + child = node->get_child(node->context, i); float nextContentDim = 0; // It only makes sense to consider a child flexible if we have a computed @@ -478,26 +517,27 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { totalFlexible += getFlex(child); // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information to compute the - // remaining space. + // border and margin. We'll use this partial information, which represents + // the smallest possible size for the child, to compute the remaining + // available space. nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + getMarginAxis(child, mainAxis); } else { - float maxWidth = CSS_UNDEFINED; - if (mainAxis == CSS_FLEX_DIRECTION_ROW) { - // do nothing - } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { - maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); - } else { + maxWidth = CSS_UNDEFINED; + if (mainAxis != CSS_FLEX_DIRECTION_ROW) { maxWidth = parentMaxWidth - getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + + if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + } } // This is the main recursive call. We layout non flexible children. - if (nextLine == 0) { + if (alreadyComputedNextLayout == 0) { layoutNode(child, maxWidth); } @@ -513,11 +553,14 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // The element we are about to add would make us go to the next line if (isFlexWrap(node) && !isUndefined(node->layout.dimensions[dim[mainAxis]]) && - mainContentDim + nextContentDim > definedMainDim) { - nextLine = i + 1; + mainContentDim + nextContentDim > definedMainDim && + // If there's only one element, then it's bigger than the content + // and needs its own line + i != startLine) { + alreadyComputedNextLayout = 1; break; } - nextLine = 0; + alreadyComputedNextLayout = 0; mainContentDim += nextContentDim; endLine = i + 1; } @@ -542,6 +585,26 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // remaining space if (flexibleChildrenCount != 0) { float flexibleMainDim = remainingMainDim / totalFlexible; + float baseMainDim; + float boundMainDim; + + // Iterate over every child in the axis. If the flex share of remaining + // space doesn't meet min/max bounds, remove this child from flex + // calculations. + for (i = startLine; i < endLine; ++i) { + child = node->get_child(node->context, i); + if (isFlex(child)) { + baseMainDim = flexibleMainDim * getFlex(child) + + getPaddingAndBorderAxis(child, mainAxis); + boundMainDim = boundAxis(child, mainAxis, baseMainDim); + + if (baseMainDim != boundMainDim) { + remainingMainDim -= boundMainDim; + totalFlexible -= getFlex(child); + } + } + } + flexibleMainDim = remainingMainDim / totalFlexible; // The non flexible children can overflow the container, in this case // we should just assume that there is no space available. @@ -551,21 +614,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // We iterate over the full array and only apply the action on flexible // children. This is faster than actually allocating a new array that // contains only flexible children. - for (int i = startLine; i < endLine; ++i) { - css_node_t* child = node->get_child(node->context, i); + for (i = startLine; i < endLine; ++i) { + child = node->get_child(node->context, i); if (isFlex(child)) { // At this point we know the final size of the element in the main // dimension - child->layout.dimensions[dim[mainAxis]] = flexibleMainDim * getFlex(child) + - getPaddingAndBorderAxis(child, mainAxis); + child->layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis, + flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis) + ); - float maxWidth = CSS_UNDEFINED; - if (mainAxis == CSS_FLEX_DIRECTION_ROW) { - // do nothing - } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + maxWidth = CSS_UNDEFINED; + if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); - } else { + } else if (mainAxis != CSS_FLEX_DIRECTION_ROW) { maxWidth = parentMaxWidth - getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); @@ -580,9 +642,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // space available } else { css_justify_t justifyContent = getJustifyContent(node); - if (justifyContent == CSS_JUSTIFY_FLEX_START) { - // Do nothing - } else if (justifyContent == CSS_JUSTIFY_CENTER) { + if (justifyContent == CSS_JUSTIFY_CENTER) { leadingMainDim = remainingMainDim / 2; } else if (justifyContent == CSS_JUSTIFY_FLEX_END) { leadingMainDim = remainingMainDim; @@ -612,8 +672,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { float mainDim = leadingMainDim + getPaddingAndBorder(node, leading[mainAxis]); - for (int i = startLine; i < endLine; ++i) { - css_node_t* child = node->get_child(node->context, i); + for (i = startLine; i < endLine; ++i) { + child = node->get_child(node->context, i); if (getPositionType(child) == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { @@ -638,25 +698,38 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); // The cross dimension is the max of the elements dimension since there // can only be one element in that cross dimension. - crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis)); + crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); } } + float containerMainAxis = node->layout.dimensions[dim[mainAxis]]; + // If the user didn't specify a width or height, and it has not been set + // by the container, then we set it via the children. + if (isUndefined(containerMainAxis)) { + containerMainAxis = fmaxf( + // We're missing the last padding at this point to get the final + // dimension + boundAxis(node, mainAxis, mainDim + getPaddingAndBorder(node, trailing[mainAxis])), + // We can never assign a width smaller than the padding and borders + getPaddingAndBorderAxis(node, mainAxis) + ); + } + float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { containerCrossAxis = fmaxf( // For the cross dim, we add both sides at the end because the value // is aggregate via a max function. Intermediate negative values // can mess this computation otherwise - crossDim + getPaddingAndBorderAxis(node, crossAxis), + boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)), getPaddingAndBorderAxis(node, crossAxis) ); } // Position elements in the cross axis - for (int i = startLine; i < endLine; ++i) { - css_node_t* child = node->get_child(node->context, i); + for (i = startLine; i < endLine; ++i) { + child = node->get_child(node->context, i); if (getPositionType(child) == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[crossAxis])) { @@ -674,21 +747,19 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // alignSelf (child) in order to determine the position in the cross axis if (getPositionType(child) == CSS_POSITION_RELATIVE) { css_align_t alignItem = getAlignItem(node, child); - if (alignItem == CSS_ALIGN_FLEX_START) { - // Do nothing - } else if (alignItem == CSS_ALIGN_STRETCH) { + if (alignItem == CSS_ALIGN_STRETCH) { // You can only stretch if the dimension has not already been set // previously. if (!isDimDefined(child, crossAxis)) { child->layout.dimensions[dim[crossAxis]] = fmaxf( - containerCrossAxis - + boundAxis(child, crossAxis, containerCrossAxis - getPaddingAndBorderAxis(node, crossAxis) - - getMarginAxis(child, crossAxis), + getMarginAxis(child, crossAxis)), // You never want to go smaller than padding getPaddingAndBorderAxis(child, crossAxis) ); } - } else { + } else if (alignItem != CSS_ALIGN_FLEX_START) { // The remaining space between the parent dimensions+padding and child // dimensions+margin. float remainingCrossDim = containerCrossAxis - @@ -719,7 +790,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { node->layout.dimensions[dim[mainAxis]] = fmaxf( // We're missing the last padding at this point to get the final // dimension - linesMainDim + getPaddingAndBorder(node, trailing[mainAxis]), + boundAxis(node, mainAxis, linesMainDim + getPaddingAndBorder(node, trailing[mainAxis])), // We can never assign a width smaller than the padding and borders getPaddingAndBorderAxis(node, mainAxis) ); @@ -730,37 +801,38 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // For the cross dim, we add both sides at the end because the value // is aggregate via a max function. Intermediate negative values // can mess this computation otherwise - linesCrossDim + getPaddingAndBorderAxis(node, crossAxis), + boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)), getPaddingAndBorderAxis(node, crossAxis) ); } // Calculate dimensions for absolutely positioned elements - for (int i = 0; i < node->children_count; ++i) { - css_node_t* child = node->get_child(node->context, i); + for (i = 0; i < node->children_count; ++i) { + child = node->get_child(node->context, i); if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both // left and right or top and bottom). - for (int ii = 0; ii < 2; ii++) { - css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; if (!isUndefined(node->layout.dimensions[dim[axis]]) && !isDimDefined(child, axis) && isPosDefined(child, leading[axis]) && isPosDefined(child, trailing[axis])) { child->layout.dimensions[dim[axis]] = fmaxf( - node->layout.dimensions[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis]), + boundAxis(child, axis, node->layout.dimensions[dim[axis]] - + getBorderAxis(node, axis) - + getMarginAxis(child, axis) - + getPosition(child, leading[axis]) - + getPosition(child, trailing[axis]) + ), // You never want to go smaller than padding getPaddingAndBorderAxis(child, axis) ); } } - for (int ii = 0; ii < 2; ii++) { - css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; if (isPosDefined(child, trailing[axis]) && !isPosDefined(child, leading[axis])) { child->layout.position[leading[axis]] = diff --git a/React/Layout/Layout.h b/React/Layout/Layout.h index 51f72493bb5b2f..fe383ea5728ed1 100644 --- a/React/Layout/Layout.h +++ b/React/Layout/Layout.h @@ -1,21 +1,15 @@ /** - * @generated SignedSource<<58298c7a8815a8675e970b0347dedfed>> - * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in from github! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Go to https://github.com/facebook/css-layout !! - * !! 2) Make a pull request and get it merged !! - * !! 3) Execute ./import.sh to pull in the latest version !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * WARNING: You should not modify this file directly. Instead: + * 1) Go to https://github.com/facebook/css-layout + * 2) Make a pull request and get it merged + * 3) Run import.sh to copy Layout.* to react-native-github */ #ifndef __LAYOUT_H @@ -113,6 +107,8 @@ typedef struct { float padding[4]; float border[4]; float dimensions[2]; + float minDimensions[2]; + float maxDimensions[2]; } css_style_t; typedef struct css_node { diff --git a/React/Layout/import.sh b/React/Layout/import.sh new file mode 100755 index 00000000000000..7e69403f3a44a3 --- /dev/null +++ b/React/Layout/import.sh @@ -0,0 +1,15 @@ +LAYOUT_C=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.c` +LAYOUT_H=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.h` + +REPLACE_STRING="* + * WARNING: You should not modify this file directly. Instead: + * 1) Go to https://github.com/facebook/css-layout + * 2) Make a pull request and get it merged + * 3) Run import.sh to copy Layout.* to react-native-github + */" + +LAYOUT_C=${LAYOUT_C/\*\//$REPLACE_STRING} +LAYOUT_H=${LAYOUT_H/\*\//$REPLACE_STRING} + +echo "$LAYOUT_C" > Layout.c +echo "$LAYOUT_H" > Layout.h From d713a711c423a71ed004e54b1b779fb753baab5e Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Tue, 5 May 2015 02:31:20 -0700 Subject: [PATCH 191/250] [ReactNative] Fix packager assets --- React/Base/RCTBridge.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 349cc725e13a2b..35e85b3c978279 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -941,6 +941,11 @@ - (instancetype)initWithParentBridge:(RCTBridge *)bridge return self; } +- (NSURL *)bundleURL +{ + return _parentBridge.bundleURL; +} + - (NSDictionary *)launchOptions { return _parentBridge.launchOptions; From 4402c4c885d81c5d5a9a0773afce3e1fa0ae1394 Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Tue, 5 May 2015 02:51:17 -0700 Subject: [PATCH 192/250] [react_native] JS files from D2038965: Extract view specific commands from UIManager. --- Libraries/Components/ScrollView/ScrollView.js | 18 +++++++++++++----- .../Components/WebView/WebView.android.js | 18 +++++++++++++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 76419bce0aa0e4..f8dc3bcba15356 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -211,11 +211,19 @@ var ScrollView = React.createClass({ }, scrollTo: function(destY?: number, destX?: number) { - RCTUIManager.scrollTo( - this.getNodeHandle(), - destX || 0, - destY || 0 - ); + if (Platform.OS === 'android') { + RCTUIManager.dispatchViewManagerCommand( + this.getNodeHandle(), + RCTUIManager.RCTScrollView.Commands.scrollTo, + [destX || 0, destY || 0] + ); + } else { + RCTUIManager.scrollTo( + this.getNodeHandle(), + destX || 0, + destY || 0 + ); + } }, scrollWithoutAnimationTo: function(destY?: number, destX?: number) { diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js index 959422bbc9888a..79ded650658a2a 100644 --- a/Libraries/Components/WebView/WebView.android.js +++ b/Libraries/Components/WebView/WebView.android.js @@ -109,15 +109,27 @@ var WebView = React.createClass({ }, goForward: function() { - RCTUIManager.webViewGoForward(this.getWebWiewHandle()); + RCTUIManager.dispatchViewManagerCommand( + this.getWebWiewHandle(), + RCTUIManager.RCTWebView.Commands.goForward, + null + ); }, goBack: function() { - RCTUIManager.webViewGoBack(this.getWebWiewHandle()); + RCTUIManager.dispatchViewManagerCommand( + this.getWebWiewHandle(), + RCTUIManager.RCTWebView.Commands.goBack, + null + ); }, reload: function() { - RCTUIManager.webViewReload(this.getWebWiewHandle()); + RCTUIManager.dispatchViewManagerCommand( + this.getWebWiewHandle(), + RCTUIManager.RCTWebView.Commands.reload, + null + ); }, /** From 65a3da300349fa74308be786fc27c5dee89d8ca4 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Tue, 5 May 2015 05:15:53 -0700 Subject: [PATCH 193/250] [ReactNative] Fix chrome debugger --- React/Base/RCTBridge.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 35e85b3c978279..a7c6a30a6914da 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -1108,7 +1108,8 @@ - (void)initJS * Register the display link to start sending js calls after everything * is setup */ - [_jsDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop]; + [_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification object:_parentBridge From 5eca2e1d3cf42f40c648726f0ba7a2a621ab0127 Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Tue, 5 May 2015 05:34:38 -0700 Subject: [PATCH 194/250] [React Native] Added RCTSettings --- .../UIExplorer.xcodeproj/project.pbxproj | 28 ++ .../RCTSettings.xcodeproj/project.pbxproj | 250 ++++++++++++++++++ Libraries/Settings/RCTSettingsManager.h | 18 ++ Libraries/Settings/RCTSettingsManager.m | 100 +++++++ Libraries/Settings/Settings.android.js | 34 +++ Libraries/Settings/Settings.ios.js | 77 ++++++ React/Base/RCTConvert.h | 6 + React/Base/RCTConvert.m | 46 ++++ 8 files changed, 559 insertions(+) create mode 100644 Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj create mode 100644 Libraries/Settings/RCTSettingsManager.h create mode 100644 Libraries/Settings/RCTSettingsManager.m create mode 100644 Libraries/Settings/Settings.android.js create mode 100644 Libraries/Settings/Settings.ios.js diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index cf9440c05e72e9..0d16e33e30737d 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -103,6 +103,13 @@ remoteGlobalIDString = 580C376F1AB104AF0015E709; remoteInfo = RCTTest; }; + 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTSettings; + }; D85B829B1AB6D5CE003F4FE2 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */; @@ -129,6 +136,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = UIExplorer/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = ""; }; + 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = ""; }; 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = ""; }; 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; @@ -200,6 +208,7 @@ 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */, 13417FE31AA91428003F314A /* RCTImage.xcodeproj */, 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, + 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */, 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */, 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */, 00D2770E1AB8C2C700DC1E48 /* RCTWebSocketDebugger.xcodeproj */, @@ -293,6 +302,14 @@ name = Products; sourceTree = ""; }; + 834C36CE1AF8DA610019C93C /* Products */ = { + isa = PBXGroup; + children = ( + 834C36D21AF8DA610019C93C /* libRCTSettings.a */, + ); + name = Products; + sourceTree = ""; + }; 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( @@ -411,6 +428,10 @@ ProductGroup = 14DC67E81AB71876001358AB /* Products */; ProjectRef = 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */; }, + { + ProductGroup = 834C36CE1AF8DA610019C93C /* Products */; + ProjectRef = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */; + }, { ProductGroup = 58005BE51ABA80530062E044 /* Products */; ProjectRef = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */; @@ -511,6 +532,13 @@ remoteRef = 58005BED1ABA80530062E044 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 834C36D21AF8DA610019C93C /* libRCTSettings.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTSettings.a; + remoteRef = 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj b/Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj new file mode 100644 index 00000000000000..e4a210466c66f4 --- /dev/null +++ b/Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj @@ -0,0 +1,250 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 13DBA45E1AEE749000A17CF8 /* RCTSettingsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 58B511D91A9E6C8500147676 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 134814201AA4EA6300B7C361 /* libRCTSettings.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTSettings.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 13DBA45C1AEE749000A17CF8 /* RCTSettingsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSettingsManager.h; sourceTree = ""; }; + 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSettingsManager.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 58B511D81A9E6C8500147676 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 134814211AA4EA7D00B7C361 /* Products */ = { + isa = PBXGroup; + children = ( + 134814201AA4EA6300B7C361 /* libRCTSettings.a */, + ); + name = Products; + sourceTree = ""; + }; + 58B511D21A9E6C8500147676 = { + isa = PBXGroup; + children = ( + 13DBA45C1AEE749000A17CF8 /* RCTSettingsManager.h */, + 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */, + 134814211AA4EA7D00B7C361 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 58B511DA1A9E6C8500147676 /* RCTSettings */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTSettings" */; + buildPhases = ( + 58B511D71A9E6C8500147676 /* Sources */, + 58B511D81A9E6C8500147676 /* Frameworks */, + 58B511D91A9E6C8500147676 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RCTSettings; + productName = RCTDataManager; + productReference = 134814201AA4EA6300B7C361 /* libRCTSettings.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 58B511D31A9E6C8500147676 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 58B511DA1A9E6C8500147676 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTSettings" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 58B511D21A9E6C8500147676; + productRefGroup = 58B511D21A9E6C8500147676; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 58B511DA1A9E6C8500147676 /* RCTSettings */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 58B511D71A9E6C8500147676 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13DBA45E1AEE749000A17CF8 /* RCTSettingsManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 58B511ED1A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 58B511EE1A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 58B511F01A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTSettings; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 58B511F11A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTSettings; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTSettings" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511ED1A9E6C8500147676 /* Debug */, + 58B511EE1A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTSettings" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511F01A9E6C8500147676 /* Debug */, + 58B511F11A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 58B511D31A9E6C8500147676 /* Project object */; +} diff --git a/Libraries/Settings/RCTSettingsManager.h b/Libraries/Settings/RCTSettingsManager.h new file mode 100644 index 00000000000000..274cc69aed4328 --- /dev/null +++ b/Libraries/Settings/RCTSettingsManager.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "RCTBridgeModule.h" + +@interface RCTSettingsManager : NSObject + +- (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults NS_DESIGNATED_INITIALIZER; + +@end diff --git a/Libraries/Settings/RCTSettingsManager.m b/Libraries/Settings/RCTSettingsManager.m new file mode 100644 index 00000000000000..b17439eafa15dd --- /dev/null +++ b/Libraries/Settings/RCTSettingsManager.m @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTSettingsManager.h" + +#import "RCTBridge.h" +#import "RCTConvert.h" +#import "RCTEventDispatcher.h" + +@implementation RCTSettingsManager +{ + BOOL _ignoringUpdates; + NSUserDefaults *_defaults; +} + +@synthesize bridge = _bridge; + +RCT_EXPORT_MODULE() + +- (instancetype)init +{ + return [self initWithUserDefaults:[NSUserDefaults standardUserDefaults]]; +} + +- (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults +{ + if ((self = [super init])) { + _defaults = defaults; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(userDefaultsDidChange:) + name:NSUserDefaultsDidChangeNotification + object:_defaults]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)userDefaultsDidChange:(NSNotification *)note +{ + if (_ignoringUpdates) { + return; + } + + [_bridge.eventDispatcher sendDeviceEventWithName:@"settingsUpdated" body:[_defaults dictionaryRepresentation]]; +} + +- (NSDictionary *)constantsToExport +{ + return @{ + @"settings": [_defaults dictionaryRepresentation] + }; +} + +/** + * Set one or more values in the settings. + * TODO: would it be useful to have a callback for when this has completed? + */ +RCT_EXPORT_METHOD(setValues:(NSDictionary *)values) +{ + _ignoringUpdates = YES; + [values enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, BOOL *stop) { + id plist = [RCTConvert NSPropertyList:json]; + if (plist) { + [_defaults setObject:plist forKey:key]; + } else { + [_defaults removeObjectForKey:key]; + } + }]; + + [_defaults synchronize]; + _ignoringUpdates = NO; +} + +/** + * Remove some values from the settings. + */ +RCT_EXPORT_METHOD(deleteValues:(NSStringArray *)keys) +{ + _ignoringUpdates = YES; + for (NSString *key in keys) { + [_defaults removeObjectForKey:key]; + } + + [_defaults synchronize]; + _ignoringUpdates = NO; +} + +@end diff --git a/Libraries/Settings/Settings.android.js b/Libraries/Settings/Settings.android.js new file mode 100644 index 00000000000000..d13a32ba9218bc --- /dev/null +++ b/Libraries/Settings/Settings.android.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule Settings + * @flow + */ +'use strict'; + +var Settings = { + get(key: string): mixed { + console.warn('Settings is not yet supported on Android'); + return null; + }, + + set(settings: Object) { + console.warn('Settings is not yet supported on Android'); + }, + + watchKeys(keys: string | Array, callback: Function): number { + console.warn('Settings is not yet supported on Android'); + return -1; + }, + + clearWatch(watchId: number) { + console.warn('Settings is not yet supported on Android'); + }, +}; + +module.exports = Settings; diff --git a/Libraries/Settings/Settings.ios.js b/Libraries/Settings/Settings.ios.js new file mode 100644 index 00000000000000..c9836f0101e77a --- /dev/null +++ b/Libraries/Settings/Settings.ios.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule Settings + * @flow + */ +'use strict'; + +var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); +var RCTSettingsManager = require('NativeModules').SettingsManager; + +var invariant = require('invariant'); + +var subscriptions: Array<{keys: Array; callback: ?Function}> = []; + +var Settings = { + _settings: RCTSettingsManager.settings, + + get(key: string): mixed { + return this._settings[key]; + }, + + set(settings: Object) { + this._settings = merge(this._settings, settings); + RCTSettingsManager.set(settings); + }, + + watchKeys(keys: string | Array, callback: Function): number { + if (typeof keys == 'string') { + keys = [keys]; + } + + invariant( + Array.isArray(keys), + 'keys should be a string or array of strings' + ); + + var sid = subscriptions.length; + subscriptions.push({keys: keys, callback: callback}) + return sid; + }, + + clearWatch(watchId: number) { + if (watchId < subscriptions.length) { + subscriptions[watchId] = {keys: [], callback: null}; + } + }, + + _sendObservations(body: Object) { + var $this = this; + Object.keys(body).forEach((key) => { + var newValue = body[key]; + var didChange = $this._settings[key] !== newValue; + $this._settings[key] = newValue; + + if (didChange) { + subscriptions.forEach((sub) => { + if (~sub.keys.indexOf(key) && sub.callback) { + sub.callback(); + } + }); + } + }); + }, +}; + +RCTDeviceEventEmitter.addListener( + 'settingsUpdated', + Settings._sendObservations, +); + +module.exports = Settings; diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index eb81c167c9aa02..71bf7b30282546 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -101,6 +101,12 @@ typedef NSArray UIColorArray; typedef NSArray CGColorArray; + (CGColorArray *)CGColorArray:(id)json; +/** + * Convert a JSON object to a Plist-safe equivalent by stripping null values. + */ +typedef id NSPropertyList; ++ (NSPropertyList)NSPropertyList:(id)json; + typedef BOOL css_overflow; + (css_overflow)css_overflow:(id)json; + (css_flex_direction_t)css_flex_direction_t:(id)json; diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 5802a80f6fe1bf..a5d2239af57364 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -828,6 +828,52 @@ + (NSArray *)CGColorArray:(id)json return colors; } +static id RCTConvertPropertyListValue(id json) +{ + if (!json || json == (id)kCFNull) { + return nil; + } else if ([json isKindOfClass:[NSDictionary class]]) { + __block BOOL copy = NO; + NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]]; + [json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, BOOL *stop) { + id value = RCTConvertPropertyListValue(jsonValue); + if (value) { + values[key] = value; + } + copy |= value != jsonValue; + }]; + return copy ? values : json; + } else if ([json isKindOfClass:[NSArray class]]) { + __block BOOL copy = NO; + __block NSArray *values = json; + [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, BOOL *stop) { + id value = RCTConvertPropertyListValue(jsonValue); + if (copy) { + if (value) { + [(NSMutableArray *)values addObject:value]; + } + } else if (value != jsonValue) { + // Converted value is different, so we'll need to copy the array + values = [[NSMutableArray alloc] initWithCapacity:values.count]; + for (NSInteger i = 0; i < idx; i++) { + [(NSMutableArray *)values addObject:json[i]]; + } + [(NSMutableArray *)values addObject:value]; + copy = YES; + } + }]; + return values; + } else { + // All other JSON types are supported by property lists + return json; + } +} + ++ (NSPropertyList)NSPropertyList:(id)json +{ + return RCTConvertPropertyListValue(json); +} + RCT_ENUM_CONVERTER(css_overflow, (@{ @"hidden": @NO, @"visible": @YES From 1a17cceb17ea9eaa820b0ecbedb10266980ba1ce Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Tue, 5 May 2015 05:34:39 -0700 Subject: [PATCH 195/250] [React Native] Save UIExplorer search state --- .../UIExplorer/UIExplorer.xcodeproj/project.pbxproj | 2 ++ Examples/UIExplorer/UIExplorerList.js | 11 ++++++++++- Libraries/Settings/Settings.ios.js | 12 ++++++------ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index 0d16e33e30737d..698fd3b3f42fff 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; }; 14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; }; 58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58005BEE1ABA80530062E044 /* libRCTTest.a */; }; + 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; /* End PBXBuildFile section */ @@ -156,6 +157,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */, 14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */, 00D2771A1AB8C3E100DC1E48 /* libRCTWebSocketDebugger.a in Frameworks */, 58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */, diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index a10d291bdb54d2..a24ec1a54a20ff 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -28,6 +28,7 @@ var { } = React; var { TestModule } = React.addons; +var Settings = require('Settings'); var createExamplePage = require('./createExamplePage'); @@ -114,9 +115,14 @@ class UIExplorerList extends React.Component { components: COMPONENTS, apis: APIS, }), + searchText: Settings.get('searchText'), }; } + componentDidMount() { + this._search(this.state.searchText); + } + render() { return ( @@ -128,6 +134,7 @@ class UIExplorerList extends React.Component { onChangeText={this._search.bind(this)} placeholder="Search..." style={styles.searchTextInput} + value={this.state.searchText} /> , callback: Function): number { @@ -52,11 +52,11 @@ var Settings = { }, _sendObservations(body: Object) { - var $this = this; + var _this = this; Object.keys(body).forEach((key) => { var newValue = body[key]; - var didChange = $this._settings[key] !== newValue; - $this._settings[key] = newValue; + var didChange = _this._settings[key] !== newValue; + _this._settings[key] = newValue; if (didChange) { subscriptions.forEach((sub) => { @@ -71,7 +71,7 @@ var Settings = { RCTDeviceEventEmitter.addListener( 'settingsUpdated', - Settings._sendObservations, + Settings._sendObservations.bind(Settings) ); module.exports = Settings; From acafa7e9217465179fb22c90efeb764df56fbcce Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 5 May 2015 05:58:07 -0700 Subject: [PATCH 196/250] [ReactNative] Properly transition RCTTouchHandler state Summary: When touches end or cancel, update self.state in RCTTouchHandler to let iOS know that we are in an ended/canceled state. This way we won't eat other touches because it still thinks we're in a began/changed state. @public Test Plan: Scrolling in the back swipe area no longer busts gesture recognition in Wilde. --- React/Base/RCTTouchHandler.m | 52 +++++++++++++++++++++++++++++++++--- React/Layout/Layout.c | 13 --------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/React/Base/RCTTouchHandler.m b/React/Base/RCTTouchHandler.m index 7af26da74eab70..2af5c428c3de74 100644 --- a/React/Base/RCTTouchHandler.m +++ b/React/Base/RCTTouchHandler.m @@ -55,8 +55,9 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _pendingTouches = [[NSMutableArray alloc] init]; _bridgeInteractionTiming = [[NSMutableArray alloc] init]; - // `cancelsTouchesInView` is needed in order to be used as a top level event delegated recognizer. Otherwise, lower - // level components not build using RCT, will fail to recognize gestures. + // `cancelsTouchesInView` is needed in order to be used as a top level + // event delegated recognizer. Otherwise, lower-level components not built + // using RCT, will fail to recognize gestures. self.cancelsTouchesInView = NO; } return self; @@ -165,7 +166,9 @@ - (void)_updateReactTouchAtIndex:(NSInteger)touchIndex * (start/end/move/cancel) and the indices that represent "changed" `Touch`es * from that array. */ -- (void)_updateAndDispatchTouches:(NSSet *)touches eventName:(NSString *)eventName originatingTime:(CFTimeInterval)originatingTime +- (void)_updateAndDispatchTouches:(NSSet *)touches + eventName:(NSString *)eventName + originatingTime:(CFTimeInterval)originatingTime { // Update touches NSMutableArray *changedIndexes = [[NSMutableArray alloc] init]; @@ -196,15 +199,39 @@ - (void)_updateAndDispatchTouches:(NSSet *)touches eventName:(NSString *)eventNa #pragma mark - Gesture Recognizer Delegate Callbacks +static BOOL RCTAllTouchesAreCancelldOrEnded(NSSet *touches) +{ + for (UITouch *touch in touches) { + if (touch.phase == UITouchPhaseBegan || + touch.phase == UITouchPhaseMoved || + touch.phase == UITouchPhaseStationary) { + return NO; + } + } + return YES; +} + +static BOOL RCTAnyTouchesChanged(NSSet *touches) +{ + for (UITouch *touch in touches) { + if (touch.phase == UITouchPhaseBegan || + touch.phase == UITouchPhaseMoved) { + return YES; + } + } + return NO; +} + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; - self.state = UIGestureRecognizerStateBegan; // "start" has to record new touches before extracting the event. // "end"/"cancel" needs to remove the touch *after* extracting the event. [self _recordNewTouches:touches]; [self _updateAndDispatchTouches:touches eventName:@"topTouchStart" originatingTime:event.timestamp]; + + self.state = UIGestureRecognizerStateBegan; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event @@ -213,7 +240,12 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event if (self.state == UIGestureRecognizerStateFailed) { return; } + [self _updateAndDispatchTouches:touches eventName:@"topTouchMove" originatingTime:event.timestamp]; + + if (self.state == UIGestureRecognizerStateBegan) { + self.state = UIGestureRecognizerStateChanged; + } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event @@ -221,6 +253,12 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event [super touchesEnded:touches withEvent:event]; [self _updateAndDispatchTouches:touches eventName:@"topTouchEnd" originatingTime:event.timestamp]; [self _recordRemovedTouches:touches]; + + if (RCTAllTouchesAreCancelldOrEnded(event.allTouches)) { + self.state = UIGestureRecognizerStateEnded; + } else if (RCTAnyTouchesChanged(event.allTouches)) { + self.state = UIGestureRecognizerStateChanged; + } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event @@ -228,6 +266,12 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event [super touchesCancelled:touches withEvent:event]; [self _updateAndDispatchTouches:touches eventName:@"topTouchCancel" originatingTime:event.timestamp]; [self _recordRemovedTouches:touches]; + + if (RCTAllTouchesAreCancelldOrEnded(event.allTouches)) { + self.state = UIGestureRecognizerStateCancelled; + } else if (RCTAnyTouchesChanged(event.allTouches)) { + self.state = UIGestureRecognizerStateChanged; + } } - (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c index 50d69b522b54d8..9ed711cd07be52 100644 --- a/React/Layout/Layout.c +++ b/React/Layout/Layout.c @@ -702,19 +702,6 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { } } - float containerMainAxis = node->layout.dimensions[dim[mainAxis]]; - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (isUndefined(containerMainAxis)) { - containerMainAxis = fmaxf( - // We're missing the last padding at this point to get the final - // dimension - boundAxis(node, mainAxis, mainDim + getPaddingAndBorder(node, trailing[mainAxis])), - // We can never assign a width smaller than the padding and borders - getPaddingAndBorderAxis(node, mainAxis) - ); - } - float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { containerCrossAxis = fmaxf( From 929b2999c45dbc457ec2d3c106bb6750fcba8e45 Mon Sep 17 00:00:00 2001 From: jmstout Date: Tue, 5 May 2015 06:21:11 -0700 Subject: [PATCH 197/250] Fix a bug in the Navigator's gesture.edgeHitWidth implementation Summary: Should resolve #1081 cc @ericvicenti Closes https://github.com/facebook/react-native/pull/1082 Github Author: jmstout Test Plan: Imported from GitHub, without a `Test Plan:` line. Revert Plan: This seems legit, but I'm not qualified to review. --- Libraries/CustomComponents/Navigator/Navigator.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 26c6a46bdc6a3d..ad9ea0e8a863f9 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -54,7 +54,11 @@ var rebound = require('rebound'); var PropTypes = React.PropTypes; +// TODO: this is not ideal because there is no guarantee that the navigator +// is full screen, hwoever we don't have a good way to measure the actual +// size of the navigator right now, so this is the next best thing. var SCREEN_WIDTH = Dimensions.get('window').width; +var SCREEN_HEIGHT = Dimensions.get('window').height; var SCENE_DISABLED_NATIVE_PROPS = { style: { left: SCREEN_WIDTH, @@ -905,13 +909,17 @@ var Navigator = React.createClass({ var travelDist = isTravelVertical ? gestureState.dy : gestureState.dx; var oppositeAxisTravelDist = isTravelVertical ? gestureState.dx : gestureState.dy; + var edgeHitWidth = gesture.edgeHitWidth; if (isTravelInverted) { currentLoc = -currentLoc; travelDist = -travelDist; oppositeAxisTravelDist = -oppositeAxisTravelDist; + edgeHitWidth = isTravelVertical ? + -(SCREEN_HEIGHT - edgeHitWidth) : + -(SCREEN_WIDTH - edgeHitWidth); } var moveStartedInRegion = gesture.edgeHitWidth == null || - currentLoc < gesture.edgeHitWidth; + currentLoc < edgeHitWidth; var moveTravelledFarEnough = travelDist >= gesture.gestureDetectMovement && travelDist > oppositeAxisTravelDist * gesture.directionRatio; From 08246b77dffa90c738c30d69e6c67c37b74e5c03 Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Tue, 5 May 2015 07:16:53 -0700 Subject: [PATCH 198/250] [React Native] Fix immediate animation crash --- Libraries/Animation/AnimationUtils.js | 6 ++++-- Libraries/Animation/RCTAnimationExperimentalManager.m | 2 +- React/Base/RCTConvert.m | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Libraries/Animation/AnimationUtils.js b/Libraries/Animation/AnimationUtils.js index ac62465a02e219..2ee2d8a7097591 100644 --- a/Libraries/Animation/AnimationUtils.js +++ b/Libraries/Animation/AnimationUtils.js @@ -231,8 +231,10 @@ module.exports = { var tickCount = Math.round(duration * ticksPerSecond / 1000); var samples = []; - for (var i = 0; i <= tickCount; i++) { - samples.push(easing.call(defaults, i / tickCount)); + if (tickCount > 0) { + for (var i = 0; i <= tickCount; i++) { + samples.push(easing.call(defaults, i / tickCount)); + } } return samples; diff --git a/Libraries/Animation/RCTAnimationExperimentalManager.m b/Libraries/Animation/RCTAnimationExperimentalManager.m index 64ee577fef30b6..6bcda39ae30d83 100644 --- a/Libraries/Animation/RCTAnimationExperimentalManager.m +++ b/Libraries/Animation/RCTAnimationExperimentalManager.m @@ -114,7 +114,7 @@ static void RCTInvalidAnimationProp(RCTSparseArray *callbacks, NSNumber *tag, NS animationTag:(NSNumber *)animationTag duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay - easingSample:(NSArray *)easingSample + easingSample:(NSNumberArray *)easingSample properties:(NSDictionary *)properties callback:(RCTResponseSenderBlock)callback) { diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index a5d2239af57364..9c27b95d98de4f 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -805,7 +805,9 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family for (NSInteger i = 0; i < idx; i++) { [(NSMutableArray *)values addObject:json[i]]; } - [(NSMutableArray *)values addObject:value]; + if (value) { + [(NSMutableArray *)values addObject:value]; + } copy = YES; } }]; From 3ab4d32538fac43c7dd2320354e30e2e3c07aca3 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Tue, 5 May 2015 07:27:40 -0700 Subject: [PATCH 199/250] [ReactNative] Fix DevMenu crash when launching the app with WebView executor --- React/Base/RCTDevMenu.m | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 6c64e467754ab6..d2e4b7416e0fa1 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -123,11 +123,17 @@ - (void)updateSettings { _settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]]; + __weak RCTDevMenu *weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ - self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue]; - self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue]; - self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue]; - self.executorClass = NSClassFromString(_settings[@"executorClass"]); + RCTDevMenu *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue]; + strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue]; + strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue]; + strongSelf.executorClass = NSClassFromString(strongSelf->_settings[@"executorClass"]); }); } From 88715e5c93af57dbd39c97e3cca5ca66e692e615 Mon Sep 17 00:00:00 2001 From: Iragne Date: Tue, 5 May 2015 09:25:01 -0700 Subject: [PATCH 200/250] Update RCTNavigator.m Summary: Related to this issue I created the PR. Feel free to talk about it. https://github.com/facebook/react-native/issues/65#issuecomment-93240332 Thanks Closes https://github.com/facebook/react-native/pull/1131 Github Author: Iragne Test Plan: Open NavigatorIOS example in UIExplorer, push recurse navigation several times. Press back and observe that it no longer breaks. --- React/Views/RCTNavigator.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index f947a6128e23ba..57415fbb79a8ce 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -461,6 +461,10 @@ - (void)reactBridgeDidFinishTransaction // --- previously caught up -------- ------- still caught up ---------- viewControllerCount == previousReactCount && currentReactCount == previousReactCount; +BOOL jsGettingtooSlow = + // --- previously not caught up -------- ------- no longer caught up ---------- + viewControllerCount < previousReactCount && currentReactCount < previousReactCount; + BOOL reactPushOne = jsGettingAhead && currentReactCount == previousReactCount + 1; BOOL reactPopN = jsGettingAhead && currentReactCount < previousReactCount; @@ -471,7 +475,8 @@ - (void)reactBridgeDidFinishTransaction if (!(jsGettingAhead || jsCatchingUp || jsMakingNoProgressButNeedsToCatchUp || - jsMakingNoProgressAndDoesntNeedTo)) { + jsMakingNoProgressAndDoesntNeedTo || + jsGettingtooSlow)) { RCTLogError(@"JS has only made partial progress to catch up to UIKit"); } if (currentReactCount > _currentViews.count) { From 79e33502fc21aec2abf090e0642ff364feac6234 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 5 May 2015 10:36:44 -0700 Subject: [PATCH 201/250] [react-packager] check in graceful-fs From bd591505f16ac69e6c00cf6979101f07e3920797 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 5 May 2015 10:37:30 -0700 Subject: [PATCH 202/250] [react-packager] update sane --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 54d7ada11c2288..987347a1d4f88f 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "react-timer-mixin": "^0.13.1", "react-tools": "0.13.2", "rebound": "^0.0.12", - "sane": "^1.1.1", + "sane": "^1.1.2", "source-map": "0.1.31", "stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638", "uglify-js": "~2.4.16", From 8a74a9f7c333866842f30e25653faf9ef3373ebc Mon Sep 17 00:00:00 2001 From: Ryan Pastorelle Date: Tue, 5 May 2015 14:14:48 -0700 Subject: [PATCH 203/250] Force `responseHeaders` to lower case to respect case-insensitivity Summary: `XMLHttpRequest.getResponseHeader` is case-insensitive, therefor the React-Native implementation needs to mimic this behavior as to not break libraries that are dependent on this. There is a corresponding issue in `superagent` but this is the root cause (https://github.com/visionmedia/superagent/issues/636). Closes https://github.com/facebook/react-native/pull/1138 Github Author: Ryan Pastorelle Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Network/XMLHttpRequestBase.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Libraries/Network/XMLHttpRequestBase.js b/Libraries/Network/XMLHttpRequestBase.js index 4a4f16ac685178..99a327b5f236eb 100644 --- a/Libraries/Network/XMLHttpRequestBase.js +++ b/Libraries/Network/XMLHttpRequestBase.js @@ -66,7 +66,7 @@ class XMLHttpRequestBase { getResponseHeader(header: string): ?string { if (this.responseHeaders) { - var value = this.responseHeaders[header]; + var value = this.responseHeaders[header.toLowerCase()]; return value !== undefined ? value : null; } return null; @@ -132,7 +132,12 @@ class XMLHttpRequestBase { return; } this.status = status; - this.responseHeaders = responseHeaders || {}; + // Headers should be case-insensitive + var lcResponseHeaders = {}; + for (var header in responseHeaders) { + lcResponseHeaders[header.toLowerCase()] = responseHeaders[header]; + } + this.responseHeaders = lcResponseHeaders; this.responseText = responseText; this._setReadyState(this.DONE); this._sendLoad(); From 57348a4028c33c227f31de6c5cf0f93369bd4a86 Mon Sep 17 00:00:00 2001 From: Evgen Filatov Date: Tue, 5 May 2015 14:14:49 -0700 Subject: [PATCH 204/250] Fixed name of Chome window, Connects to #297 Summary: Hi! I have the same problem as described here https://github.com/facebook/react-native/issues/297 It could occurs after restarting `packager.sh` or `debuger-ui` page. I found simple solution that works for me, but I am not 100% sure it will works for any user with this problem. How could this be tested automatically? Closes https://github.com/facebook/react-native/pull/1101 Github Author: Evgen Filatov Test Plan: Imported from GitHub, without a `Test Plan:` line. --- packager/launchChromeDevTools.applescript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/launchChromeDevTools.applescript b/packager/launchChromeDevTools.applescript index 1fe6f4b075f435..4b718f5bd48c78 100755 --- a/packager/launchChromeDevTools.applescript +++ b/packager/launchChromeDevTools.applescript @@ -10,7 +10,7 @@ on run argv set theURL to item 1 of argv - tell application "Google Chrome" + tell application "Chrome" activate if (count every window) = 0 then From 86cfcbc77e428295f850bfc927b6eb3c6fdd30cf Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Tue, 5 May 2015 14:31:41 -0700 Subject: [PATCH 205/250] [ReactNative] Navigator fix opacity reset for presented scene Summary: jumpN will call enableScene for the dest scene, which makes sure the scene can be made visible, but we need to avoid resetting the opacity for it when it is already the presented scene. There is already a check to make sure we don't reset opacity for the transitioningFrom scene. @public Test Plan: Fixes the issue on Android device when jumping to a scene that is already presented --- Libraries/CustomComponents/Navigator/Navigator.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index ad9ea0e8a863f9..d4c97561bb37cd 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -689,7 +689,8 @@ var Navigator = React.createClass({ var enabledSceneNativeProps = { left: sceneStyle.left, }; - if (sceneIndex !== this.state.transitionFromIndex) { + if (sceneIndex !== this.state.transitionFromIndex && + sceneIndex !== this.state.presentedIndex) { // If we are not in a transition from this index, make sure opacity is 0 // to prevent the enabled scene from flashing over the presented scene enabledSceneNativeProps.opacity = 0; From 7362f11c2250ec394f18256ee4198df570a0e472 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 5 May 2015 14:05:19 -0700 Subject: [PATCH 206/250] [react-packager] Use gracful-fs to avoid EMFILE errors Summary: @public Currently, every time we call into the packager we have to change the ulimit to make sure we don't hit the EMFILE error (the packager uses as much concurrency as possible). Using graceful-fs, the fs module -- with monkey patching -- becomes intelligent enough to recover from EMFILE errors. Test Plan: * set `ulimit -n 256* * start server * request from your browser: http://localhost:8081/RKJSModules/MainBundle/CatalystBundle.includeRequire.bundle * it works --- package.json | 1 + packager/react-packager/index.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/package.json b/package.json index 987347a1d4f88f..8e059c3fa5b66c 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "chalk": "^1.0.0", "connect": "2.8.3", "debug": "~2.1.0", + "graceful-fs": "^3.0.6", "image-size": "0.3.5", "joi": "~5.1.0", "jstransform": "10.1.0", diff --git a/packager/react-packager/index.js b/packager/react-packager/index.js index 3a70659cf09553..a7b6264b7b740f 100644 --- a/packager/react-packager/index.js +++ b/packager/react-packager/index.js @@ -8,6 +8,8 @@ */ 'use strict'; +useGracefulFs(); + var Activity = require('./src/Activity'); var Server = require('./src/Server'); @@ -45,3 +47,16 @@ exports.getDependencies = function(options, main) { return r.dependencies; }); }; + +function useGracefulFs() { + var fs = require('fs'); + var gracefulFs = require('graceful-fs'); + + // A bit sneaky but it's not straightforward to update all the + // modules we depend on. + Object.keys(fs).forEach(function(method) { + if (typeof fs[method] === 'function' && gracefulFs[method]) { + fs[method] = gracefulFs[method]; + } + }); +} From 54974d63336e6d6d70bae313ba6cff5da622703f Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 5 May 2015 17:38:32 -0700 Subject: [PATCH 207/250] +[RCTConvert UIFont:] tests --- React/Base/RCTConvert.h | 1 + React/Base/RCTConvert.m | 28 ++++++++++++++++------------ React/Base/RCTDevMenu.m | 2 -- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 71bf7b30282546..2412f715b729a6 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -76,6 +76,7 @@ + (UIImage *)UIImage:(id)json; + (CGImageRef)CGImage:(id)json; ++ (UIFont *)UIFont:(id)json; + (UIFont *)UIFont:(UIFont *)font withSize:(id)json; + (UIFont *)UIFont:(UIFont *)font withWeight:(id)json; + (UIFont *)UIFont:(UIFont *)font withStyle:(id)json; diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 9c27b95d98de4f..2d0606b6feed30 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -679,6 +679,16 @@ static BOOL RCTFontIsCondensed(UIFont *font) return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0; } ++ (UIFont *)UIFont:(id)json +{ + json = [self NSDictionary:json]; + return [self UIFont:nil + withFamily:json[@"fontFamily"] + size:json[@"fontSize"] + weight:json[@"fontWeight"] + style:json[@"fontStyle"]]; +} + + (UIFont *)UIFont:(UIFont *)font withSize:(id)json { return [self UIFont:font withFamily:nil size:json weight:nil style:nil]; @@ -728,11 +738,6 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family // Get font family familyName = [self NSString:family] ?: familyName; - // Get font style - if (style) { - isItalic = [self RCTFontStyle:style]; - } - // Gracefully handle being given a font name rather than font family, for // example: "Helvetica Light Oblique" rather than just "Helvetica". if ([UIFont fontNamesForFamilyName:familyName].count == 0) { @@ -751,6 +756,11 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family } } + // Get font style + if (style) { + isItalic = [self RCTFontStyle:style]; + } + // Get font weight if (weight) { fontWeight = [self RCTFontWeight:weight]; @@ -758,13 +768,7 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family // Get the closest font that matches the given weight for the fontFamily UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize]; - CGFloat closestWeight; - - if (font && [font.familyName isEqualToString: familyName]) { - closestWeight = RCTWeightOfFont(font); - } else { - closestWeight = INFINITY; - } + CGFloat closestWeight = INFINITY; for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) { UIFont *match = [UIFont fontWithName:name size:fontSize]; diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index d2e4b7416e0fa1..cf5c6635a01f86 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -66,9 +66,7 @@ + (void)initialize // We're swizzling here because it's poor form to override methods in a category, // however UIWindow doesn't actually implement motionEnded:withEvent:, so there's // no need to call the original implementation. -#if RCT_DEV RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:)); -#endif } - (instancetype)init From 77a3190606ea6fdf22f3de70567e50b9456ed22f Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Tue, 5 May 2015 15:27:43 -0700 Subject: [PATCH 208/250] [Docs] Remove deprecated styleProps - rotation, scaleX/Y, translateX/Y all belong under transform from ViewStylePropTypes --- website/server/extractDocs.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js index d96514226d1f10..fec125cadc1baf 100644 --- a/website/server/extractDocs.js +++ b/website/server/extractDocs.js @@ -169,6 +169,14 @@ var styleDocs = styles.slice(1).reduce(function(docs, filepath) { docgenHelpers.findExportedObject, [docgen.handlers.propTypeHandler] ); + + // Remove deprecated style props + if (docs['ViewStylePropTypes']) { + ['rotation', 'scaleX', 'scaleY', 'translateX', 'translateY'].forEach(function(key) { + delete docs['ViewStylePropTypes']['props'][key]; + }); + } + return docs; }, {}); From 88a83084e4cc730cb420d6e70a5d0ec15809769f Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 6 May 2015 00:11:31 -0700 Subject: [PATCH 209/250] Fixed UIExplorer in Catalyst shell --- .../SampleApp.xcodeproj/project.pbxproj | 34 ++++++++++++- Examples/SampleApp/index.ios.js | 2 +- Libraries/Settings/RCTSettingsManager.m | 7 ++- React/Base/RCTConvert.m | 18 ++++--- React/Base/RCTUtils.h | 3 ++ React/Base/RCTUtils.m | 51 +++++++++++++++++++ 6 files changed, 104 insertions(+), 11 deletions(-) diff --git a/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj b/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj index d2fc3eadedee94..3928adc332ef9c 100644 --- a/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj +++ b/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 00E356F31AD99517003FC87E /* SampleAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SampleAppTests.m */; }; 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; @@ -82,6 +83,13 @@ remoteGlobalIDString = 13B07F861A680F5B00A75B9A; remoteInfo = SampleApp; }; + 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTSettings; + }; 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; @@ -117,6 +125,7 @@ 00E356EE1AD99517003FC87E /* SampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* SampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAppTests.m; sourceTree = ""; }; + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = ""; }; @@ -142,6 +151,7 @@ buildActionMask = 2147483647; files = ( 146834051AC3E58100842450 /* libReact.a in Frameworks */, + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, 00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */, 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */, @@ -230,6 +240,14 @@ name = "Supporting Files"; sourceTree = ""; }; + 139105B71AF99BAD00B5F7CC /* Products */ = { + isa = PBXGroup; + children = ( + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, + ); + name = Products; + sourceTree = ""; + }; 13B07FAE1A68108700A75B9A /* SampleApp */ = { isa = PBXGroup; children = ( @@ -263,14 +281,15 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */, 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */, ); @@ -395,6 +414,10 @@ ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; }, + { + ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; + ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + }, { ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; @@ -470,6 +493,13 @@ remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTSettings.a; + remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 146834041AC3E56700842450 /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/Examples/SampleApp/index.ios.js b/Examples/SampleApp/index.ios.js index 629118d10721ad..46c0712bd9b286 100644 --- a/Examples/SampleApp/index.ios.js +++ b/Examples/SampleApp/index.ios.js @@ -24,7 +24,7 @@ var SampleApp = React.createClass({ Press Cmd+R to reload,{'\n'} - Cmd+Control+Z for dev menu + Cmd+D or shake for dev menu ); diff --git a/Libraries/Settings/RCTSettingsManager.m b/Libraries/Settings/RCTSettingsManager.m index b17439eafa15dd..6f8b146c1dc1ef 100644 --- a/Libraries/Settings/RCTSettingsManager.m +++ b/Libraries/Settings/RCTSettingsManager.m @@ -12,6 +12,7 @@ #import "RCTBridge.h" #import "RCTConvert.h" #import "RCTEventDispatcher.h" +#import "RCTUtils.h" @implementation RCTSettingsManager { @@ -53,13 +54,15 @@ - (void)userDefaultsDidChange:(NSNotification *)note return; } - [_bridge.eventDispatcher sendDeviceEventWithName:@"settingsUpdated" body:[_defaults dictionaryRepresentation]]; + [_bridge.eventDispatcher + sendDeviceEventWithName:@"settingsUpdated" + body:RCTJSONClean([_defaults dictionaryRepresentation])]; } - (NSDictionary *)constantsToExport { return @{ - @"settings": [_defaults dictionaryRepresentation] + @"settings": RCTJSONClean([_defaults dictionaryRepresentation]) }; } diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 2d0606b6feed30..6f07feebd77301 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -838,7 +838,9 @@ static id RCTConvertPropertyListValue(id json) { if (!json || json == (id)kCFNull) { return nil; - } else if ([json isKindOfClass:[NSDictionary class]]) { + } + + if ([json isKindOfClass:[NSDictionary class]]) { __block BOOL copy = NO; NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]]; [json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, BOOL *stop) { @@ -849,7 +851,9 @@ static id RCTConvertPropertyListValue(id json) copy |= value != jsonValue; }]; return copy ? values : json; - } else if ([json isKindOfClass:[NSArray class]]) { + } + + if ([json isKindOfClass:[NSArray class]]) { __block BOOL copy = NO; __block NSArray *values = json; [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, BOOL *stop) { @@ -864,15 +868,17 @@ static id RCTConvertPropertyListValue(id json) for (NSInteger i = 0; i < idx; i++) { [(NSMutableArray *)values addObject:json[i]]; } - [(NSMutableArray *)values addObject:value]; + if (value) { + [(NSMutableArray *)values addObject:value]; + } copy = YES; } }]; return values; - } else { - // All other JSON types are supported by property lists - return json; } + + // All other JSON types are supported by property lists + return json; } + (NSPropertyList)NSPropertyList:(id)json diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h index 1c04125684fd11..6c4b91464e6390 100644 --- a/React/Base/RCTUtils.h +++ b/React/Base/RCTUtils.h @@ -19,6 +19,9 @@ RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error); RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error); +// Strip non JSON-safe values from an object graph +RCT_EXTERN id RCTJSONClean(id object); + // Get MD5 hash of a string (TODO: currently unused. Remove?) RCT_EXTERN NSString *RCTMD5Hash(NSString *string); diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 3c644dbff4e788..a74b4e11464d9f 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -42,6 +42,57 @@ id RCTJSONParse(NSString *jsonString, NSError **error) return [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:error]; } +id RCTJSONClean(id object) +{ + static dispatch_once_t onceToken; + static NSSet *validLeafTypes; + dispatch_once(&onceToken, ^{ + validLeafTypes = [[NSSet alloc] initWithArray:@[ + [NSString class], + [NSMutableString class], + [NSNumber class], + [NSNull class], + ]]; + }); + + if ([validLeafTypes containsObject:[object classForCoder]]) { + return object; + } + + if ([object isKindOfClass:[NSDictionary class]]) { + __block BOOL copy = NO; + NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[object count]]; + [object enumerateKeysAndObjectsUsingBlock:^(NSString *key, id item, BOOL *stop) { + id value = RCTJSONClean(item); + values[key] = value; + copy |= value != item; + }]; + return copy ? values : object; + } + + if ([object isKindOfClass:[NSArray class]]) { + __block BOOL copy = NO; + __block NSArray *values = object; + [object enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) { + id value = RCTJSONClean(item); + if (copy) { + [(NSMutableArray *)values addObject:value]; + } else if (value != item) { + // Converted value is different, so we'll need to copy the array + values = [[NSMutableArray alloc] initWithCapacity:values.count]; + for (NSInteger i = 0; i < idx; i++) { + [(NSMutableArray *)values addObject:object[i]]; + } + [(NSMutableArray *)values addObject:value]; + copy = YES; + } + }]; + return values; + } + + return (id)kCFNull; +} + NSString *RCTMD5Hash(NSString *string) { const char *str = [string UTF8String]; From c88b1d2c881c66a780585b971f53951765427379 Mon Sep 17 00:00:00 2001 From: Quico Moya Date: Wed, 6 May 2015 12:29:25 +0200 Subject: [PATCH 210/250] Fix grammatical error --- docs/Tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 7cb77c08449734..b8d7b2aabaccc8 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -287,7 +287,7 @@ Let's now modify this application to render all of this data in a `ListView` com Why is a `ListView` better than just rendering all of these elements or putting them in a `ScrollView`? Despite React being fast, rendering a possibly infinite list of elements could be slow. `ListView` schedules rendering of views so that you only display the ones on screen and those already rendered but off screen are removed from the native view hierarchy. -First thing's first: add the `ListView` require to the top of the file. +First things first: add the `ListView` require to the top of the file. ```javascript var { From ea470408e2f02574c725a532e55484d3777806fb Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Wed, 6 May 2015 05:44:48 -0700 Subject: [PATCH 211/250] [ReactNative] Allow JSON fragments on RCTJSONStringify --- React/Base/RCTUtils.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index a74b4e11464d9f..ff11b57640074b 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -20,7 +20,7 @@ NSString *RCTJSONStringify(id jsonObject, NSError **error) { - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:error]; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:(NSJSONWritingOptions)NSJSONReadingAllowFragments error:error]; return jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] : nil; } From 286e1b0ae9541c064cfe0644aef7c711bcb260bd Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 6 May 2015 09:31:39 -0700 Subject: [PATCH 212/250] [ReactNative] Re-record OSS snapshot tests --- .../testTextExampleSnapshot_1@2x.png | Bin 270778 -> 270812 bytes .../testViewExampleSnapshot_1@2x.png | Bin 89870 -> 89799 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png index d697ae5b5e3e17787331c45592ba574db67d4f7a..2c6675b7b4eeba914d0ee3fee1d82d18fcae9b85 100644 GIT binary patch delta 51641 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 Lt6HFJ`TYL^<^Zn; diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testViewExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testViewExampleSnapshot_1@2x.png index 74e03581c464bc6a278eda27fb8ef9724f9a6bb5..161ddba986f2818fa61219fc48ae626b95c12d4a 100644 GIT binary patch delta 1818 zcmcJP{a4b59>;+*b}EBcna0}%qzkbenpZ9rvKJRx~7yOwJ zUVado{W$dMd*EX+)?EMQ9(Tdh5uW9UE(AQ1UygUpnUxi+_~i$?pP*#R05 zLw~zoUpjNyE%O2JmZp?9Ch8iDLYnuf*CCY?^+31&8B0{awaljR-$?Lw$#kSp6TC3B z{4?w1sS+cnMgQ5}wc{^1pE4?#)2cK`a+Eq%baZT=%T|2xC6X8&G4L?vJ|sYYzp1=}cJji}G;OWOE~{3@08 zaO*KfYp+#{weh+Z)My|tIItXW)VM@hsotQDB{HWfl1K*D1>RWa3LP+F=OQbmb2b&- zxFDH9@oUcx9WAoH6QhBmMTrM5Pz&nJ@8q!?RhIc&qNrD^dVd|;rEBjLnJ?9uMoBqR zK}abIK4g}ht;uL(Q)+PAy_WvY3CMm9Z!$May(BACh;t&Fb!55`Obpht<>?oqUH44# zAzKPQ{H{wYH?iV>_+>gsWrz;$`JfP4TX)i4ow*ujxCD$PLbRm;CLa)e+sSo(-x@VY z*EODZ^5Ki#eF;v+Ly8xLyG3?4xwju1a{L)!7(cd3-p-uvca~$j76lWW!2)NsW|{`( zCj_qxVZxS&SY0jF-!wC=Pd8CsV6P(|8Awsc{#djte2x#&#W=nwDa_6hRh5RgB~sLe9Y)>(~I#ia{4PwX?Uv zH#qN8bL8%ngViRg?B*3%`x=)6j_h35sqN*|x;XYHu~eI7Nl~n#Chdr1k^sP>dpOvE zIyj6Yl##ut&)X~;vP|1X^Rm~pced$9iDw4ncTgBc*q~Gk@rx&f)3gTvh?`wY!i5n) zRzGaU6ceMwM-sIAg>*j-7zT#58MY8|U(@66)0+Gv^3vpW zuX9u$|6%jxfePPB!gMZ>Rj^ZVlC0k6Aa%G4(erNnsBO7IElc5FxIVu6vd1BZpB6Ol z$`;&;kh!=;HW!40x5{ViSQYin5Gv-Yxho!JHz+1*blUy9v7biqAa7`j1Z^jzItP;f zjTMR1a8oS;J(Do(dN^u#y*nW58TVvQ5l6fGQ482Tj8vM~STM&+uIY1EBJl!o1g1AoWS7i-QCaaQ(T{noNvvV?4ApsD-{0Jd190JZ z$@Xi}PXZq<1mY0RKu)F{NLZ<8mB|qw9Ix)_IBUL5*e;xur2LuNyZuSxwrDRx&Gk z^f3xt*7Lkfu>$>Bi@g>n(DX^)2xRbleO>@gZX07p=83ywm9$Tj_v(f zVJE{>5YGr{l;A%&!wNZNbKer<3hV*oD(XaEs7(EXOJ(+49!&V8a&`_2gE#Vg=EdK! z2Cz`OFFx2>iN&8lR_3Brbgrnj8(PWAPJntsLs@TQp#Oj!hOm53KvVh2C!qJCtSmg# nm;W~s6bt?T?O7}O-#K?X;k_H#L7CF>Rc}WRM<03=ntbu!zVy7? delta 2054 zcmd^9`&ZHl76&$)o!TDLn66EuTc&BLIG7J?)6{$3E}0~vnG~U$>e}6kXPi`o#A|?A9;R22yUvP{W9y9^In*gdwd&^_ zKFY53#MPka?9KN!5hgA$$0U{+6Nw{RMVHzqNhW&`-TfLXDh70l)0#8mNbXk>+ z%{4vl>3>ckNjiWb$JJewRAn^*D_uQ$EDilgd9yw@Fq#EfuDT2eV|) z#~8>Ck54ljPJ=p>{j+dol{|-Op72SE(MN+_%dY)3T5miY$W!}P>S#hRlu~l-e8E~I zXCvYmg(kwRQX+r0&+}2o(Wi}G30}X|BKL5GH8GEe5@Q21aK#b}j5?M$lH^gmMpSkQ zdz#FRECQi9izrMqFsV>RpS$6_#27FZ<}}qA;~s-0`Rg%|d($YfMUE~9e%&Uo_%c*G zoaiySYvZ|j5aI9lwb-AwcT!($Qll1FG4q`;z|qsc?iCvdR{f&h+N~yr`|<8hqhy}h zlewktx@Bi^OPU%PnDE__AQZ`bI+}^hYC+pf0Huhq328P{BnUNkqB0&_c;rHep>_{1L%}7?ULj>J~CbRHM}M{m+zEcDI>`aHnS>C5N~@$9B>WjnOc<8 zmqg>{F3%6sCkhQ4lb{9te63AhONXr_#{|8#?LA1`>}%ZT+X0~rcJl%b{)?(;7&>sP zOe5!TJhU_oBJ{!BhO{~L#uWDJySO335ue-N^sW2!$;eTX{(UoR6XP+dJzk#Wp4$KH zX!Lo_S3sPWFn$>d^%4L3h+b8Xd<+<=8Py`mF{q*2k$#}@BDq5hK9UpdC_)`{N0gD=e zE<3B4wUq!(DVaEpJC$f`&n=*6kz32b^N&)|e{ljQ5A(Kca|L{K?jN%=Zr~7<3h}M5 zI3B6U$2JdXn$dE;MoGZS<2B~oFIiI$q1c&l;VWyXBdjRRNxyeP3GCfxqH1Pc&?~h+ zBDl5J2pQBLTNa;|1klE8rHW^Q{7crwDa_;ILl)wlQmsQCHZ0h$i^LBJdVYw3+Jee1 z{pZ@?I0E>=%*(#q(S5hnZZ^z13mcx^VYVc!IsKTlP(xl4?xY`kZ;yp4ZLq9qbf2U; z8!yz&t9dUQsJ}knG851Xo-jUc6mq^bU_9cmQz$XOwfK@M&_NKn{kmW7^-L6+v+~eF zmvptlUT=s6fHTWRvupQ;OW61eW|=sQ zxX_acZgn0+EhTiL1XvN)=3UspZO{39l6qjog z(LC4v1hMadUHTi@#327RJB2~)y^A8wr6pOTsKN@V#GxKmpE%P-&xr{ z7F+D%E1fj<)_HYB&dsKk#Qp74BTlG!=mW From 735f67337a0919b8d2e1addafc6269c854139b3a Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 6 May 2015 09:33:20 -0700 Subject: [PATCH 213/250] [ReactNative] Fail faster in OSS tests --- Examples/SampleApp/SampleAppTests/SampleAppTests.m | 4 ++-- Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m | 5 +++-- Libraries/RCTTest/RCTTestRunner.m | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Examples/SampleApp/SampleAppTests/SampleAppTests.m b/Examples/SampleApp/SampleAppTests/SampleAppTests.m index 64794271376b04..d8dce811dac25d 100644 --- a/Examples/SampleApp/SampleAppTests/SampleAppTests.m +++ b/Examples/SampleApp/SampleAppTests/SampleAppTests.m @@ -44,8 +44,8 @@ - (void)testRendersWelcomeScreen { NSString *redboxError = nil; while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date]; + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; redboxError = [[RCTRedBox sharedInstance] currentErrorMessage]; diff --git a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m index 2c2359b4400339..df748ef0376ec2 100644 --- a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m +++ b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m @@ -69,8 +69,9 @@ - (void)testAAA_RootViewLoadsAndRenders NSString *redboxError = nil; while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date]; + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + redboxError = [[RCTRedBox sharedInstance] currentErrorMessage]; foundElement = [self findSubviewInView:vc.view matching:^(UIView *view) { if ([view respondsToSelector:@selector(attributedText)]) { diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index 0aa148fbc889bb..9c0cacf709c3e1 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -84,8 +84,8 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictiona NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage]; while ([date timeIntervalSinceNow] > 0 && ![testModule isDone] && error == nil) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date]; + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; error = [[RCTRedBox sharedInstance] currentErrorMessage]; } [rootView removeFromSuperview]; From 8c95de11e167d7f3a217619f7623eecf220dd711 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 6 May 2015 09:57:50 -0700 Subject: [PATCH 214/250] [ReactNative] Re-record OSS snapshot tests --- .../testTextExampleSnapshot_1@2x.png | Bin 270778 -> 270812 bytes .../testViewExampleSnapshot_1@2x.png | Bin 89870 -> 89799 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png index d697ae5b5e3e17787331c45592ba574db67d4f7a..2c6675b7b4eeba914d0ee3fee1d82d18fcae9b85 100644 GIT binary patch delta 51641 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 Lt6HFJ`TYL^<^Zn; diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testViewExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testViewExampleSnapshot_1@2x.png index 74e03581c464bc6a278eda27fb8ef9724f9a6bb5..161ddba986f2818fa61219fc48ae626b95c12d4a 100644 GIT binary patch delta 1818 zcmcJP{a4b59>;+*b}EBcna0}%qzkbenpZ9rvKJRx~7yOwJ zUVado{W$dMd*EX+)?EMQ9(Tdh5uW9UE(AQ1UygUpnUxi+_~i$?pP*#R05 zLw~zoUpjNyE%O2JmZp?9Ch8iDLYnuf*CCY?^+31&8B0{awaljR-$?Lw$#kSp6TC3B z{4?w1sS+cnMgQ5}wc{^1pE4?#)2cK`a+Eq%baZT=%T|2xC6X8&G4L?vJ|sYYzp1=}cJji}G;OWOE~{3@08 zaO*KfYp+#{weh+Z)My|tIItXW)VM@hsotQDB{HWfl1K*D1>RWa3LP+F=OQbmb2b&- zxFDH9@oUcx9WAoH6QhBmMTrM5Pz&nJ@8q!?RhIc&qNrD^dVd|;rEBjLnJ?9uMoBqR zK}abIK4g}ht;uL(Q)+PAy_WvY3CMm9Z!$May(BACh;t&Fb!55`Obpht<>?oqUH44# zAzKPQ{H{wYH?iV>_+>gsWrz;$`JfP4TX)i4ow*ujxCD$PLbRm;CLa)e+sSo(-x@VY z*EODZ^5Ki#eF;v+Ly8xLyG3?4xwju1a{L)!7(cd3-p-uvca~$j76lWW!2)NsW|{`( zCj_qxVZxS&SY0jF-!wC=Pd8CsV6P(|8Awsc{#djte2x#&#W=nwDa_6hRh5RgB~sLe9Y)>(~I#ia{4PwX?Uv zH#qN8bL8%ngViRg?B*3%`x=)6j_h35sqN*|x;XYHu~eI7Nl~n#Chdr1k^sP>dpOvE zIyj6Yl##ut&)X~;vP|1X^Rm~pced$9iDw4ncTgBc*q~Gk@rx&f)3gTvh?`wY!i5n) zRzGaU6ceMwM-sIAg>*j-7zT#58MY8|U(@66)0+Gv^3vpW zuX9u$|6%jxfePPB!gMZ>Rj^ZVlC0k6Aa%G4(erNnsBO7IElc5FxIVu6vd1BZpB6Ol z$`;&;kh!=;HW!40x5{ViSQYin5Gv-Yxho!JHz+1*blUy9v7biqAa7`j1Z^jzItP;f zjTMR1a8oS;J(Do(dN^u#y*nW58TVvQ5l6fGQ482Tj8vM~STM&+uIY1EBJl!o1g1AoWS7i-QCaaQ(T{noNvvV?4ApsD-{0Jd190JZ z$@Xi}PXZq<1mY0RKu)F{NLZ<8mB|qw9Ix)_IBUL5*e;xur2LuNyZuSxwrDRx&Gk z^f3xt*7Lkfu>$>Bi@g>n(DX^)2xRbleO>@gZX07p=83ywm9$Tj_v(f zVJE{>5YGr{l;A%&!wNZNbKer<3hV*oD(XaEs7(EXOJ(+49!&V8a&`_2gE#Vg=EdK! z2Cz`OFFx2>iN&8lR_3Brbgrnj8(PWAPJntsLs@TQp#Oj!hOm53KvVh2C!qJCtSmg# nm;W~s6bt?T?O7}O-#K?X;k_H#L7CF>Rc}WRM<03=ntbu!zVy7? delta 2054 zcmd^9`&ZHl76&$)o!TDLn66EuTc&BLIG7J?)6{$3E}0~vnG~U$>e}6kXPi`o#A|?A9;R22yUvP{W9y9^In*gdwd&^_ zKFY53#MPka?9KN!5hgA$$0U{+6Nw{RMVHzqNhW&`-TfLXDh70l)0#8mNbXk>+ z%{4vl>3>ckNjiWb$JJewRAn^*D_uQ$EDilgd9yw@Fq#EfuDT2eV|) z#~8>Ck54ljPJ=p>{j+dol{|-Op72SE(MN+_%dY)3T5miY$W!}P>S#hRlu~l-e8E~I zXCvYmg(kwRQX+r0&+}2o(Wi}G30}X|BKL5GH8GEe5@Q21aK#b}j5?M$lH^gmMpSkQ zdz#FRECQi9izrMqFsV>RpS$6_#27FZ<}}qA;~s-0`Rg%|d($YfMUE~9e%&Uo_%c*G zoaiySYvZ|j5aI9lwb-AwcT!($Qll1FG4q`;z|qsc?iCvdR{f&h+N~yr`|<8hqhy}h zlewktx@Bi^OPU%PnDE__AQZ`bI+}^hYC+pf0Huhq328P{BnUNkqB0&_c;rHep>_{1L%}7?ULj>J~CbRHM}M{m+zEcDI>`aHnS>C5N~@$9B>WjnOc<8 zmqg>{F3%6sCkhQ4lb{9te63AhONXr_#{|8#?LA1`>}%ZT+X0~rcJl%b{)?(;7&>sP zOe5!TJhU_oBJ{!BhO{~L#uWDJySO335ue-N^sW2!$;eTX{(UoR6XP+dJzk#Wp4$KH zX!Lo_S3sPWFn$>d^%4L3h+b8Xd<+<=8Py`mF{q*2k$#}@BDq5hK9UpdC_)`{N0gD=e zE<3B4wUq!(DVaEpJC$f`&n=*6kz32b^N&)|e{ljQ5A(Kca|L{K?jN%=Zr~7<3h}M5 zI3B6U$2JdXn$dE;MoGZS<2B~oFIiI$q1c&l;VWyXBdjRRNxyeP3GCfxqH1Pc&?~h+ zBDl5J2pQBLTNa;|1klE8rHW^Q{7crwDa_;ILl)wlQmsQCHZ0h$i^LBJdVYw3+Jee1 z{pZ@?I0E>=%*(#q(S5hnZZ^z13mcx^VYVc!IsKTlP(xl4?xY`kZ;yp4ZLq9qbf2U; z8!yz&t9dUQsJ}knG851Xo-jUc6mq^bU_9cmQz$XOwfK@M&_NKn{kmW7^-L6+v+~eF zmvptlUT=s6fHTWRvupQ;OW61eW|=sQ zxX_acZgn0+EhTiL1XvN)=3UspZO{39l6qjog z(LC4v1hMadUHTi@#327RJB2~)y^A8wr6pOTsKN@V#GxKmpE%P-&xr{ z7F+D%E1fj<)_HYB&dsKk#Qp74BTlG!=mW From 790cee6e26ef907520df45da66a9b5aa65255312 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 6 May 2015 10:46:45 -0700 Subject: [PATCH 215/250] Fixed broken listview header alignment --- React/Base/RCTConvert.h | 1 + React/Base/RCTConvert.m | 14 +++ React/Base/RCTDevMenu.m | 4 +- React/Views/RCTScrollView.h | 2 +- React/Views/RCTScrollView.m | 144 ++++++++++++----------------- React/Views/RCTScrollViewManager.m | 2 +- 6 files changed, 76 insertions(+), 91 deletions(-) diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 2412f715b729a6..dd99ac9fbedaa1 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -39,6 +39,7 @@ + (NSString *)NSString:(id)json; + (NSNumber *)NSNumber:(id)json; + (NSData *)NSData:(id)json; ++ (NSIndexSet *)NSIndexSet:(id)json; + (NSURL *)NSURL:(id)json; + (NSURLRequest *)NSURLRequest:(id)json; diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 6f07feebd77301..06b5aa023bbae0 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -64,6 +64,20 @@ + (NSData *)NSData:(id)json return [[self NSString:json] dataUsingEncoding:NSUTF8StringEncoding]; } ++ (NSIndexSet *)NSIndexSet:(id)json +{ + json = [self NSNumberArray:json]; + NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init]; + for (NSNumber *number in json) { + NSInteger index = number.integerValue; + if (RCT_DEBUG && index < 0) { + RCTLogError(@"Invalid index value %zd. Indices must be positive.", index); + } + [indexSet addIndex:index]; + } + return indexSet; +} + + (NSURL *)NSURL:(id)json { NSString *path = [self NSString:json]; diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index cf5c6635a01f86..3d88caf55b41ea 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -119,8 +119,6 @@ - (instancetype)init - (void)updateSettings { - _settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]]; - __weak RCTDevMenu *weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ RCTDevMenu *strongSelf = weakSelf; @@ -128,6 +126,8 @@ - (void)updateSettings return; } + strongSelf->_settings = [NSMutableDictionary dictionaryWithDictionary:[strongSelf->_defaults objectForKey:RCTDevMenuSettingsKey]]; + strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue]; strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue]; strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue]; diff --git a/React/Views/RCTScrollView.h b/React/Views/RCTScrollView.h index f218ea6ead4360..0333a38a74f024 100644 --- a/React/Views/RCTScrollView.h +++ b/React/Views/RCTScrollView.h @@ -45,6 +45,6 @@ @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; @property (nonatomic, assign) NSTimeInterval scrollEventThrottle; @property (nonatomic, assign) BOOL centerContent; -@property (nonatomic, copy) NSArray *stickyHeaderIndices; +@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices; @end diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m index 1b31700649b31d..ca8f94242df8c7 100644 --- a/React/Views/RCTScrollView.m +++ b/React/Views/RCTScrollView.m @@ -28,8 +28,8 @@ */ @interface RCTCustomScrollView : UIScrollView -@property (nonatomic, copy, readwrite) NSArray *stickyHeaderIndices; -@property (nonatomic, readwrite, assign) BOOL centerContent; +@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices; +@property (nonatomic, assign) BOOL centerContent; @end @@ -155,97 +155,72 @@ - (void)setContentOffset:(CGPoint)contentOffset [super setContentOffset:contentOffset]; } -- (void)setBounds:(CGRect)bounds -{ - [super setBounds:bounds]; - [self dockClosestSectionHeader]; -} - - (void)dockClosestSectionHeader { UIView *contentView = [self contentView]; - if (_stickyHeaderIndices.count == 0 || !contentView) { - return; - } - - // find the section header that needs to be docked - NSInteger firstIndexInView = [[_stickyHeaderIndices firstObject] integerValue] + 1; - CGRect scrollBounds = self.bounds; - scrollBounds.origin.x += self.contentInset.left; - scrollBounds.origin.y += self.contentInset.top; - - NSInteger i = 0; - for (UIView *subview in contentView.reactSubviews) { - CGRect rowFrame = [RCTCustomScrollView _calculateUntransformedFrame:subview]; - if (CGRectIntersectsRect(scrollBounds, rowFrame)) { - firstIndexInView = i; - break; + CGFloat scrollTop = self.bounds.origin.y + self.contentInset.top; + + // Find the section headers that need to be docked + __block UIView *previousHeader = nil; + __block UIView *currentHeader = nil; + __block UIView *nextHeader = nil; + NSInteger subviewCount = contentView.reactSubviews.count; + [_stickyHeaderIndices enumerateIndexesWithOptions:0 usingBlock:^(NSUInteger idx, BOOL *stop) { + + if (idx >= subviewCount) { + RCTLogError(@"Sticky header index %zd was outside the range {0, %zd}", idx, subviewCount); + return; } - i++; - } - NSInteger stickyHeaderii = 0; - for (NSNumber *stickyHeaderI in _stickyHeaderIndices) { - if ([stickyHeaderI integerValue] > firstIndexInView) { - break; - } - stickyHeaderii++; - } - stickyHeaderii = MAX(0, stickyHeaderii - 1); - // Set up transforms for the various section headers - NSInteger currentlyDockedIndex = [_stickyHeaderIndices[stickyHeaderii] integerValue]; - NSInteger previouslyDockedIndex = stickyHeaderii > 0 ? [_stickyHeaderIndices[stickyHeaderii-1] integerValue] : -1; - NSInteger nextDockedIndex = (stickyHeaderii < _stickyHeaderIndices.count - 1) ? - [_stickyHeaderIndices[stickyHeaderii + 1] integerValue] : -1; + UIView *header = contentView.reactSubviews[idx]; + + // If nextHeader not yet found, search for docked headers + if (!nextHeader) { + CGFloat height = header.bounds.size.height; + CGFloat top = header.center.y - height * header.layer.anchorPoint.y; + if (top > scrollTop) { + nextHeader = header; + } else { + previousHeader = currentHeader; + currentHeader = header; + } + } - UIView *currentHeader = contentView.reactSubviews[currentlyDockedIndex]; - UIView *previousHeader = previouslyDockedIndex >= 0 ? contentView.reactSubviews[previouslyDockedIndex] : nil; - CGRect curFrame = [RCTCustomScrollView _calculateUntransformedFrame:currentHeader]; + // Reset transforms for header views + header.transform = CGAffineTransformIdentity; + header.layer.zPosition = ZINDEX_DEFAULT; - if (previousHeader) { - // the previous header is offset to sit right above the currentlyDockedHeader's initial position - // (so it scrolls away nicely once the currentHeader locks into position) - CGRect previousFrame = [RCTCustomScrollView _calculateUntransformedFrame:previousHeader]; - CGFloat yOffset = curFrame.origin.y - previousFrame.origin.y - previousFrame.size.height; - previousHeader.transform = CGAffineTransformMakeTranslation(0, yOffset); - } + }]; - UIView *nextHeader = nextDockedIndex >= 0 ? contentView.reactSubviews[nextDockedIndex] : nil; - CGRect nextFrame = [RCTCustomScrollView _calculateUntransformedFrame:nextHeader]; - - if (curFrame.origin.y < scrollBounds.origin.y) { - // scrolled off (or being scrolled off) the top of the screen - CGFloat yOffset = 0; - if (nextHeader && nextFrame.origin.y < scrollBounds.origin.y + curFrame.size.height) { - // next frame is bumping me off if scrolling down (or i'm bumping the next one off if scrolling up) - yOffset = nextFrame.origin.y - curFrame.origin.y - curFrame.size.height; - } else { - // standard sticky header position - yOffset = scrollBounds.origin.y - curFrame.origin.y; - } - currentHeader.transform = CGAffineTransformMakeTranslation(0, yOffset); - currentHeader.layer.zPosition = ZINDEX_STICKY_HEADER; - } else { - // i'm the current header but in the viewport, so just scroll in normal position - currentHeader.transform = CGAffineTransformIdentity; - currentHeader.layer.zPosition = ZINDEX_DEFAULT; + // If no docked header, bail out + if (!currentHeader) { + return; } - // in our setup, 'next header' will always just scroll with the page + // Adjust current header to hug the top of the screen + CGFloat currentFrameHeight = currentHeader.bounds.size.height; + CGFloat currentFrameTop = currentHeader.center.y - currentFrameHeight * currentHeader.layer.anchorPoint.y; + CGFloat yOffset = scrollTop - currentFrameTop; if (nextHeader) { - nextHeader.transform = CGAffineTransformIdentity; - nextHeader.layer.zPosition = ZINDEX_DEFAULT; + // The next header nudges the current header out of the way when it reaches + // the top of the screen + CGFloat nextFrameHeight = nextHeader.bounds.size.height; + CGFloat nextFrameTop = nextHeader.center.y - nextFrameHeight * nextHeader.layer.anchorPoint.y; + CGFloat overlap = currentFrameHeight - (nextFrameTop - scrollTop); + yOffset -= MAX(0, overlap); } -} + currentHeader.transform = CGAffineTransformMakeTranslation(0, yOffset); + currentHeader.layer.zPosition = ZINDEX_STICKY_HEADER; -+ (CGRect)_calculateUntransformedFrame:(UIView *)view -{ - CGRect frame = CGRectNull; - if (view) { - frame.size = view.bounds.size; - frame.origin = CGPointMake(view.layer.position.x - view.bounds.size.width * view.layer.anchorPoint.x, view.layer.position.y - view.bounds.size.height * view.layer.anchorPoint.y); + if (previousHeader) { + // The previous header sits right above the currentHeader's initial position + // so it scrolls away nicely once the currentHeader has locked into place + CGFloat previousFrameHeight = previousHeader.bounds.size.height; + CGFloat targetCenter = currentFrameTop - previousFrameHeight * (1.0 - previousHeader.layer.anchorPoint.y); + yOffset = targetCenter - previousHeader.center.y; + previousHeader.transform = CGAffineTransformMakeTranslation(0, yOffset); + previousHeader.layer.zPosition = ZINDEX_STICKY_HEADER; } - return frame; } @end @@ -312,7 +287,7 @@ - (void)setCenterContent:(BOOL)centerContent _scrollView.centerContent = centerContent; } -- (void)setStickyHeaderIndices:(NSArray *)headerIndices +- (void)setStickyHeaderIndices:(NSIndexSet *)headerIndices { RCTAssert(_scrollView.contentSize.width <= self.frame.size.width, @"sticky headers are not supported with horizontal scrolled views"); @@ -340,14 +315,8 @@ - (void)layoutSubviews - (void)setContentInset:(UIEdgeInsets)contentInset { - CGPoint contentOffset = _scrollView.contentOffset; - _contentInset = contentInset; - [RCTView autoAdjustInsetsForView:self - withScrollView:_scrollView - updateOffset:NO]; - - _scrollView.contentOffset = contentOffset; + [self setNeedsLayout]; } - (void)scrollToOffset:(CGPoint)offset @@ -390,6 +359,7 @@ - (void)delegateMethod:(UIScrollView *)scrollView \ - (void)scrollViewDidScroll:(UIScrollView *)scrollView { + [_scrollView dockClosestSectionHeader]; [self updateClippedSubviews]; NSTimeInterval now = CACurrentMediaTime(); diff --git a/React/Views/RCTScrollViewManager.m b/React/Views/RCTScrollViewManager.m index ededf5f7fc22d4..8441de74d29b1c 100644 --- a/React/Views/RCTScrollViewManager.m +++ b/React/Views/RCTScrollViewManager.m @@ -41,7 +41,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL) RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL) RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL) -RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSNumberArray) +RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSIndexSet) RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval) RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat) RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets) From 40e3f6ea8c17c0fcc43eab5a9e836b6a6be47d5c Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Wed, 6 May 2015 12:17:23 -0700 Subject: [PATCH 216/250] [ReactNative] Navigator initialRoute default change Summary: The default initialRoute was the first route, but it makes more sense for the default initialRoute to be the last route in the initialRouteStack. Updated the docs to reflect that. @public Test Plan: Updated call sites and checked that they work. Not many places use initialRouteStack yet. --- .../CustomComponents/Navigator/Navigator.js | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index d4c97561bb37cd..c009e8086f10d9 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -227,15 +227,17 @@ var Navigator = React.createClass({ renderScene: PropTypes.func.isRequired, /** - * Provide a single "route" to start on. A route is an arbitrary object - * that the navigator will use to identify each scene before rendering. - * Either initialRoute or initialRouteStack is required. + * Specify a route to start on. A route is an object that the navigator + * will use to identify each scene to render. `initialRoute` must be + * a route in the `initialRouteStack` if both props are provided. The + * `initialRoute` will default to the last item in the `initialRouteStack`. */ initialRoute: PropTypes.object, /** - * Provide a set of routes to initially mount the scenes for. Required if no - * initialRoute is provided + * Provide a set of routes to initially mount. Required if no initialRoute + * is provided. Otherwise, it will default to an array containing only the + * `initialRoute` */ initialRouteStack: PropTypes.arrayOf(PropTypes.object), @@ -295,21 +297,18 @@ var Navigator = React.createClass({ }, getInitialState: function() { - var routeStack = this.props.initialRouteStack || []; - var initialRouteIndex = 0; - if (this.props.initialRoute && routeStack.length) { + var routeStack = this.props.initialRouteStack || [this.props.initialRoute]; + invariant( + routeStack.length >= 1, + 'Navigator requires props.initialRoute or props.initialRouteStack.' + ); + var initialRouteIndex = routeStack.length - 1; + if (this.props.initialRoute) { initialRouteIndex = routeStack.indexOf(this.props.initialRoute); invariant( initialRouteIndex !== -1, 'initialRoute is not in initialRouteStack.' ); - } else if (this.props.initialRoute) { - routeStack = [this.props.initialRoute]; - } else { - invariant( - routeStack.length >= 1, - 'Navigator requires props.initialRoute or props.initialRouteStack.' - ); } return { sceneConfigStack: routeStack.map( From d177a45bdce124b635dbc31ab0949d2e9d139eb3 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Wed, 6 May 2015 14:28:05 -0700 Subject: [PATCH 217/250] [ReactNative] Pull transform properties out into TransformPropTypes --- .../Components/View/ViewStylePropTypes.js | 11 ++------ Libraries/Image/ImageStylePropTypes.js | 2 ++ Libraries/StyleSheet/TransformPropTypes.js | 28 +++++++++++++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 Libraries/StyleSheet/TransformPropTypes.js diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index a5b591a61f8ef6..c1f6b4b1ccf7d5 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -13,12 +13,14 @@ var LayoutPropTypes = require('LayoutPropTypes'); var ReactPropTypes = require('ReactPropTypes'); +var TransformPropTypes = require('TransformPropTypes'); /** * Warning: Some of these properties may not be supported in all releases. */ var ViewStylePropTypes = { ...LayoutPropTypes, + ...TransformPropTypes, backgroundColor: ReactPropTypes.string, borderColor: ReactPropTypes.string, borderTopColor: ReactPropTypes.string, @@ -34,15 +36,6 @@ var ViewStylePropTypes = { ), shadowOpacity: ReactPropTypes.number, shadowRadius: ReactPropTypes.number, - transform: ReactPropTypes.arrayOf(ReactPropTypes.object), - transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), - - // DEPRECATED - rotation: ReactPropTypes.number, - scaleX: ReactPropTypes.number, - scaleY: ReactPropTypes.number, - translateX: ReactPropTypes.number, - translateY: ReactPropTypes.number, }; module.exports = ViewStylePropTypes; diff --git a/Libraries/Image/ImageStylePropTypes.js b/Libraries/Image/ImageStylePropTypes.js index d46807ce7c2a14..c70bee73a1908e 100644 --- a/Libraries/Image/ImageStylePropTypes.js +++ b/Libraries/Image/ImageStylePropTypes.js @@ -14,9 +14,11 @@ var ImageResizeMode = require('ImageResizeMode'); var LayoutPropTypes = require('LayoutPropTypes'); var ReactPropTypes = require('ReactPropTypes'); +var TransformPropTypes = require('TransformPropTypes'); var ImageStylePropTypes = { ...LayoutPropTypes, + ...TransformPropTypes, resizeMode: ReactPropTypes.oneOf(Object.keys(ImageResizeMode)), backgroundColor: ReactPropTypes.string, borderColor: ReactPropTypes.string, diff --git a/Libraries/StyleSheet/TransformPropTypes.js b/Libraries/StyleSheet/TransformPropTypes.js new file mode 100644 index 00000000000000..9338f7ff5f83bf --- /dev/null +++ b/Libraries/StyleSheet/TransformPropTypes.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule TransformPropTypes + * @flow + */ +'use strict'; + +var ReactPropTypes = require('ReactPropTypes'); + +var TransformPropTypes = { + transform: ReactPropTypes.arrayOf(ReactPropTypes.object), + transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), + + // DEPRECATED + rotation: ReactPropTypes.number, + scaleX: ReactPropTypes.number, + scaleY: ReactPropTypes.number, + translateX: ReactPropTypes.number, + translateY: ReactPropTypes.number, +}; + +module.exports = TransformPropTypes; From ee449301b66948bbdb983b9bbade382b8fb350c3 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 6 May 2015 14:17:17 -0700 Subject: [PATCH 218/250] [ReactNative] Fail faster in OSS tests --- Examples/SampleApp/SampleAppTests/SampleAppTests.m | 4 ++-- Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m | 5 +++-- Libraries/RCTTest/RCTTestRunner.m | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Examples/SampleApp/SampleAppTests/SampleAppTests.m b/Examples/SampleApp/SampleAppTests/SampleAppTests.m index 64794271376b04..d8dce811dac25d 100644 --- a/Examples/SampleApp/SampleAppTests/SampleAppTests.m +++ b/Examples/SampleApp/SampleAppTests/SampleAppTests.m @@ -44,8 +44,8 @@ - (void)testRendersWelcomeScreen { NSString *redboxError = nil; while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date]; + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; redboxError = [[RCTRedBox sharedInstance] currentErrorMessage]; diff --git a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m index 2c2359b4400339..df748ef0376ec2 100644 --- a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m +++ b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m @@ -69,8 +69,9 @@ - (void)testAAA_RootViewLoadsAndRenders NSString *redboxError = nil; while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date]; + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + redboxError = [[RCTRedBox sharedInstance] currentErrorMessage]; foundElement = [self findSubviewInView:vc.view matching:^(UIView *view) { if ([view respondsToSelector:@selector(attributedText)]) { diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index 0aa148fbc889bb..9c0cacf709c3e1 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -84,8 +84,8 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictiona NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage]; while ([date timeIntervalSinceNow] > 0 && ![testModule isDone] && error == nil) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date]; + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; error = [[RCTRedBox sharedInstance] currentErrorMessage]; } [rootView removeFromSuperview]; From d3c0029cef88970a144ce35913b78ebeacfb964f Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 6 May 2015 15:51:59 -0700 Subject: [PATCH 219/250] [ReactNative] Do not throw when Image.source is null --- Libraries/Image/Image.ios.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index a41352f44ce4b0..32965c213ac449 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -123,8 +123,7 @@ var Image = React.createClass({ 'not be set directly on Image.'); } } - var source = resolveAssetSource(this.props.source); - invariant(source, 'source must be initialized'); + var source = resolveAssetSource(this.props.source) || {}; var {width, height} = source; var style = flattenStyle([{width, height}, styles.base, this.props.style]); From 8d83b7ff631cc5fc691fe8d21f819ad70329b73b Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Wed, 6 May 2015 16:14:10 -0700 Subject: [PATCH 220/250] [ReactNative] RCTWebViewExecutor - Fix string containing script tag support --- React/Executors/RCTWebViewExecutor.m | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m index a180bae2cf3c7e..56323fb99be674 100644 --- a/React/Executors/RCTWebViewExecutor.m +++ b/React/Executors/RCTWebViewExecutor.m @@ -43,6 +43,7 @@ @implementation RCTWebViewExecutor UIWebView *_webView; NSMutableDictionary *_objectsToInject; NSRegularExpression *_commentsRegex; + NSRegularExpression *_scriptTagsRegex; } @synthesize valid = _valid; @@ -52,7 +53,8 @@ - (instancetype)initWithWebView:(UIWebView *)webView if ((self = [super init])) { _objectsToInject = [[NSMutableDictionary alloc] init]; _webView = webView ?: [[UIWebView alloc] init]; - _commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]+?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL]; + _commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL], + _scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL], _webView.delegate = self; } return self; @@ -139,11 +141,6 @@ - (void)executeApplicationScript:(NSString *)script onComplete(error); }; - script = [_commentsRegex stringByReplacingMatchesInString:script - options:0 - range:NSMakeRange(0, script.length) - withTemplate:@""]; - if (_objectsToInject.count > 0) { NSMutableString *scriptWithInjections = [[NSMutableString alloc] initWithString:@"/* BEGIN NATIVELY INJECTED OBJECTS */\n"]; [_objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *blockScript, BOOL *stop) { @@ -158,6 +155,15 @@ - (void)executeApplicationScript:(NSString *)script script = scriptWithInjections; } + script = [_commentsRegex stringByReplacingMatchesInString:script + options:0 + range:NSMakeRange(0, script.length) + withTemplate:@""]; + script = [_scriptTagsRegex stringByReplacingMatchesInString:script + options:0 + range:NSMakeRange(0, script.length) + withTemplate:@"\\\\<$1\\\\>"]; + NSString *runScript = [NSString stringWithFormat:@"", From 47164baaec01d4791d15db6ef5d4c5f94923e35e Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 6 May 2015 17:23:57 -0700 Subject: [PATCH 221/250] Fixed redbox error loop when camera access is disabled --- React/Base/RCTBridge.m | 16 ++++----- React/Modules/RCTExceptionsManager.m | 51 +++++++++++++--------------- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index a7c6a30a6914da..fd3ecaa249e9b0 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -1495,17 +1495,13 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i return; } - if (!RCT_DEBUG) { + @try { [method invokeWithBridge:strongSelf module:module arguments:params context:context]; - } else { - @try { - [method invokeWithBridge:strongSelf module:module arguments:params context:context]; - } - @catch (NSException *exception) { - RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception); - if ([exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) { - @throw; - } + } + @catch (NSException *exception) { + RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception); + if (!RCT_DEBUG && [exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) { + @throw exception; } } diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index f391e6af0dbf88..ddea1275acabd3 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -44,50 +44,45 @@ - (instancetype)init return; } -#if RCT_DEBUG // Red box is only available in debug mode - [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; -#else + if (!RCT_DEBUG) { - static NSUInteger reloadRetries = 0; - const NSUInteger maxMessageLength = 75; + static NSUInteger reloadRetries = 0; + const NSUInteger maxMessageLength = 75; - if (reloadRetries < _maxReloadAttempts) { + if (reloadRetries < _maxReloadAttempts) { - reloadRetries++; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification - object:nil]; + reloadRetries++; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification + object:nil]; - } else { + } else { - if (message.length > maxMessageLength) { - message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."]; - } + if (message.length > maxMessageLength) { + message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."]; + } - NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"]; - for (NSDictionary *frame in stack) { - [prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]]; - } + NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"]; + for (NSDictionary *frame in stack) { + [prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]]; + } - NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:message]; - [NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack]; + NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:message]; + [NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack]; + } } - -#endif - } RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message stack:(NSArray *)stack) { + if (_delegate) { + [_delegate unhandledJSExceptionWithMessage:message stack:stack]; + return; + } -#if RCT_DEBUG // Red box is only available in debug mode - - [[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack]; - -#endif - + [[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack]; } @end From 0b844feedb5168d9025ecfdfd39ed720dfd5d1f5 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Wed, 6 May 2015 17:02:03 -0700 Subject: [PATCH 222/250] [TouchableOpacity] Reset opacity to the inactiveValue rather than always 1.0 Summary: As per #941 - fixes bug with `TouchabeOpacity` always reseting child opacity to `1.0` after press. A note about the code: we could probably use a general `getNativeProp(propName, callback)` function rather than `getOpacity` but just used that as it was simpler for this specific PR, perhaps that refactor could be left to another - or maybe there is a way to do this already that I missed. Before: ![bug](https://cloud.githubusercontent.com/assets/90494/7287207/52d6a686-e907-11e4-8e16-04b2ddd0582c.gif) After: ![after](https://cloud.githubusercontent.com/assets/90494/7287689/5aca4776-e90c-11e4-8c40-aa6bd3e822d8.gif) Example code: ```javascript 'use strict'; var React = require('react-native'); var { AppRegistry, StyleSheet, Text, View, TouchableOpacity, } = React; var TestIt = React.createClass({ render() { return ( Closes https://github.com/facebook/react-native/pull/977 Github Author: Brent Vatne Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Components/Touchable/TouchableOpacity.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index e590999debd23f..8e9a34146d0c9a 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -20,6 +20,7 @@ var TouchableWithoutFeedback = require('TouchableWithoutFeedback'); var cloneWithProps = require('cloneWithProps'); var ensureComponentIsNative = require('ensureComponentIsNative'); +var flattenStyle = require('flattenStyle'); var keyOf = require('keyOf'); var onlyChild = require('onlyChild'); @@ -105,12 +106,12 @@ var TouchableOpacity = React.createClass({ }, touchableHandleActivePressOut: function() { - this.setOpacityTo(1.0); + var childStyle = flattenStyle(this.refs[CHILD_REF].props.style) || {}; + this.setOpacityTo(childStyle.opacity === undefined ? 1 : childStyle.opacity); this.props.onPressOut && this.props.onPressOut(); }, touchableHandlePress: function() { - this.setOpacityTo(1.0); this.props.onPress && this.props.onPress(); }, From f05494c96f7d4c43afd0023faad792aa1699bd04 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Wed, 6 May 2015 21:18:03 -0700 Subject: [PATCH 223/250] Fix scrollview doc type - canimprove -> can improve Summary: Simple one character change - add a missing space Closes https://github.com/facebook/react-native/pull/1112 Github Author: Brent Vatne Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Components/ScrollView/ScrollView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index f8dc3bcba15356..3b5122b24774aa 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -186,7 +186,7 @@ var ScrollView = React.createClass({ /** * Experimental: When true, offscreen child views (whose `overflow` value is * `hidden`) are removed from their native backing superview when offscreen. - * This canimprove scrolling performance on long lists. The default value is + * This can improve scrolling performance on long lists. The default value is * false. */ removeClippedSubviews: PropTypes.bool, From 205c22b9158df871cd3f7b216f014a6805f04677 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Thu, 7 May 2015 03:53:35 -0700 Subject: [PATCH 224/250] Fixed dev menu loop --- React/Base/RCTBridge.m | 41 +++++++++++++++++++++--------- React/Base/RCTDevMenu.m | 56 +++++++++++++++++++++++++++-------------- React/Base/RCTRedBox.m | 18 ++++++++++++- 3 files changed, 83 insertions(+), 32 deletions(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index fd3ecaa249e9b0..f442f1cf44435e 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -1144,18 +1144,11 @@ - (void)invalidate _latestJSExecutor = nil; } - /** - * Main Thread deallocations - */ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [_mainDisplayLink invalidate]; + void (^mainThreadInvalidate)(void) = ^{ - [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ - /** - * JS Thread deallocations - */ - [_javaScriptExecutor invalidate]; - [_jsDisplayLink invalidate]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_mainDisplayLink invalidate]; + _mainDisplayLink = nil; // Invalidate modules for (id target in _modulesByID.allObjects) { @@ -1165,11 +1158,35 @@ - (void)invalidate } // Release modules (breaks retain cycle if module has strong bridge reference) - _javaScriptExecutor = nil; _frameUpdateObservers = nil; _modulesByID = nil; _queuesByID = nil; _modulesByName = nil; + }; + + if (!_javaScriptExecutor) { + + // No JS thread running + mainThreadInvalidate(); + return; + } + + [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ + + /** + * JS Thread deallocations + */ + [_javaScriptExecutor invalidate]; + _javaScriptExecutor = nil; + + [_jsDisplayLink invalidate]; + _jsDisplayLink = nil; + + /** + * Main Thread deallocations + */ + mainThreadInvalidate(); + }]; } diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 3d88caf55b41ea..f7e688df560258 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -73,9 +73,6 @@ - (instancetype)init { if ((self = [super init])) { - _defaults = [NSUserDefaults standardUserDefaults]; - [self updateSettings]; - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self @@ -93,19 +90,27 @@ - (instancetype)init name:RCTJavaScriptDidLoadNotification object:nil]; -#if TARGET_IPHONE_SIMULATOR + _defaults = [NSUserDefaults standardUserDefaults]; + _settings = [[NSMutableDictionary alloc] init]; + // Delay setup until after Bridge init __weak RCTDevMenu *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf updateSettings]; + }); + +#if TARGET_IPHONE_SIMULATOR + RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; - // toggle debug menu + // Toggle debug menu [commands registerKeyCommandWithInput:@"d" modifierFlags:UIKeyModifierCommand action:^(UIKeyCommand *command) { [weakSelf toggle]; }]; - // reload in normal mode + // Reload in normal mode [commands registerKeyCommandWithInput:@"n" modifierFlags:UIKeyModifierCommand action:^(UIKeyCommand *command) { @@ -117,22 +122,23 @@ - (instancetype)init return self; } -- (void)updateSettings +- (dispatch_queue_t)methodQueue { - __weak RCTDevMenu *weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - RCTDevMenu *strongSelf = weakSelf; - if (!strongSelf) { - return; - } + return dispatch_get_main_queue(); +} - strongSelf->_settings = [NSMutableDictionary dictionaryWithDictionary:[strongSelf->_defaults objectForKey:RCTDevMenuSettingsKey]]; +- (void)updateSettings +{ + NSDictionary *settings = [_defaults objectForKey:RCTDevMenuSettingsKey]; + if ([settings isEqualToDictionary:_settings]) { + return; + } - strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue]; - strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue]; - strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue]; - strongSelf.executorClass = NSClassFromString(strongSelf->_settings[@"executorClass"]); - }); + [_settings setDictionary:settings]; + self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue]; + self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue]; + self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue]; + self.executorClass = NSClassFromString(_settings[@"executorClass"]); } - (void)jsLoaded @@ -161,6 +167,11 @@ - (void)jsLoaded }); } +- (BOOL)isValid +{ + return NO; +} + - (void)dealloc { [_updateTask cancel]; @@ -170,6 +181,10 @@ - (void)dealloc - (void)updateSetting:(NSString *)name value:(id)value { + id currentValue = _settings[name]; + if (currentValue == value || [currentValue isEqual:value]) { + return; + } if (value) { _settings[name] = value; } else { @@ -239,6 +254,9 @@ - (void)toggle - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { _actionSheet = nil; + if (buttonIndex == actionSheet.cancelButtonIndex) { + return; + } switch (buttonIndex) { case 0: { diff --git a/React/Base/RCTRedBox.m b/React/Base/RCTRedBox.m index 0de61d1721c487..9c5b6d3dd3bf0e 100644 --- a/React/Base/RCTRedBox.m +++ b/React/Base/RCTRedBox.m @@ -76,10 +76,27 @@ - (id)initWithFrame:(CGRect)frame reloadButton.frame = CGRectMake(buttonWidth, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight); [_rootView addSubview:dismissButton]; [_rootView addSubview:reloadButton]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self + selector:@selector(dismiss) + name:RCTReloadNotification + object:nil]; + + [notificationCenter addObserver:self + selector:@selector(dismiss) + name:RCTJavaScriptDidLoadNotification + object:nil]; } return self; } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + - (void)openStackFrameInEditor:(NSDictionary *)stackFrame { NSData *stackFrameJSON = [RCTJSONStringify(stackFrame, nil) dataUsingEncoding:NSUTF8StringEncoding]; @@ -125,7 +142,6 @@ - (void)dismiss - (void)reload { [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil userInfo:nil]; - [self dismiss]; } #pragma mark - TableView From b8bf4d0957a51964773dc3a06b8c0a68b71d26b8 Mon Sep 17 00:00:00 2001 From: James Ide Date: Thu, 7 May 2015 04:29:31 -0700 Subject: [PATCH 225/250] [Loader] Post a notification when the JS fails to load Summary: This provides a way to get notified when a bridge fails to load JS, allowing apps to handle the error. Closes https://github.com/facebook/react-native/pull/1085 Github Author: James Ide Test Plan: run the UIExplorer app with the packager server not running, and verify that the notification is posted. --- React/Base/RCTBridge.h | 5 +++++ React/Base/RCTBridge.m | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 77cbb215ec6c24..7c8af00cce73a3 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -28,6 +28,11 @@ extern NSString *const RCTReloadNotification; */ extern NSString *const RCTJavaScriptDidLoadNotification; +/** + * This notification fires when the bridge failed to load. + */ +extern NSString *const RCTJavaScriptDidFailToLoadNotification; + /** * This block can be used to instantiate modules that require additional * init parameters, or additional configuration prior to being used. diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index f442f1cf44435e..28a218b074bf33 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -31,6 +31,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification"; NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification"; +NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification"; dispatch_queue_t const RCTJSThread = nil; @@ -1082,16 +1083,18 @@ - (void)initJS RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self]; [loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) { + _loading = NO; if (!self.isValid) { return; } + RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; sourceCodeModule.scriptURL = bundleURL; sourceCodeModule.scriptText = script; - if (error != nil) { + if (error) { - NSArray *stack = [[error userInfo] objectForKey:@"stack"]; + NSArray *stack = [error userInfo][@"stack"]; if (stack) { [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription] withStack:stack]; @@ -1100,10 +1103,17 @@ - (void)initJS withDetails:[error localizedFailureReason]]; } + NSDictionary *userInfo = @{@"error": error}; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification + object:self + userInfo:userInfo]; + } else { + [self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) { if (!loadError) { + /** * Register the display link to start sending js calls after everything * is setup From b97ce93ceae0dc4adc16706fbf054f14dcf208d0 Mon Sep 17 00:00:00 2001 From: James Ide Date: Thu, 7 May 2015 07:16:59 -0700 Subject: [PATCH 226/250] [Nav] Add support for bar button icons and left buttons Summary: NavigatorIOS supports four new properties: - **rightButtonImageSource:** The source of an image to display in the top right. This must be a static image since UINavigationController only supports UIImages. Adding support for UIImageViews (or arbitrary views) is more complicated because custom views do not fade on touch and do not have hit slop the same way that UIImage buttons do. Usage: `rightButtonImageSource: ix('ImageName')` - **backButtonImageSource:** Use a custom image for the back button. This does not replace the back caret (`<`) but instead replaces the text next to it. - **leftButtonTitle**: Text for the left nav button, which supersedes the previous nav item's back button when specified. The main use case for this is your initial screen/UIVC which has nothing to go back to (since it is the first VC on the stack) but need to display a left button. This does hide the back button if there would have been one otherwise. - **leftButtonImageSource:** Image source for the left button, super Closes https://github.com/facebook/react-native/pull/263 Github Author: James Ide Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Examples/UIExplorer/ImageMocks.js | 5 + Examples/UIExplorer/NavigatorIOSExample.js | 25 +++++ .../NavBarButtonPlus.imageset/Contents.json | 21 ++++ .../NavBarButtonPlus@3x.png | Bin 0 -> 166 bytes .../Components/Navigation/NavigatorIOS.ios.js | 49 ++++++++- React/Modules/RCTUIManager.m | 6 ++ React/Views/RCTNavItem.h | 14 ++- React/Views/RCTNavItem.m | 101 +++++++++++++++++- React/Views/RCTNavItemManager.m | 20 ++-- React/Views/RCTWrapperViewController.m | 39 +++---- 10 files changed, 247 insertions(+), 33 deletions(-) create mode 100644 Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json create mode 100644 Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png diff --git a/Examples/UIExplorer/ImageMocks.js b/Examples/UIExplorer/ImageMocks.js index b888acbf74ca41..3f1883fa65c5bb 100644 --- a/Examples/UIExplorer/ImageMocks.js +++ b/Examples/UIExplorer/ImageMocks.js @@ -39,3 +39,8 @@ declare module 'image!uie_thumb_selected' { declare var uri: string; declare var isStatic: boolean; } + +declare module 'image!NavBarButtonPlus' { + declare var uri: string; + declare var isStatic: boolean; +} diff --git a/Examples/UIExplorer/NavigatorIOSExample.js b/Examples/UIExplorer/NavigatorIOSExample.js index eee731bd53834a..4a2011a654611c 100644 --- a/Examples/UIExplorer/NavigatorIOSExample.js +++ b/Examples/UIExplorer/NavigatorIOSExample.js @@ -19,6 +19,7 @@ var React = require('react-native'); var ViewExample = require('./ViewExample'); var createExamplePage = require('./createExamplePage'); var { + AlertIOS, PixelRatio, ScrollView, StyleSheet, @@ -92,6 +93,30 @@ var NavigatorIOSExample = React.createClass({ } }); })} + {this._renderRow('Custom Left & Right Icons', () => { + this.props.navigator.push({ + title: NavigatorIOSExample.title, + component: EmptyPage, + leftButtonTitle: 'Custom Left', + onLeftButtonPress: () => this.props.navigator.pop(), + rightButtonIcon: require('image!NavBarButtonPlus'), + onRightButtonPress: () => { + AlertIOS.alert( + 'Bar Button Action', + 'Recognized a tap on the bar button icon', + [ + { + text: 'OK', + onPress: () => console.log('Tapped OK'), + }, + ] + ); + }, + passProps: { + text: 'This page has an icon for the right button in the nav bar', + } + }); + })} {this._renderRow('Pop', () => { this.props.navigator.pop(); })} diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json b/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json new file mode 100644 index 00000000000000..13726b4e948939 --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "NavBarButtonPlus@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png b/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..706a9c9052240e9c88d4aef09f70c8ad77f1edf3 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^#vshW1|+Q(8fXD2!4lVqlHmNblJdl&REC1Q^yH$_ z;tYpU&(A=~08bakkc@k8uWaOGaNuBZ{GER*|A0#+bE25uG|!$Jy4`bm+0Z~*(T7(F k2??_#)=Q$vusw6;@BjN)n|rEo2+(W>Pgg&ebxsLQ0Dm|t*8l(j literal 0 HcmV?d00001 diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index 3babd140954201..103e749f78b237 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -12,6 +12,7 @@ 'use strict'; var EventEmitter = require('EventEmitter'); +var Image = require('Image'); var React = require('React'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var RCTNavigatorManager = require('NativeModules').NavigatorManager; @@ -47,11 +48,16 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({ // NavigatorIOS does not use them all, because some are problematic title: true, barTintColor: true, + leftButtonIcon: true, + leftButtonTitle: true, + onNavLeftButtonTap: true, + rightButtonIcon: true, rightButtonTitle: true, onNavRightButtonTap: true, + backButtonIcon: true, + backButtonTitle: true, tintColor: true, navigationBarHidden: true, - backButtonTitle: true, titleTextColor: true, style: true, }, @@ -79,7 +85,12 @@ type Route = { title: string; passProps: Object; backButtonTitle: string; + backButtonIcon: Object; + leftButtonTitle: string; + leftButtonIcon: Object; + onLeftButtonPress: Function; rightButtonTitle: string; + rightButtonIcon: Object; onRightButtonPress: Function; wrapperStyle: any; }; @@ -212,6 +223,13 @@ var NavigatorIOS = React.createClass({ */ passProps: PropTypes.object, + /** + * If set, the left header button image will appear with this source. Note + * that this doesn't apply for the header of the current view, but the + * ones of the views that are pushed afterward. + */ + backButtonIcon: Image.propTypes.source, + /** * If set, the left header button will appear with this name. Note that * this doesn't apply for the header of the current view, but the ones @@ -219,6 +237,26 @@ var NavigatorIOS = React.createClass({ */ backButtonTitle: PropTypes.string, + /** + * If set, the left header button image will appear with this source + */ + leftButtonIcon: Image.propTypes.source, + + /** + * If set, the left header button will appear with this name + */ + leftButtonTitle: PropTypes.string, + + /** + * Called when the left header button is pressed + */ + onLeftButtonPress: PropTypes.func, + + /** + * If set, the right header button image will appear with this source + */ + rightButtonIcon: Image.propTypes.source, + /** * If set, the right header button will appear with this name */ @@ -560,7 +598,12 @@ var NavigatorIOS = React.createClass({ this.props.itemWrapperStyle, route.wrapperStyle ]} + backButtonIcon={this._imageNameFromSource(route.backButtonIcon)} backButtonTitle={route.backButtonTitle} + leftButtonIcon={this._imageNameFromSource(route.leftButtonIcon)} + leftButtonTitle={route.leftButtonTitle} + onNavLeftButtonTap={route.onLeftButtonPress} + rightButtonIcon={this._imageNameFromSource(route.rightButtonIcon)} rightButtonTitle={route.rightButtonTitle} onNavRightButtonTap={route.onRightButtonPress} navigationBarHidden={this.props.navigationBarHidden} @@ -577,6 +620,10 @@ var NavigatorIOS = React.createClass({ ); }, + _imageNameFromSource: function(source: ?Object) { + return source ? source.uri : undefined; + }, + renderNavigationStackItems: function() { var shouldRecurseToNavigator = this.state.makingNavigatorRequest || diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index d35cae03a1e4e9..eac3a77359fd96 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1159,6 +1159,12 @@ - (NSDictionary *)customBubblingEventTypes @"captured": @"onNavigationCompleteCapture" } }, + @"topNavLeftButtonTap": @{ + @"phasedRegistrationNames": @{ + @"bubbled": @"onNavLeftButtonTap", + @"captured": @"onNavLefttButtonTapCapture" + } + }, @"topNavRightButtonTap": @{ @"phasedRegistrationNames": @{ @"bubbled": @"onNavRightButtonTap", diff --git a/React/Views/RCTNavItem.h b/React/Views/RCTNavItem.h index 5ae874522e8a5d..cd9833a4477183 100644 --- a/React/Views/RCTNavItem.h +++ b/React/Views/RCTNavItem.h @@ -12,11 +12,19 @@ @interface RCTNavItem : UIView @property (nonatomic, copy) NSString *title; +@property (nonatomic, strong) UIImage *leftButtonIcon; +@property (nonatomic, copy) NSString *leftButtonTitle; +@property (nonatomic, strong) UIImage *rightButtonIcon; @property (nonatomic, copy) NSString *rightButtonTitle; +@property (nonatomic, strong) UIImage *backButtonIcon; @property (nonatomic, copy) NSString *backButtonTitle; @property (nonatomic, assign) BOOL navigationBarHidden; -@property (nonatomic, copy) UIColor *tintColor; -@property (nonatomic, copy) UIColor *barTintColor; -@property (nonatomic, copy) UIColor *titleTextColor; +@property (nonatomic, strong) UIColor *tintColor; +@property (nonatomic, strong) UIColor *barTintColor; +@property (nonatomic, strong) UIColor *titleTextColor; + +@property (nonatomic, readonly) UIBarButtonItem *backButtonItem; +@property (nonatomic, readonly) UIBarButtonItem *leftButtonItem; +@property (nonatomic, readonly) UIBarButtonItem *rightButtonItem; @end diff --git a/React/Views/RCTNavItem.m b/React/Views/RCTNavItem.m index 6b1e92f44a3493..56346a363b10ac 100644 --- a/React/Views/RCTNavItem.m +++ b/React/Views/RCTNavItem.m @@ -11,5 +11,104 @@ @implementation RCTNavItem -@end +@synthesize backButtonItem = _backButtonItem; +@synthesize leftButtonItem = _leftButtonItem; +@synthesize rightButtonItem = _rightButtonItem; + +- (void)setBackButtonTitle:(NSString *)backButtonTitle +{ + _backButtonTitle = backButtonTitle; + _backButtonItem = nil; +} + +- (void)setBackButtonIcon:(UIImage *)backButtonIcon +{ + _backButtonIcon = backButtonIcon; + _backButtonItem = nil; +} + +- (UIBarButtonItem *)backButtonItem +{ + if (!_backButtonItem) { + if (_backButtonIcon) { + _backButtonItem = [[UIBarButtonItem alloc] initWithImage:_backButtonIcon + style:UIBarButtonItemStylePlain + target:nil + action:nil]; + } else if (_backButtonTitle.length) { + _backButtonItem = [[UIBarButtonItem alloc] initWithTitle:_backButtonTitle + style:UIBarButtonItemStylePlain + target:nil + action:nil]; + } else { + _backButtonItem = nil; + } + } + return _backButtonItem; +} + +- (void)setLeftButtonTitle:(NSString *)leftButtonTitle +{ + _leftButtonTitle = leftButtonTitle; + _leftButtonItem = nil; +} + +- (void)setLeftButtonIcon:(UIImage *)leftButtonIcon +{ + _leftButtonIcon = leftButtonIcon; + _leftButtonIcon = nil; +} +- (UIBarButtonItem *)leftButtonItem +{ + if (!_leftButtonItem) { + if (_leftButtonIcon) { + _leftButtonItem = [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon + style:UIBarButtonItemStylePlain + target:nil + action:nil]; + } else if (_leftButtonTitle.length) { + _leftButtonItem = [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle + style:UIBarButtonItemStylePlain + target:nil + action:nil]; + } else { + _leftButtonItem = nil; + } + } + return _leftButtonItem; +} + +- (void)setRightButtonTitle:(NSString *)rightButtonTitle +{ + _rightButtonTitle = rightButtonTitle; + _rightButtonItem = nil; +} + +- (void)setRightButtonIcon:(UIImage *)rightButtonIcon +{ + _rightButtonIcon = rightButtonIcon; + _rightButtonItem = nil; +} + +- (UIBarButtonItem *)rightButtonItem +{ + if (!_rightButtonItem) { + if (_rightButtonIcon) { + _rightButtonItem = [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon + style:UIBarButtonItemStylePlain + target:nil + action:nil]; + } else if (_rightButtonTitle.length) { + _rightButtonItem = [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle + style:UIBarButtonItemStylePlain + target:nil + action:nil]; + } else { + _rightButtonItem = nil; + } + } + return _rightButtonItem; +} + +@end diff --git a/React/Views/RCTNavItemManager.m b/React/Views/RCTNavItemManager.m index fc601632f4c4e4..33588c938acdb7 100644 --- a/React/Views/RCTNavItemManager.m +++ b/React/Views/RCTNavItemManager.m @@ -21,12 +21,20 @@ - (UIView *)view return [[RCTNavItem alloc] init]; } +RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL) +RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor) + RCT_EXPORT_VIEW_PROPERTY(title, NSString) -RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString); -RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString); -RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL); -RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor); -RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor); -RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor); +RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor) + +RCT_EXPORT_VIEW_PROPERTY(backButtonIcon, UIImage) +RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString) + +RCT_EXPORT_VIEW_PROPERTY(leftButtonTitle, NSString) +RCT_EXPORT_VIEW_PROPERTY(leftButtonIcon, UIImage) + +RCT_EXPORT_VIEW_PROPERTY(rightButtonIcon, UIImage) +RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString) @end diff --git a/React/Views/RCTWrapperViewController.m b/React/Views/RCTWrapperViewController.m index 53c2f16a75c73c..400ce5fab6a6f2 100644 --- a/React/Views/RCTWrapperViewController.m +++ b/React/Views/RCTWrapperViewController.m @@ -64,7 +64,6 @@ - (void)viewWillAppear:(BOOL)animated // TODO: find a way to make this less-tightly coupled to navigation controller if ([self.parentViewController isKindOfClass:[UINavigationController class]]) { - [self.navigationController setNavigationBarHidden:_navItem.navigationBarHidden animated:animated]; @@ -73,33 +72,23 @@ - (void)viewWillAppear:(BOOL)animated return; } - self.navigationItem.title = _navItem.title; - UINavigationBar *bar = self.navigationController.navigationBar; - if (_navItem.barTintColor) { - bar.barTintColor = _navItem.barTintColor; - } - if (_navItem.tintColor) { - bar.tintColor = _navItem.tintColor; - } + bar.barTintColor = _navItem.barTintColor; + bar.tintColor = _navItem.tintColor; if (_navItem.titleTextColor) { [bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}]; } - if (_navItem.rightButtonTitle.length > 0) { - self.navigationItem.rightBarButtonItem = - [[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle - style:UIBarButtonItemStyleDone - target:self - action:@selector(handleNavRightButtonTapped)]; + UINavigationItem *item = self.navigationItem; + item.title = _navItem.title; + item.backBarButtonItem = _navItem.backButtonItem; + if ((item.leftBarButtonItem = _navItem.leftButtonItem)) { + item.leftBarButtonItem.target = self; + item.leftBarButtonItem.action = @selector(handleNavLeftButtonTapped); } - - if (_navItem.backButtonTitle.length > 0) { - self.navigationItem.backBarButtonItem = - [[UIBarButtonItem alloc] initWithTitle:_navItem.backButtonTitle - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + if ((item.rightBarButtonItem = _navItem.rightButtonItem)) { + item.rightBarButtonItem.target = self; + item.rightBarButtonItem.action = @selector(handleNavRightButtonTapped); } } } @@ -114,6 +103,12 @@ - (void)loadView self.view = _wrapperView; } +- (void)handleNavLeftButtonTapped +{ + [_eventDispatcher sendInputEventWithName:@"topNavLeftButtonTap" + body:@{@"target":_navItem.reactTag}]; +} + - (void)handleNavRightButtonTapped { [_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap" From 6b38fad219588b2a2d12bacbead41bc418ab3289 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 7 May 2015 09:10:47 -0700 Subject: [PATCH 227/250] [ReactNative] Fix ref to eventDispatcher on RCTBridge --- React/Base/RCTBridge.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 28a218b074bf33..6e839ed0504a91 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -868,6 +868,11 @@ - (NSDictionary *)modules return _batchedBridge.modules; } +- (RCTEventDispatcher *)eventDispatcher +{ + return _eventDispatcher ?: _batchedBridge.eventDispatcher; +} + #define RCT_BRIDGE_WARN(...) \ - (void)__VA_ARGS__ \ { \ From f251b0ddb84834ad9f4e82c310d30bda1cb0eb8e Mon Sep 17 00:00:00 2001 From: Siddharth Agarwal Date: Thu, 7 May 2015 10:19:32 -0700 Subject: [PATCH 228/250] [react] fix line endings From f878be84e609631895278a951af2a29ac2a02bd6 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Wed, 6 May 2015 11:05:47 -0700 Subject: [PATCH 229/250] Clarify transform for ViewStyleProps, generate union docs properly --- Libraries/Components/View/ViewStylePropTypes.js | 17 +++++++++++++++++ website/layout/AutodocsLayout.js | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index c1f6b4b1ccf7d5..c69ff91e71ffcf 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -36,6 +36,23 @@ var ViewStylePropTypes = { ), shadowOpacity: ReactPropTypes.number, shadowRadius: ReactPropTypes.number, + transform: ReactPropTypes.arrayOf( + ReactPropTypes.oneOfType([ + ReactPropTypes.shape({rotation: ReactPropTypes.number}), + ReactPropTypes.shape({scaleX: ReactPropTypes.number}), + ReactPropTypes.shape({scaleY: ReactPropTypes.number}), + ReactPropTypes.shape({translateX: ReactPropTypes.number}), + ReactPropTypes.shape({translateY: ReactPropTypes.number}) + ]) + ), + transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), + + // DEPRECATED + rotation: ReactPropTypes.number, + scaleX: ReactPropTypes.number, + scaleY: ReactPropTypes.number, + translateX: ReactPropTypes.number, + translateY: ReactPropTypes.number, }; module.exports = ViewStylePropTypes; diff --git a/website/layout/AutodocsLayout.js b/website/layout/AutodocsLayout.js index 23b2238144e434..d9e38e488c464e 100644 --- a/website/layout/AutodocsLayout.js +++ b/website/layout/AutodocsLayout.js @@ -20,6 +20,7 @@ var slugify = require('slugify'); var styleReferencePattern = /^[^.]+\.propTypes\.style$/; + var ComponentDoc = React.createClass({ renderType: function(type) { if (type.name === 'enum') { @@ -33,6 +34,10 @@ var ComponentDoc = React.createClass({ return '{' + Object.keys(type.value).map((key => key + ': ' + this.renderType(type.value[key]))).join(', ') + '}'; } + if (type.name == 'union') { + return type.value.map(this.renderType).join(', '); + } + if (type.name === 'arrayOf') { return '[' + this.renderType(type.value) + ']'; } From 42d4de9ce02afe300b1e14edf124a278c6a9a154 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Wed, 6 May 2015 12:30:21 -0700 Subject: [PATCH 230/250] Remove unnecessary newline --- website/layout/AutodocsLayout.js | 1 - 1 file changed, 1 deletion(-) diff --git a/website/layout/AutodocsLayout.js b/website/layout/AutodocsLayout.js index d9e38e488c464e..13aee8e07e6958 100644 --- a/website/layout/AutodocsLayout.js +++ b/website/layout/AutodocsLayout.js @@ -20,7 +20,6 @@ var slugify = require('slugify'); var styleReferencePattern = /^[^.]+\.propTypes\.style$/; - var ComponentDoc = React.createClass({ renderType: function(type) { if (type.name === 'enum') { From 0bccc33dff81aed245a9fae349d36a009b22c51e Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Wed, 6 May 2015 18:01:31 -0700 Subject: [PATCH 231/250] Change rotation to rotate --- Libraries/Components/View/ViewStylePropTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index c69ff91e71ffcf..80f1a5a3a0a274 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -38,7 +38,7 @@ var ViewStylePropTypes = { shadowRadius: ReactPropTypes.number, transform: ReactPropTypes.arrayOf( ReactPropTypes.oneOfType([ - ReactPropTypes.shape({rotation: ReactPropTypes.number}), + ReactPropTypes.shape({rotate: ReactPropTypes.string}), ReactPropTypes.shape({scaleX: ReactPropTypes.number}), ReactPropTypes.shape({scaleY: ReactPropTypes.number}), ReactPropTypes.shape({translateX: ReactPropTypes.number}), From 1c90a2dbcc476b4dc170f315891f5e441d9057d7 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 7 May 2015 12:14:15 -0700 Subject: [PATCH 232/250] Move updated propTypes into new TransformPropTypes --- Libraries/Components/View/ViewStylePropTypes.js | 17 ----------------- Libraries/StyleSheet/TransformPropTypes.js | 10 +++++++++- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index 80f1a5a3a0a274..c1f6b4b1ccf7d5 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -36,23 +36,6 @@ var ViewStylePropTypes = { ), shadowOpacity: ReactPropTypes.number, shadowRadius: ReactPropTypes.number, - transform: ReactPropTypes.arrayOf( - ReactPropTypes.oneOfType([ - ReactPropTypes.shape({rotate: ReactPropTypes.string}), - ReactPropTypes.shape({scaleX: ReactPropTypes.number}), - ReactPropTypes.shape({scaleY: ReactPropTypes.number}), - ReactPropTypes.shape({translateX: ReactPropTypes.number}), - ReactPropTypes.shape({translateY: ReactPropTypes.number}) - ]) - ), - transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), - - // DEPRECATED - rotation: ReactPropTypes.number, - scaleX: ReactPropTypes.number, - scaleY: ReactPropTypes.number, - translateX: ReactPropTypes.number, - translateY: ReactPropTypes.number, }; module.exports = ViewStylePropTypes; diff --git a/Libraries/StyleSheet/TransformPropTypes.js b/Libraries/StyleSheet/TransformPropTypes.js index 9338f7ff5f83bf..c9f403f75fd515 100644 --- a/Libraries/StyleSheet/TransformPropTypes.js +++ b/Libraries/StyleSheet/TransformPropTypes.js @@ -14,7 +14,15 @@ var ReactPropTypes = require('ReactPropTypes'); var TransformPropTypes = { - transform: ReactPropTypes.arrayOf(ReactPropTypes.object), + transform: ReactPropTypes.arrayOf( + ReactPropTypes.oneOfType([ + ReactPropTypes.shape({rotate: ReactPropTypes.string}), + ReactPropTypes.shape({scaleX: ReactPropTypes.number}), + ReactPropTypes.shape({scaleY: ReactPropTypes.number}), + ReactPropTypes.shape({translateX: ReactPropTypes.number}), + ReactPropTypes.shape({translateY: ReactPropTypes.number}) + ]) + ), transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number), // DEPRECATED From b8ca4e450b2fdff5eed68dbb3e1b4ca00b7d4a9c Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 7 May 2015 12:16:48 -0700 Subject: [PATCH 233/250] Add TransformPropTypes to docs --- website/layout/AutodocsLayout.js | 14 +++++++++----- website/server/extractDocs.js | 16 ++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/website/layout/AutodocsLayout.js b/website/layout/AutodocsLayout.js index 13aee8e07e6958..ed583372e46c26 100644 --- a/website/layout/AutodocsLayout.js +++ b/website/layout/AutodocsLayout.js @@ -100,14 +100,18 @@ var ComponentDoc = React.createClass({
{(style.composes || []).map((name) => { var link; - if (name !== 'LayoutPropTypes') { - name = name.replace('StylePropTypes', ''); - link = - {name}#style...; - } else { + if (name === 'LayoutPropTypes') { name = 'Flexbox'; link = {name}...; + } else if (name === 'TransformPropTypes') { + name = 'Transforms'; + link = + {name}...; + } else { + name = name.replace('StylePropTypes', ''); + link = + {name}#style...; } return (
diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js index fec125cadc1baf..ccb15f3296d10d 100644 --- a/website/server/extractDocs.js +++ b/website/server/extractDocs.js @@ -19,9 +19,12 @@ function getNameFromPath(filepath) { while (ext = path.extname(filepath)) { filepath = path.basename(filepath, ext); } + if (filepath === 'LayoutPropTypes') { return 'Flexbox'; - } else if (filepath == 'TabBarItemIOS') { + } else if (filepath === 'TransformPropTypes') { + return 'Transforms'; + } else if (filepath === 'TabBarItemIOS') { return 'TabBarIOS.Item'; } return filepath; @@ -148,6 +151,7 @@ var apis = [ var styles = [ '../Libraries/StyleSheet/LayoutPropTypes.js', + '../Libraries/StyleSheet/TransformPropTypes.js', '../Libraries/Components/View/ViewStylePropTypes.js', '../Libraries/Text/TextStylePropTypes.js', '../Libraries/Image/ImageStylePropTypes.js', @@ -159,10 +163,10 @@ var polyfills = [ var all = components .concat(apis) - .concat(styles.slice(0, 1)) + .concat(styles.slice(0, 2)) .concat(polyfills); -var styleDocs = styles.slice(1).reduce(function(docs, filepath) { +var styleDocs = styles.slice(2).reduce(function(docs, filepath) { docs[path.basename(filepath).replace(path.extname(filepath), '')] = docgen.parse( fs.readFileSync(filepath), @@ -171,9 +175,9 @@ var styleDocs = styles.slice(1).reduce(function(docs, filepath) { ); // Remove deprecated style props - if (docs['ViewStylePropTypes']) { + if (docs['TransformPropTypes']) { ['rotation', 'scaleX', 'scaleY', 'translateX', 'translateY'].forEach(function(key) { - delete docs['ViewStylePropTypes']['props'][key]; + delete docs['TransformPropTypes']['props'][key]; }); } @@ -185,7 +189,7 @@ module.exports = function() { return [].concat( components.map(renderComponent), apis.map(renderAPI('api')), - styles.slice(0, 1).map(renderStyle), + styles.slice(0, 2).map(renderStyle), polyfills.map(renderAPI('Polyfill')) ); }; From 731d4a061085558a302c3e76304140973e7b57b4 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 7 May 2015 12:50:41 -0700 Subject: [PATCH 234/250] Remove the deprecated transform propTypes --- website/server/extractDocs.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js index ccb15f3296d10d..57cb5b17a1f31a 100644 --- a/website/server/extractDocs.js +++ b/website/server/extractDocs.js @@ -83,6 +83,7 @@ function renderComponent(filepath) { docgenHelpers.findExportedOrFirst, docgen.defaultHandlers.concat(docgenHelpers.stylePropTypeHandler) ); + return componentsToMarkdown('component', json, filepath, n++, styleDocs); } @@ -105,6 +106,17 @@ function renderStyle(filepath) { docgenHelpers.findExportedObject, [docgen.handlers.propTypeHandler] ); + + // Remove deprecated style props + if (filepath === "../Libraries/StyleSheet/TransformPropTypes.js") { + ['rotation', 'scaleX', 'scaleY', 'translateX', 'translateY'].forEach(function(key) { + delete json['props'][key]; + }); + } + + + // console.log(json); + return componentsToMarkdown('style', json, filepath, n++); } @@ -174,13 +186,6 @@ var styleDocs = styles.slice(2).reduce(function(docs, filepath) { [docgen.handlers.propTypeHandler] ); - // Remove deprecated style props - if (docs['TransformPropTypes']) { - ['rotation', 'scaleX', 'scaleY', 'translateX', 'translateY'].forEach(function(key) { - delete docs['TransformPropTypes']['props'][key]; - }); - } - return docs; }, {}); From 1ef4e00fba948c9684ef208b278df11dd85620de Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Thu, 7 May 2015 12:11:02 -0700 Subject: [PATCH 235/250] [ReactNative] Introduce onLayout events Summary: Simply add an `onLayout` callback to a native view component, and the callback will be invoked with the current layout information when the view is mounted and whenever the layout changes. The only limitation is that scroll position and other stuff the layout system isn't aware of is not taken into account. This is because onLayout events wouldn't be triggered for these changes and if they are desired they should be tracked separately (e.g. with `onScroll`) and combined. Also fixes some bugs with LayoutAnimation callbacks. @public Test Plan: - Run new LayoutEventsExample in UIExplorer and see it work correctly. - New integration test passes internally (IntegrationTest project seems busted). - New jest test case passes. {F22318433} ``` 2015-05-06 15:45:05.848 [info][tid:com.facebook.React.JavaScript] "Running application "UIExplorerApp" with appParams: {"rootTag":1,"initialProps":{}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF" 2015-05-06 15:45:05.881 [info][tid:com.facebook.React.JavaScript] "received text layout event ", {"target":27,"layout":{"y":123,"x":12.5,"width":140.5,"height":18}} 2015-05-06 15:45:05.882 [info][tid:com.facebook.React.JavaScript] "received image layout event ", {"target":23,"layout":{"y":12.5,"x":122,"width":50,"height":50}} 2015-05-06 15:45:05.883 [info][tid:com.facebook.React.JavaScript] "received view layout event ", {"target":22,"layout":{"y":70.5,"x":20,"width":294,"height":204}} 2015-05-06 15:45:05.897 [info][tid:com.facebook.React.JavaScript] "received text layout event ", {"target":27,"layout":{"y":206.5,"x":12.5,"width":140.5,"height":18}} 2015-05-06 15:45:05.897 [info][tid:com.facebook.React.JavaScript] "received view layout event ", {"target":22,"layout":{"y":70.5,"x":20,"width":294,"height":287.5}} 2015-05-06 15:45:09.847 [info][tid:com.facebook.React.JavaScript] "layout animation done." 2015-05-06 15:45:09.847 [info][tid:com.facebook.React.JavaScript] "received image layout event ", {"target":23,"layout":{"y":12.5,"x":82,"width":50,"height":50}} 2015-05-06 15:45:09.848 [info][tid:com.facebook.React.JavaScript] "received view layout event ", {"target":22,"layout":{"y":110.5,"x":60,"width":214,"height":287.5}} 2015-05-06 15:45:09.862 [info][tid:com.facebook.React.JavaScript] "received text layout event ", {"target":27,"layout":{"y":206.5,"x":12.5,"width":120,"height":68}} 2015-05-06 15:45:09.863 [info][tid:com.facebook.React.JavaScript] "received image layout event ", {"target":23,"layout":{"y":12.5,"x":55,"width":50,"height":50}} 2015-05-06 15:45:09.863 [info][tid:com.facebook.React.JavaScript] "received view layout event ", {"target":22,"layout":{"y":128,"x":60,"width":160,"height":337.5}} ``` --- Examples/UIExplorer/LayoutEventsExample.js | 150 ++++++++++++++++ Examples/UIExplorer/UIExplorerList.js | 1 + IntegrationTests/IntegrationTestsApp.js | 1 + .../IntegrationTestsTests.m | 5 + IntegrationTests/LayoutEventsTest.js | 167 ++++++++++++++++++ Libraries/Components/View/View.js | 5 + Libraries/ReactIOS/ReactIOSViewAttributes.js | 6 +- Libraries/ReactIOS/diffRawProperties.js | 20 +++ React/Modules/RCTUIManager.m | 42 ++++- React/Views/RCTShadowView.h | 1 + React/Views/RCTViewManager.m | 2 + 11 files changed, 389 insertions(+), 11 deletions(-) create mode 100644 Examples/UIExplorer/LayoutEventsExample.js create mode 100644 IntegrationTests/LayoutEventsTest.js diff --git a/Examples/UIExplorer/LayoutEventsExample.js b/Examples/UIExplorer/LayoutEventsExample.js new file mode 100644 index 00000000000000..6aec6257e77629 --- /dev/null +++ b/Examples/UIExplorer/LayoutEventsExample.js @@ -0,0 +1,150 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + Image, + LayoutAnimation, + StyleSheet, + Text, + View, +} = React; + +type LayoutEvent = { + nativeEvent: { + layout: { + x: number; + y: number; + width: number; + height: number; + }; + }; +}; + +var LayoutEventExample = React.createClass({ + getInitialState: function() { + return { + viewStyle: { + margin: 20, + }, + }; + }, + animateViewLayout: function() { + LayoutAnimation.configureNext( + LayoutAnimation.Presets.spring, + () => { + console.log('layout animation done.'); + this.addWrapText(); + }, + (error) => { throw new Error(JSON.stringify(error)); } + ); + this.setState({ + viewStyle: { + margin: this.state.viewStyle.margin > 20 ? 20 : 60, + } + }); + }, + addWrapText: function() { + this.setState( + {extraText: ' And a bunch more text to wrap around a few lines.'}, + this.changeContainer + ); + }, + changeContainer: function() { + this.setState({containerStyle: {width: 280}}); + }, + onViewLayout: function(e: LayoutEvent) { + console.log('received view layout event\n', e.nativeEvent); + this.setState({viewLayout: e.nativeEvent.layout}); + }, + onTextLayout: function(e: LayoutEvent) { + console.log('received text layout event\n', e.nativeEvent); + this.setState({textLayout: e.nativeEvent.layout}); + }, + onImageLayout: function(e: LayoutEvent) { + console.log('received image layout event\n', e.nativeEvent); + this.setState({imageLayout: e.nativeEvent.layout}); + }, + render: function() { + var viewStyle = [styles.view, this.state.viewStyle]; + var textLayout = this.state.textLayout || {width: '?', height: '?'}; + var imageLayout = this.state.imageLayout || {x: '?', y: '?'}; + return ( + + + onLayout events are called on mount and whenever layout is updated, + including after layout animations complete.{' '} + + Press here to change layout. + + + + + + ViewLayout: {JSON.stringify(this.state.viewLayout, null, ' ') + '\n\n'} + + + A simple piece of text.{this.state.extraText} + + + {'\n'} + Text w/h: {textLayout.width}/{textLayout.height + '\n'} + Image x/y: {imageLayout.x}/{imageLayout.y} + + + + ); + } +}); + +var styles = StyleSheet.create({ + view: { + padding: 12, + borderColor: 'black', + borderWidth: 0.5, + backgroundColor: 'transparent', + }, + text: { + alignSelf: 'flex-start', + borderColor: 'rgba(0, 0, 255, 0.2)', + borderWidth: 0.5, + }, + image: { + width: 50, + height: 50, + marginBottom: 10, + alignSelf: 'center', + }, + pressText: { + fontWeight: 'bold', + }, +}); + +exports.title = 'onLayout'; +exports.description = 'Layout events can be used to measure view size and position.'; +exports.examples = [ +{ + title: 'onLayout', + render: function(): ReactElement { + return ; + }, +}]; diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index a24ec1a54a20ff..dd2336df59dff0 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -64,6 +64,7 @@ var APIS = [ require('./BorderExample'), require('./CameraRollExample.ios'), require('./GeolocationExample'), + require('./LayoutEventsExample'), require('./LayoutExample'), require('./NetInfoExample'), require('./PanResponderExample'), diff --git a/IntegrationTests/IntegrationTestsApp.js b/IntegrationTests/IntegrationTestsApp.js index dbb5dde835b5cf..1e61a0dbc155e9 100644 --- a/IntegrationTests/IntegrationTestsApp.js +++ b/IntegrationTests/IntegrationTestsApp.js @@ -25,6 +25,7 @@ var TESTS = [ require('./IntegrationTestHarnessTest'), require('./TimersTest'), require('./AsyncStorageTest'), + require('./LayoutEventsTest'), require('./SimpleSnapshotTest'), ]; diff --git a/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m b/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m index e0a43e7931fae6..9bf1a4fc1450b9 100644 --- a/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m +++ b/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m @@ -71,6 +71,11 @@ - (void)testAsyncStorage [_runner runTest:_cmd module:@"AsyncStorageTest"]; } +- (void)testLayoutEvents +{ + [_runner runTest:_cmd module:@"LayoutEventsTest"]; +} + #pragma mark Snapshot Tests - (void)testSimpleSnapshot diff --git a/IntegrationTests/LayoutEventsTest.js b/IntegrationTests/LayoutEventsTest.js new file mode 100644 index 00000000000000..7e8cd3a0dcad3b --- /dev/null +++ b/IntegrationTests/LayoutEventsTest.js @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule LayoutEventsTest + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + Image, + LayoutAnimation, + NativeModules, + StyleSheet, + Text, + View, +} = React; +var TestModule = NativeModules.TestModule || NativeModules.SnapshotTestManager; + +var deepDiffer = require('deepDiffer'); + +function debug() { + //console.log.apply(null, arguments); +} + +type LayoutEvent = { + nativeEvent: { + layout: { + x: number; + y: number; + width: number; + height: number; + }; + }; +}; + +var LayoutEventsTest = React.createClass({ + getInitialState: function() { + return { + didAnimation: false, + }; + }, + animateViewLayout: function() { + LayoutAnimation.configureNext( + LayoutAnimation.Presets.spring, + () => { + debug('layout animation done.'); + this.checkLayout(this.addWrapText); + }, + (error) => { throw new Error(JSON.stringify(error)); } + ); + this.setState({viewStyle: {margin: 60}}); + }, + addWrapText: function() { + this.setState( + {extraText: ' And a bunch more text to wrap around a few lines.'}, + () => this.checkLayout(this.changeContainer) + ); + }, + changeContainer: function() { + this.setState( + {containerStyle: {width: 280}}, + () => this.checkLayout(TestModule.markTestCompleted) + ); + }, + checkLayout: function(next?: ?Function) { + if (!this.isMounted()) { + return; + } + this.refs.view.measure((x, y, width, height) => { + this.compare('view', {x, y, width, height}, this.state.viewLayout); + if (typeof next === 'function') { + next(); + } else if (!this.state.didAnimation) { + // Trigger first state change after onLayout fires + this.animateViewLayout(); + this.state.didAnimation = true; + } + }); + this.refs.txt.measure((x, y, width, height) => { + this.compare('txt', {x, y, width, height}, this.state.textLayout); + }); + this.refs.img.measure((x, y, width, height) => { + this.compare('img', {x, y, width, height}, this.state.imageLayout); + }); + }, + compare: function(node: string, measured: any, onLayout: any): void { + if (deepDiffer(measured, onLayout)) { + var data = {measured, onLayout}; + throw new Error( + node + ' onLayout mismatch with measure ' + + JSON.stringify(data, null, ' ') + ); + } + }, + onViewLayout: function(e: LayoutEvent) { + debug('received view layout event\n', e.nativeEvent); + this.setState({viewLayout: e.nativeEvent.layout}, this.checkLayout); + }, + onTextLayout: function(e: LayoutEvent) { + debug('received text layout event\n', e.nativeEvent); + this.setState({textLayout: e.nativeEvent.layout}, this.checkLayout); + }, + onImageLayout: function(e: LayoutEvent) { + debug('received image layout event\n', e.nativeEvent); + this.setState({imageLayout: e.nativeEvent.layout}, this.checkLayout); + }, + render: function() { + var viewStyle = [styles.view, this.state.viewStyle]; + var textLayout = this.state.textLayout || {width: '?', height: '?'}; + var imageLayout = this.state.imageLayout || {x: '?', y: '?'}; + return ( + + + + + ViewLayout: {JSON.stringify(this.state.viewLayout, null, ' ') + '\n\n'} + + + A simple piece of text.{this.state.extraText} + + + {'\n'} + Text w/h: {textLayout.width}/{textLayout.height + '\n'} + Image x/y: {imageLayout.x}/{imageLayout.y} + + + + ); + } +}); + +var styles = StyleSheet.create({ + container: { + margin: 40, + }, + view: { + margin: 20, + padding: 12, + borderColor: 'black', + borderWidth: 0.5, + backgroundColor: 'transparent', + }, + text: { + alignSelf: 'flex-start', + borderColor: 'rgba(0, 0, 255, 0.2)', + borderWidth: 0.5, + }, + image: { + width: 50, + height: 50, + marginBottom: 10, + alignSelf: 'center', + }, +}); + +module.exports = LayoutEventsTest; diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index c7ca2ee261ab91..aa69ab0eb70c66 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -90,6 +90,11 @@ var View = React.createClass({ onStartShouldSetResponder: PropTypes.func, onStartShouldSetResponderCapture: PropTypes.func, + /** + * Invoked on mount and layout changes with {x, y, width, height}. + */ + onLayout: PropTypes.func, + /** * In the absence of `auto` property, `none` is much like `CSS`'s `none` * value. `box-none` is as if you had applied the `CSS` class: diff --git a/Libraries/ReactIOS/ReactIOSViewAttributes.js b/Libraries/ReactIOS/ReactIOSViewAttributes.js index 069f00b2491925..489741b053b5ea 100644 --- a/Libraries/ReactIOS/ReactIOSViewAttributes.js +++ b/Libraries/ReactIOS/ReactIOSViewAttributes.js @@ -9,8 +9,7 @@ * @providesModule ReactIOSViewAttributes * @flow */ - -"use strict"; +'use strict'; var merge = require('merge'); @@ -21,6 +20,7 @@ ReactIOSViewAttributes.UIView = { accessible: true, accessibilityLabel: true, testID: true, + onLayout: true, }; ReactIOSViewAttributes.RCTView = merge( @@ -31,7 +31,7 @@ ReactIOSViewAttributes.RCTView = merge( // For this property to be effective, it must be applied to a view that contains // many subviews that extend outside its bound. The subviews must also have // overflow: hidden, as should the containing view (or one of its superviews). - removeClippedSubviews: true + removeClippedSubviews: true, }); module.exports = ReactIOSViewAttributes; diff --git a/Libraries/ReactIOS/diffRawProperties.js b/Libraries/ReactIOS/diffRawProperties.js index 3a5de284f4910e..ddd6edbea05c71 100644 --- a/Libraries/ReactIOS/diffRawProperties.js +++ b/Libraries/ReactIOS/diffRawProperties.js @@ -42,6 +42,16 @@ function diffRawProperties( } prevProp = prevProps && prevProps[propKey]; nextProp = nextProps[propKey]; + + // functions are converted to booleans as markers that the associated + // events should be sent from native. + if (typeof prevProp === 'function') { + prevProp = true; + } + if (typeof nextProp === 'function') { + nextProp = true; + } + if (prevProp !== nextProp) { // If you want a property's diff to be detected, you must configure it // to be so - *or* it must be a scalar property. For now, we'll allow @@ -75,6 +85,16 @@ function diffRawProperties( } prevProp = prevProps[propKey]; nextProp = nextProps && nextProps[propKey]; + + // functions are converted to booleans as markers that the associated + // events should be sent from native. + if (typeof prevProp === 'function') { + prevProp = true; + } + if (typeof nextProp === 'function') { + nextProp = true; + } + if (prevProp !== nextProp) { if (nextProp === undefined) { nextProp = null; // null is a sentinel we explicitly send to native diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index eac3a77359fd96..df90ff1505c7bb 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -19,6 +19,7 @@ #import "RCTBridge.h" #import "RCTConvert.h" #import "RCTDefines.h" +#import "RCTEventDispatcher.h" #import "RCTLog.h" #import "RCTProfile.h" #import "RCTRootView.h" @@ -420,17 +421,31 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo [rootShadowView collectRootUpdatedFrames:viewsWithNewFrames parentConstraint:(CGSize){CSS_UNDEFINED, CSS_UNDEFINED}]; - // Parallel arrays + // Parallel arrays are built and then handed off to main thread NSMutableArray *frameReactTags = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *frames = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *areNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *parentsAreNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; + NSMutableArray *onLayoutEvents = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; for (RCTShadowView *shadowView in viewsWithNewFrames) { [frameReactTags addObject:shadowView.reactTag]; [frames addObject:[NSValue valueWithCGRect:shadowView.frame]]; [areNew addObject:@(shadowView.isNewView)]; [parentsAreNew addObject:@(shadowView.superview.isNewView)]; + id event = [NSNull null]; + if (shadowView.hasOnLayout) { + event = @{ + @"target": shadowView.reactTag, + @"layout": @{ + @"x": @(shadowView.frame.origin.x), + @"y": @(shadowView.frame.origin.y), + @"width": @(shadowView.frame.size.width), + @"height": @(shadowView.frame.size.height), + }, + }; + } + [onLayoutEvents addObject:event]; } for (RCTShadowView *shadowView in viewsWithNewFrames) { @@ -448,20 +463,30 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo // Perform layout (possibly animated) NSNumber *rootViewTag = rootShadowView.reactTag; return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + RCTResponseSenderBlock callback = self->_layoutAnimation.callback; + __block NSInteger completionsCalled = 0; for (NSUInteger ii = 0; ii < frames.count; ii++) { NSNumber *reactTag = frameReactTags[ii]; UIView *view = viewRegistry[reactTag]; CGRect frame = [frames[ii] CGRectValue]; + id event = onLayoutEvents[ii]; + + BOOL isNew = [areNew[ii] boolValue]; + RCTAnimation *updateAnimation = isNew ? nil : _layoutAnimation.updateAnimation; + BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue]; + RCTAnimation *createAnimation = shouldAnimateCreation ? _layoutAnimation.createAnimation : nil; void (^completion)(BOOL finished) = ^(BOOL finished) { - if (self->_layoutAnimation.callback) { - self->_layoutAnimation.callback(@[@(finished)]); + completionsCalled++; + if (event != [NSNull null]) { + [self.bridge.eventDispatcher sendInputEventWithName:@"topLayout" body:event]; + } + if (callback && completionsCalled == frames.count - 1) { + callback(@[@(finished)]); } }; // Animate view update - BOOL isNew = [areNew[ii] boolValue]; - RCTAnimation *updateAnimation = isNew ? nil: _layoutAnimation.updateAnimation; if (updateAnimation) { [updateAnimation performAnimations:^{ [view reactSetFrame:frame]; @@ -478,9 +503,7 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo } // Animate view creation - BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue]; - RCTAnimation *createAnimation = _layoutAnimation.createAnimation; - if (shouldAnimateCreation && createAnimation) { + if (createAnimation) { if ([createAnimation.property isEqualToString:@"scaleXY"]) { view.layer.transform = CATransform3DMakeScale(0, 0, 0); } else if ([createAnimation.property isEqualToString:@"opacity"]) { @@ -1262,6 +1285,9 @@ - (NSDictionary *)customDirectEventTypes @"topScrollAnimationEnd": @{ @"registrationName": @"onScrollAnimationEnd" }, + @"topLayout": @{ + @"registrationName": @"onLayout" + }, @"topSelectionChange": @{ @"registrationName": @"onSelectionChange" }, diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index 8d68855f7f186a..83350ac469cb7e 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -41,6 +41,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *); @property (nonatomic, assign) BOOL isBGColorExplicitlySet; // Used to propagate to children @property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children @property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle; +@property (nonatomic, assign) BOOL hasOnLayout; /** * isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 62fb29116f5540..4dfb296fda35de 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -198,4 +198,6 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *) view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet; } +RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL) + @end From 7141948a057e9cc1c18a9eab30d7daa2d60bfa5b Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 7 May 2015 12:29:36 -0700 Subject: [PATCH 236/250] Bump fetch and add exports/globals for Headers, Request and Response Summary: Now on fetch 0.8.1, the latest tagged release. Previous version used was 0.7.0. See #1162 cc @vjeux @jtremback Closes https://github.com/facebook/react-native/pull/1192 Github Author: Brent Vatne Test Plan: I arc patched and ran movies demo and storyline, they work fine --- Examples/Movies/SearchScreen.js | 2 - Libraries/Fetch/fetch.js | 182 ++++++++++++------ .../InitializeJavaScriptAppEngine.js | 7 +- .../Initialization/loadSourceMap.js | 2 - .../react-native/react-native-interface.js | 5 + 5 files changed, 133 insertions(+), 65 deletions(-) diff --git a/Examples/Movies/SearchScreen.js b/Examples/Movies/SearchScreen.js index fc31a90dc4d6b7..e484d0f6591ce0 100644 --- a/Examples/Movies/SearchScreen.js +++ b/Examples/Movies/SearchScreen.js @@ -29,8 +29,6 @@ var TimerMixin = require('react-timer-mixin'); var MovieCell = require('./MovieCell'); var MovieScreen = require('./MovieScreen'); -var fetch = require('fetch'); - /** * This is for demo purposes only, and rate limited. * In case you want to use the Rotten Tomatoes' API on a real app you should diff --git a/Libraries/Fetch/fetch.js b/Libraries/Fetch/fetch.js index 241172669f9066..829f7c4256d63a 100644 --- a/Libraries/Fetch/fetch.js +++ b/Libraries/Fetch/fetch.js @@ -47,6 +47,23 @@ var self = {}; return } + function normalizeName(name) { + if (typeof name !== 'string') { + name = name.toString(); + } + if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { + throw new TypeError('Invalid character in header field name') + } + return name.toLowerCase() + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = value.toString(); + } + return value + } + function Headers(headers) { this.map = {} @@ -66,7 +83,8 @@ var self = {}; } Headers.prototype.append = function(name, value) { - name = name.toLowerCase() + name = normalizeName(name) + value = normalizeValue(value) var list = this.map[name] if (!list) { list = [] @@ -76,24 +94,24 @@ var self = {}; } Headers.prototype['delete'] = function(name) { - delete this.map[name.toLowerCase()] + delete this.map[normalizeName(name)] } Headers.prototype.get = function(name) { - var values = this.map[name.toLowerCase()] + var values = this.map[normalizeName(name)] return values ? values[0] : null } Headers.prototype.getAll = function(name) { - return this.map[name.toLowerCase()] || [] + return this.map[normalizeName(name)] || [] } Headers.prototype.has = function(name) { - return this.map.hasOwnProperty(name.toLowerCase()) + return this.map.hasOwnProperty(normalizeName(name)) } Headers.prototype.set = function(name, value) { - this.map[name.toLowerCase()] = [value] + this.map[normalizeName(name)] = [normalizeValue(value)] } // Instead of iterable for now. @@ -134,22 +152,51 @@ var self = {}; return fileReaderReady(reader) } - var blobSupport = 'FileReader' in self && 'Blob' in self && (function() { - try { - new Blob(); - return true - } catch(e) { - return false - } - })(); + var support = { + blob: 'FileReader' in self && 'Blob' in self && (function() { + try { + new Blob(); + return true + } catch(e) { + return false + } + })(), + formData: 'FormData' in self + } function Body() { this.bodyUsed = false - if (blobSupport) { + + this._initBody = function(body) { + this._bodyInit = body + if (typeof body === 'string') { + this._bodyText = body + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body + } else if (!body) { + this._bodyText = '' + } else { + throw new Error('unsupported BodyInit type') + } + } + + if (support.blob) { this.blob = function() { var rejected = consumed(this) - return rejected ? rejected : Promise.resolve(this._bodyBlob) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob') + } else { + return Promise.resolve(new Blob([this._bodyText])) + } } this.arrayBuffer = function() { @@ -157,7 +204,18 @@ var self = {}; } this.text = function() { - return this.blob().then(readBlobAsText) + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text') + } else { + return Promise.resolve(this._bodyText) + } } } else { this.text = function() { @@ -166,7 +224,7 @@ var self = {}; } } - if ('FormData' in self) { + if (support.formData) { this.formData = function() { return this.text().then(decode) } @@ -190,12 +248,17 @@ var self = {}; function Request(url, options) { options = options || {} this.url = url - this._body = options.body + this.credentials = options.credentials || 'omit' this.headers = new Headers(options.headers) this.method = normalizeMethod(options.method || 'GET') this.mode = options.mode || null this.referrer = null + + if ((this.method === 'GET' || this.method === 'HEAD') && options.body) { + throw new TypeError('Body not allowed for GET or HEAD requests') + } + this._initBody(options.body) } function decode(body) { @@ -223,11 +286,43 @@ var self = {}; return head } - Request.prototype.fetch = function() { - var self = this + Body.call(Request.prototype) + + function Response(bodyInit, options) { + if (!options) { + options = {} + } + + this._initBody(bodyInit) + this.type = 'default' + this.url = null + this.status = options.status + this.ok = this.status >= 200 && this.status < 300 + this.statusText = options.statusText + this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers) + this.url = options.url || '' + } + + Body.call(Response.prototype) + + self.Headers = Headers; + self.Request = Request; + self.Response = Response; + + self.fetch = function(input, init) { + // TODO: Request constructor should accept input, init + var request + if (Request.prototype.isPrototypeOf(input) && !init) { + request = input + } else { + request = new Request(input, init) + } return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest() + if (request.credentials === 'cors') { + xhr.withCredentials = true; + } function responseURL() { if ('responseURL' in xhr) { @@ -262,57 +357,24 @@ var self = {}; reject(new TypeError('Network request failed')) } - xhr.open(self.method, self.url) - if ('responseType' in xhr && blobSupport) { + xhr.open(request.method, request.url, true) + + if ('responseType' in xhr && support.blob) { xhr.responseType = 'blob' } - self.headers.forEach(function(name, values) { + request.headers.forEach(function(name, values) { values.forEach(function(value) { xhr.setRequestHeader(name, value) }) }) - xhr.send((self._body === undefined) ? null : self._body) + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) }) } - - Body.call(Request.prototype) - - function Response(bodyInit, options) { - if (!options) { - options = {} - } - - if (blobSupport) { - if (typeof bodyInit === 'string') { - this._bodyBlob = new Blob([bodyInit]) - } else { - this._bodyBlob = bodyInit - } - } else { - this._bodyText = bodyInit - } - this.type = 'default' - this.url = null - this.status = options.status - this.statusText = options.statusText - this.headers = options.headers - this.url = options.url || '' - } - - Body.call(Response.prototype) - - self.Headers = Headers; - self.Request = Request; - self.Response = Response; - - self.fetch = function (url, options) { - return new Request(url, options).fetch() - } self.fetch.polyfill = true })(); /** End of the third-party code */ -module.exports = self.fetch; +module.exports = self; diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index bb0aa263c96470..7bddf87b69c786 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -135,7 +135,12 @@ function setupXHR() { // The native XMLHttpRequest in Chrome dev tools is CORS aware and won't // let you fetch anything from the internet GLOBAL.XMLHttpRequest = require('XMLHttpRequest'); - GLOBAL.fetch = require('fetch'); + + var fetchPolyfill = require('fetch'); + GLOBAL.fetch = fetchPolyfill.fetch; + GLOBAL.Headers = fetchPolyfill.Headers; + GLOBAL.Request = fetchPolyfill.Request; + GLOBAL.Response = fetchPolyfill.Response; } function setupGeolocation() { diff --git a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js index 4d4d21e25d6942..a826db7bf5cb34 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js +++ b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js @@ -17,8 +17,6 @@ var RCTSourceCode = require('NativeModules').SourceCode; var SourceMapConsumer = require('SourceMap').SourceMapConsumer; var SourceMapURL = require('./source-map-url'); -var fetch = require('fetch'); - function loadSourceMap(): Promise { return fetchSourceMap() .then(map => new SourceMapConsumer(map)); diff --git a/Libraries/react-native/react-native-interface.js b/Libraries/react-native/react-native-interface.js index 6408526b812e6a..b76518387826eb 100644 --- a/Libraries/react-native/react-native-interface.js +++ b/Libraries/react-native/react-native-interface.js @@ -16,3 +16,8 @@ declare var __DEV__: boolean; declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ inject: ?((stuff: Object) => void) };*/ + +declare var fetch: any; +declare var Headers: any; +declare var Request: any; +declare var Response: any; From d3119a8fb1243dc127df8890f22b8aa9c0d46ed0 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Thu, 7 May 2015 15:05:28 -0700 Subject: [PATCH 237/250] Fix typo in LinkingLibraries.md s/insed/inside --- docs/LinkingLibraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/LinkingLibraries.md b/docs/LinkingLibraries.md index c90768c94cfc4c..560ed6b590c757 100644 --- a/docs/LinkingLibraries.md +++ b/docs/LinkingLibraries.md @@ -37,7 +37,7 @@ on Xcode); Click on your main project file (the one that represents the `.xcodeproj`) select `Build Phases` and drag the static library from the `Products` folder -insed the Library you are importing to `Link Binary With Libraries` +inside the Library you are importing to `Link Binary With Libraries` ![](/react-native/img/AddToBuildPhases.png) From debd5b0942736f1c9a72c4aad3458a9c17db6eee Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 7 May 2015 16:05:30 -0700 Subject: [PATCH 238/250] [Docs] Remove Transforms from sidebar, add to Style --- docs/Style.md | 9 ++++---- website/server/convert.js | 5 +++- website/server/extractDocs.js | 43 ++++++++++++++++++++++++++++------- website/server/generate.js | 2 +- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/docs/Style.md b/docs/Style.md index cb33f9bca4e062..ec6571bcd5e601 100644 --- a/docs/Style.md +++ b/docs/Style.md @@ -95,7 +95,8 @@ var List = React.createClass({ You can checkout latest support of CSS Properties in following Links. -- [View Properties](http://facebook.github.io/react-native/docs/view.html#style) -- [Image Properties](http://facebook.github.io/react-native/docs/image.html#style) -- [Text Properties](http://facebook.github.io/react-native/docs/text.html#style) -- [Flex Properties](http://facebook.github.io/react-native/docs/flexbox.html#content) +- [View Properties](/react-native/docs/view.html#style) +- [Image Properties](/react-native/docs/image.html#style) +- [Text Properties](/react-native/docs/text.html#style) +- [Flex Properties](/react-native/docs/flexbox.html#content) +- [Transform Properties](/react-native/docs/transforms.html#content) diff --git a/website/server/convert.js b/website/server/convert.js index 8dfc4ce925a2e3..8edc1b190ba33b 100644 --- a/website/server/convert.js +++ b/website/server/convert.js @@ -69,7 +69,10 @@ function execute() { try { value = JSON.parse(value); } catch(e) { } metadata[key] = value; } - metadatas.files.push(metadata); + + if (metadata.sidebar !== false) { + metadatas.files.push(metadata); + } if (metadata.permalink.match(/^https?:/)) { return; diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js index 57cb5b17a1f31a..a4500e9ebb798c 100644 --- a/website/server/extractDocs.js +++ b/website/server/extractDocs.js @@ -44,6 +44,33 @@ function getExample(componentName) { }; } +// Hide a component from the sidebar by making it return false from +// this function +function shouldDisplayInSidebar(componentName) { + if (componentName === 'Transforms') { + return false; + } + + return true; +} + +function getNextComponent(i) { + var next; + var filepath = all[i]; + + if (all[i + 1]) { + var nextComponentName = getNameFromPath(all[i + 1]); + + if (shouldDisplayInSidebar(nextComponentName)) { + return slugify(nextComponentName); + } else { + return getNextComponent(i + 1); + } + } else { + return 'network'; + } +} + function componentsToMarkdown(type, json, filepath, i, styles) { var componentName = getNameFromPath(filepath); @@ -59,16 +86,19 @@ function componentsToMarkdown(type, json, filepath, i, styles) { } json.example = getExample(componentName); + // Put Flexbox into the Polyfills category + var category = (type === 'style' ? 'Polyfills' : type + 's'); + var next = getNextComponent(i); + var res = [ '---', 'id: ' + slugify(componentName), 'title: ' + componentName, 'layout: autodocs', - 'category: ' + (type === 'style' ? 'Polyfills' : type + 's'), + 'category: ' + category, 'permalink: docs/' + slugify(componentName) + '.html', - 'next: ' + (all[i + 1] ? - slugify(getNameFromPath(all[i + 1])) : - 'network'), + 'next: ' + next, + 'sidebar: ' + shouldDisplayInSidebar(componentName), '---', JSON.stringify(json, null, 2), ].filter(function(line) { return line; }).join('\n'); @@ -107,16 +137,13 @@ function renderStyle(filepath) { [docgen.handlers.propTypeHandler] ); - // Remove deprecated style props + // Remove deprecated transform props from docs if (filepath === "../Libraries/StyleSheet/TransformPropTypes.js") { ['rotation', 'scaleX', 'scaleY', 'translateX', 'translateY'].forEach(function(key) { delete json['props'][key]; }); } - - // console.log(json); - return componentsToMarkdown('style', json, filepath, n++); } diff --git a/website/server/generate.js b/website/server/generate.js index 2ac303b4980925..aab331c19b8668 100644 --- a/website/server/generate.js +++ b/website/server/generate.js @@ -36,7 +36,7 @@ glob('src/**/*.*', function(er, files) { return; } if (response.statusCode != 200) { - reject(new Error('Status ' + response.statusCode + ':\n' + body)); + reject(new Error('Status ' + response.statusCode + ':\n' + body)); return; } mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), '')); From 7b4ea51bf03bc76f8ff7b876744e87b28b788d1f Mon Sep 17 00:00:00 2001 From: Jiajie Zhu Date: Thu, 7 May 2015 16:20:08 -0700 Subject: [PATCH 239/250] [madman] map - fix bug that onRegionChangeComplete stopped emitting --- React/Views/RCTMap.h | 2 +- React/Views/RCTMap.m | 2 +- React/Views/RCTMapManager.m | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/React/Views/RCTMap.h b/React/Views/RCTMap.h index 89e4c0a8088ff7..d372db56e465d9 100644 --- a/React/Views/RCTMap.h +++ b/React/Views/RCTMap.h @@ -21,7 +21,7 @@ extern const CGFloat RCTMapZoomBoundBuffer; @interface RCTMap: MKMapView @property (nonatomic, assign) BOOL followUserLocation; -@property (nonatomic, assign) BOOL hasStartedLoading; +@property (nonatomic, assign) BOOL hasStartedRendering; @property (nonatomic, assign) CGFloat minDelta; @property (nonatomic, assign) CGFloat maxDelta; @property (nonatomic, assign) UIEdgeInsets legalLabelInsets; diff --git a/React/Views/RCTMap.m b/React/Views/RCTMap.m index 5037d3390b1423..40b60508e26da4 100644 --- a/React/Views/RCTMap.m +++ b/React/Views/RCTMap.m @@ -27,7 +27,7 @@ - (instancetype)init { if ((self = [super init])) { - _hasStartedLoading = NO; + _hasStartedRendering = NO; // Find Apple link label for (UIView *subview in self.subviews) { diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m index f9a6b9175ccbf0..8d334f60ba6d8c 100644 --- a/React/Views/RCTMapManager.m +++ b/React/Views/RCTMapManager.m @@ -84,15 +84,15 @@ - (void)mapView:(RCTMap *)mapView regionDidChangeAnimated:(BOOL)animated [self _regionChanged:mapView]; // Don't send region did change events until map has - // started loading, as these won't represent the final location - if (mapView.hasStartedLoading) { + // started rendering, as these won't represent the final location + if (mapView.hasStartedRendering) { [self _emitRegionChangeEvent:mapView continuous:NO]; }; } -- (void)mapViewWillStartLoadingMap:(RCTMap *)mapView +- (void)mapViewWillStartRenderingMap:(RCTMap *)mapView { - mapView.hasStartedLoading = YES; + mapView.hasStartedRendering = YES; [self _emitRegionChangeEvent:mapView continuous:NO]; } From 736d86057106376267a15c3edc85fdefda0a00fe Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 7 May 2015 16:38:29 -0700 Subject: [PATCH 240/250] [ReactNative] Fix RCTScrollView setContentInset --- React/Views/RCTScrollView.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m index ca8f94242df8c7..1373d8bd16bbbb 100644 --- a/React/Views/RCTScrollView.m +++ b/React/Views/RCTScrollView.m @@ -315,8 +315,14 @@ - (void)layoutSubviews - (void)setContentInset:(UIEdgeInsets)contentInset { + CGPoint contentOffset = _scrollView.contentOffset; + _contentInset = contentInset; - [self setNeedsLayout]; + [RCTView autoAdjustInsetsForView:self + withScrollView:_scrollView + updateOffset:NO]; + + _scrollView.contentOffset = contentOffset; } - (void)scrollToOffset:(CGPoint)offset From c76fb40ec4ad90f210425d33eb4981490dd1fd15 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Thu, 7 May 2015 17:30:41 -0700 Subject: [PATCH 241/250] [ReactNative] Register assets with AssetRegistry --- Libraries/Image/AssetRegistry.js | 20 +++++++++ .../__tests__/resolveAssetSource-test.js | 44 ++++++++++--------- Libraries/Image/resolveAssetSource.js | 44 +++++++------------ .../src/Packager/__tests__/Packager-test.js | 8 ++-- packager/react-packager/src/Packager/index.js | 3 +- 5 files changed, 66 insertions(+), 53 deletions(-) create mode 100644 Libraries/Image/AssetRegistry.js diff --git a/Libraries/Image/AssetRegistry.js b/Libraries/Image/AssetRegistry.js new file mode 100644 index 00000000000000..df4173e78fe584 --- /dev/null +++ b/Libraries/Image/AssetRegistry.js @@ -0,0 +1,20 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule AssetRegistry + */ +'use strict'; + +var assets = []; + +function registerAsset(asset) { + // `push` returns new array length, so the first asset will + // get id 1 (not 0) to make the value truthy + return assets.push(asset); +} + +function getAssetByID(assetId) { + return assets[assetId - 1]; +} + +module.exports = { registerAsset, getAssetByID }; diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js index 632ff96567ee1c..c5fc3bbe1ea3dc 100644 --- a/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -8,19 +8,24 @@ */ 'use strict'; -jest.dontMock('../resolveAssetSource'); +jest + .dontMock('AssetRegistry') + .dontMock('../resolveAssetSource'); var resolveAssetSource; var SourceCode; +var AssetRegistry; function expectResolvesAsset(input, expectedSource) { - expect(resolveAssetSource(input)).toEqual(expectedSource); + var assetId = AssetRegistry.registerAsset(input); + expect(resolveAssetSource(assetId)).toEqual(expectedSource); } describe('resolveAssetSource', () => { beforeEach(() => { jest.resetModuleRegistry(); SourceCode = require('NativeModules').SourceCode; + AssetRegistry = require('AssetRegistry'); resolveAssetSource = require('../resolveAssetSource'); }); @@ -32,6 +37,22 @@ describe('resolveAssetSource', () => { expect(resolveAssetSource(source2)).toBe(source2); }); + it('does not change deprecated assets', () => { + expect(resolveAssetSource({ + isStatic: true, + deprecated: true, + width: 100, + height: 200, + uri: 'logo', + })).toEqual({ + isStatic: true, + deprecated: true, + width: 100, + height: 200, + uri: 'logo', + }); + }); + it('ignores any weird data', () => { expect(resolveAssetSource(null)).toBe(null); expect(resolveAssetSource(42)).toBe(null); @@ -81,25 +102,6 @@ describe('resolveAssetSource', () => { }); }); - it('does not change deprecated assets', () => { - expectResolvesAsset({ - __packager_asset: true, - deprecated: true, - fileSystemLocation: '/root/app/module/a', - httpServerLocation: '/assets/module/a', - width: 100, - height: 200, - scales: [1], - hash: '5b6f00f', - name: 'logo', - type: 'png', - }, { - isStatic: true, - width: 100, - height: 200, - uri: 'logo', - }); - }); }); describe('bundle was loaded from file', () => { diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js index 02189155877006..29d59a9a5dfc5b 100644 --- a/Libraries/Image/resolveAssetSource.js +++ b/Libraries/Image/resolveAssetSource.js @@ -10,6 +10,7 @@ */ 'use strict'; +var AssetRegistry = require('AssetRegistry'); var PixelRatio = require('PixelRatio'); var SourceCode = require('NativeModules').SourceCode; @@ -44,58 +45,47 @@ function pickScale(scales, deviceScale) { } function resolveAssetSource(source) { - if (!source || typeof source !== 'object') { - return null; - } - - if (!source.__packager_asset) { + if (typeof source === 'object') { return source; } - // Deprecated assets are managed by Xcode for now, - // just returning image name as `uri` - // Examples: - // require('image!deprecatd_logo_example') - // require('./new-hotness-logo-example.png') - if (source.deprecated) { - return { - width: source.width, - height: source.height, - isStatic: true, - uri: source.name || source.uri, // TODO(frantic): remove uri - }; + var asset = AssetRegistry.getAssetByID(source); + if (asset) { + return assetToImageSource(asset); } + return null; +} + +function assetToImageSource(asset) { // TODO(frantic): currently httpServerLocation is used both as // path in http URL and path within IPA. Should we have zipArchiveLocation? - var path = source.httpServerLocation; + var path = asset.httpServerLocation; if (path[0] === '/') { path = path.substr(1); } - var scale = pickScale(source.scales, PixelRatio.get()); + var scale = pickScale(asset.scales, PixelRatio.get()); var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x'; - var fileName = source.name + scaleSuffix + '.' + source.type; + var fileName = asset.name + scaleSuffix + '.' + asset.type; var serverURL = getServerURL(); if (serverURL) { return { - width: source.width, - height: source.height, + width: asset.width, + height: asset.height, uri: serverURL + path + '/' + fileName + - '?hash=' + source.hash, + '?hash=' + asset.hash, isStatic: false, }; } else { return { - width: source.width, - height: source.height, + width: asset.width, + height: asset.height, uri: path + '/' + fileName, isStatic: true, }; } - - return source; } module.exports = resolveAssetSource; diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js index 3f09344624df16..d4a1c0f36e3ac4 100644 --- a/packager/react-packager/src/Packager/__tests__/Packager-test.js +++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js @@ -166,12 +166,12 @@ describe('Packager', function() { }; expect(p.addModule.mock.calls[3][0]).toEqual({ - code: 'lol module.exports = ' + + code: 'lol module.exports = require("AssetRegistry").registerAsset(' + JSON.stringify(imgModule) + - '; lol', - sourceCode: 'module.exports = ' + + '); lol', + sourceCode: 'module.exports = require("AssetRegistry").registerAsset(' + JSON.stringify(imgModule) + - ';', + ');', sourcePath: '/root/img/new_image.png' }); diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index c03a0432c20c26..e647f7642c1fb1 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -219,7 +219,8 @@ Packager.prototype.generateAssetModule = function(ppackage, module) { ppackage.addAsset(img); - var code = 'module.exports = ' + JSON.stringify(img) + ';'; + var ASSET_TEMPLATE = 'module.exports = require("AssetRegistry").registerAsset(%json);'; + var code = ASSET_TEMPLATE.replace('%json', JSON.stringify(img)); return new ModuleTransport({ code: code, From 61bd008ea03b33b1c2ee8b68dc5d47c6fbabda19 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Thu, 7 May 2015 18:53:41 -0700 Subject: [PATCH 242/250] [ReactNative] Fix TouchableOpacity crash when child props are missing --- Libraries/Components/Touchable/TouchableOpacity.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 8e9a34146d0c9a..5c673947f9535c 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -106,7 +106,10 @@ var TouchableOpacity = React.createClass({ }, touchableHandleActivePressOut: function() { - var childStyle = flattenStyle(this.refs[CHILD_REF].props.style) || {}; + var childStyle = ( + this.refs[CHILD_REF].props && + flattenStyle(this.refs[CHILD_REF].props.style) + ) || {}; this.setOpacityTo(childStyle.opacity === undefined ? 1 : childStyle.opacity); this.props.onPressOut && this.props.onPressOut(); }, From e433b6a57ea85980165deaa928d96b82b452dae1 Mon Sep 17 00:00:00 2001 From: Gabe Levi Date: Thu, 7 May 2015 18:41:02 -0700 Subject: [PATCH 243/250] Bump jstransform version for react-native --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e059c3fa5b66c..61735be6422bd0 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "graceful-fs": "^3.0.6", "image-size": "0.3.5", "joi": "~5.1.0", - "jstransform": "10.1.0", + "jstransform": "11.0.1", "module-deps": "3.5.6", "optimist": "0.6.1", "promise": "^7.0.0", From ec9015d0050a4b694b8164397e0670a6a008aedf Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 7 May 2015 20:13:34 -0700 Subject: [PATCH 244/250] [ReactNative] Disable event deduping --- React/Base/RCTBridge.m | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 6e839ed0504a91..c98e3648b56b8b 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -1332,48 +1332,6 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg return; } - /** - * Event deduping - * - * Right now we make a lot of assumptions about the arguments structure - * so just iterate if it's a `callFunctionReturnFlushedQueue()` - */ - if ([method isEqualToString:@"callFunctionReturnFlushedQueue"]) { - NSString *moduleName = RCTLocalModuleNames[[args[0] integerValue]]; - /** - * Keep going if it any event emmiter, e.g. RCT(Device|NativeApp)?EventEmitter - */ - if ([moduleName hasSuffix:@"EventEmitter"]) { - for (NSDictionary *call in [strongSelf->_scheduledCalls copy]) { - NSArray *callArgs = call[@"args"]; - /** - * If it's the same module && method call on the bridge && - * the same EventEmitter module && method - */ - if ( - [call[@"module"] isEqualToString:module] && - [call[@"method"] isEqualToString:method] && - [callArgs[0] isEqual:args[0]] && - [callArgs[1] isEqual:args[1]] - ) { - /** - * args[2] contains the actual arguments for the event call, where - * args[2][0] is the target for RCTEventEmitter or the eventName - * for the other EventEmitters - * if RCTEventEmitter we need to compare args[2][1] that will be - * the eventName - */ - if ( - [args[2][0] isEqual:callArgs[2][0]] && - (![moduleName isEqualToString:@"RCTEventEmitter"] || [args[2][1] isEqual:callArgs[2][1]]) - ) { - [strongSelf->_scheduledCalls removeObject:call]; - } - } - } - } - } - id call = @{ @"module": module, @"method": method, From 5e51fac8d58021b5969f5a12b274c3bc79277752 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Fri, 8 May 2015 08:26:29 -0700 Subject: [PATCH 245/250] [react-native] In TouchableOpacity, access .props on element, not component --- Libraries/Components/Touchable/TouchableOpacity.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 5c673947f9535c..8d93cd8416bf4c 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -106,10 +106,8 @@ var TouchableOpacity = React.createClass({ }, touchableHandleActivePressOut: function() { - var childStyle = ( - this.refs[CHILD_REF].props && - flattenStyle(this.refs[CHILD_REF].props.style) - ) || {}; + var child = onlyChild(this.props.children); + var childStyle = flattenStyle(child.props.style) || {}; this.setOpacityTo(childStyle.opacity === undefined ? 1 : childStyle.opacity); this.props.onPressOut && this.props.onPressOut(); }, From 4bd07f9b5e99d9ace512b685ea6c6a83b77b801b Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 8 May 2015 11:04:22 -0700 Subject: [PATCH 246/250] Fix flow errors --- .flowconfig | 6 ++++++ Examples/SampleApp/_flowconfig | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/.flowconfig b/.flowconfig index 054b0d01d80f30..aa31b812a515a6 100644 --- a/.flowconfig +++ b/.flowconfig @@ -15,6 +15,12 @@ .*/node_modules/react-tools/src/core/ReactInstanceHandles.js .*/node_modules/react-tools/src/event/EventPropagators.js +# Ignore commoner tests +.*/node_modules/react-tools/node_modules/commoner/test/.* + +# See https://github.com/facebook/flow/issues/442 +.*/react-tools/node_modules/commoner/lib/reader.js + # Ignore jest .*/react-native/node_modules/jest-cli/.* diff --git a/Examples/SampleApp/_flowconfig b/Examples/SampleApp/_flowconfig index 1082fb195b01e3..9d4fb361444f83 100644 --- a/Examples/SampleApp/_flowconfig +++ b/Examples/SampleApp/_flowconfig @@ -15,6 +15,12 @@ .*/node_modules/react-tools/src/core/ReactInstanceHandles.js .*/node_modules/react-tools/src/event/EventPropagators.js +# Ignore commoner tests +.*/node_modules/react-tools/node_modules/commoner/test/.* + +# See https://github.com/facebook/flow/issues/442 +.*/react-tools/node_modules/commoner/lib/reader.js + # Ignore jest .*/react-native/node_modules/jest-cli/.* From 9ff6c0e36b51d4677cacf2d60ecbf09d819fd71a Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 8 May 2015 11:31:02 -0700 Subject: [PATCH 247/250] Upgrade SampleApp LaunchScreen.xib --- Examples/SampleApp/iOS/Base.lproj/LaunchScreen.xib | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/SampleApp/iOS/Base.lproj/LaunchScreen.xib b/Examples/SampleApp/iOS/Base.lproj/LaunchScreen.xib index 73cc9d07c74a8e..3b8cd49b8820a0 100644 --- a/Examples/SampleApp/iOS/Base.lproj/LaunchScreen.xib +++ b/Examples/SampleApp/iOS/Base.lproj/LaunchScreen.xib @@ -1,8 +1,8 @@ - + - + From 41612f37b0c31320b025043df9e954df540db171 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Fri, 8 May 2015 13:37:14 -0700 Subject: [PATCH 248/250] 0.4.2 --- React.podspec | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/React.podspec b/React.podspec index ce7083a6ac4dbe..7cb6c1c1f8ffc5 100644 --- a/React.podspec +++ b/React.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "React" - s.version = "0.4.1" + s.version = "0.4.2" s.summary = "Build high quality mobile apps using React." s.description = <<-DESC React Native apps are built using the React JS diff --git a/package.json b/package.json index 61735be6422bd0..4b76d688abfa21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native", - "version": "0.4.1", + "version": "0.4.2", "description": "A framework for building native apps using React", "repository": { "type": "git", From 38c5be59b841fb1c9a1ca96d598e22b49267eb0b Mon Sep 17 00:00:00 2001 From: Zhao Han Date: Sat, 9 May 2015 19:05:33 -0500 Subject: [PATCH 249/250] Fix issues#1223 --- website/layout/AutodocsLayout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/layout/AutodocsLayout.js b/website/layout/AutodocsLayout.js index ed583372e46c26..b9bef328c7c2c7 100644 --- a/website/layout/AutodocsLayout.js +++ b/website/layout/AutodocsLayout.js @@ -88,7 +88,7 @@ var ComponentDoc = React.createClass({ return ( ); From 120c32c52cd606214e5d497de40ff2454657b4f7 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Sun, 10 May 2015 10:17:21 -0700 Subject: [PATCH 250/250] [Docs] Don't show copyright/flow on APIs without docblock --- website/jsdocs/jsdocs.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/website/jsdocs/jsdocs.js b/website/jsdocs/jsdocs.js index 3f7cb953e1008e..086ad296683e66 100644 --- a/website/jsdocs/jsdocs.js +++ b/website/jsdocs/jsdocs.js @@ -105,10 +105,20 @@ function getFileDocBlock(commentsForFile) { commentsForFile.some(function(comment, i) { if (comment.loc.start.line === 1) { var lines = comment.value.split('\n'); + var inCopyrightBlock = false; var filteredLines = lines.filter(function(line) { - var hasCopyright = !!line.match(/^\s*\*\s+Copyright/); + if (!!line.match(/^\s*\*\s+Copyright \(c\)/)) { + inCopyrightBlock = true; + } + var hasProvides = !!line.match(/^\s*\*\s+@provides/); - return !hasCopyright && !hasProvides; + var hasFlow = !!line.match(/^\s*\*\s+@flow/); + + if (hasFlow || hasProvides) { + inCopyrightBlock = false; + } + + return !inCopyrightBlock && !hasFlow && !hasProvides; }); docblock = filteredLines.join('\n'); return true;