diff --git a/.ado/templates/e2e-test-job.yml b/.ado/templates/e2e-test-job.yml index 2dfe9f642a6..7f073952b61 100644 --- a/.ado/templates/e2e-test-job.yml +++ b/.ado/templates/e2e-test-job.yml @@ -91,6 +91,13 @@ jobs: script: yarn run e2etest workingDirectory: packages/E2ETest + - task: CopyFiles@2 + displayName: Copy test report + inputs: + sourceFolder: $(Build.SourcesDirectory)\packages\E2ETest\reports + targetFolder: $(Build.StagingDirectory)/ReactUWPTestAppTreeDump/reports + condition: succeededOrFailed() + - task: CopyFiles@2 displayName: Copy tree dump output files inputs: diff --git a/.ado/windows-vs-pr.yml b/.ado/windows-vs-pr.yml index 3a9f922b1bd..5bdccb68324 100644 --- a/.ado/windows-vs-pr.yml +++ b/.ado/windows-vs-pr.yml @@ -65,6 +65,13 @@ jobs: clean: false submodules: false + - task: PowerShell@2 + displayName: "Check if this environment meets the development dependencies" + inputs: + targetType: filePath + filePath: $(Build.SourcesDirectory)\vnext\Scripts\rnw-dependencies.ps1 + arguments: -NoPrompt + - template: templates/build-rnw.yml parameters: yarnBuildCmd: build diff --git a/change/react-native-windows-2020-05-11-17-22-16-rnw-dependencies.json b/change/react-native-windows-2020-05-11-17-22-16-rnw-dependencies.json new file mode 100644 index 00000000000..c4d7af43b9f --- /dev/null +++ b/change/react-native-windows-2020-05-11-17-22-16-rnw-dependencies.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "add rnw-dependencies.ps1", + "packageName": "react-native-windows", + "email": "asklar@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-12T00:22:16.000Z" +} diff --git a/change/react-native-windows-2020-05-12-11-18-16-MS_UIQueue.json b/change/react-native-windows-2020-05-12-11-18-16-MS_UIQueue.json new file mode 100644 index 00000000000..aeec541b06c --- /dev/null +++ b/change/react-native-windows-2020-05-12-11-18-16-MS_UIQueue.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Use DispatcherQueue instead of CoreDispatcher for Mso::DispatchQueue", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-12T18:18:15.956Z" +} diff --git a/change/react-native-windows-2020-05-12-18-57-59-UnsafePropertyId.json b/change/react-native-windows-2020-05-12-18-57-59-UnsafePropertyId.json new file mode 100644 index 00000000000..2841e2d184d --- /dev/null +++ b/change/react-native-windows-2020-05-12-18-57-59-UnsafePropertyId.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Allow storing non-WinRT types in ReactPropertyBag", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-13T01:57:59.370Z" +} diff --git a/change/react-native-windows-2020-05-13-12-47-43-tabfocus-fix.json b/change/react-native-windows-2020-05-13-12-47-43-tabfocus-fix.json new file mode 100644 index 00000000000..3832af20367 --- /dev/null +++ b/change/react-native-windows-2020-05-13-12-47-43-tabfocus-fix.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "revert dfc57fcf2504f57baab20f550b36a618eaa99e56", + "packageName": "react-native-windows", + "email": "kmelmon@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-13T19:47:43.325Z" +} diff --git a/change/react-native-windows-2020-05-13-14-16-07-rnw-dep-dontexit.json b/change/react-native-windows-2020-05-13-14-16-07-rnw-dep-dontexit.json new file mode 100644 index 00000000000..bf1bca20445 --- /dev/null +++ b/change/react-native-windows-2020-05-13-14-16-07-rnw-dep-dontexit.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "don't exit the powershell session and pause for keyboard input if interactive", + "packageName": "react-native-windows", + "email": "asklar@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-13T21:16:07.200Z" +} diff --git a/docs/building-rnw.md b/docs/building-rnw.md index 3dd61b188ae..7b09e5cbbbf 100644 --- a/docs/building-rnw.md +++ b/docs/building-rnw.md @@ -22,7 +22,7 @@ Note that react-native-windows is a monorepo and relies on monorepo tools like y There are two ways to run the app. In a fully managed easy way, or by manually running all the required steps: ## Automatic -The playground app can be run in a completely automatic way by using `react-native run-windows`. +The playground app can be run in a completely automatic way by using `react-native run-windows --sln windows\playground.sln`. If you haven't already, install the react-native-cli (One time only!) ```cmd @@ -33,7 +33,7 @@ Then ```cmd cd packages\playground -react-native run-windows +react-native run-windows --sln windows\playground.sln ``` ## Manual diff --git a/docs/e2e-testing.md b/docs/e2e-testing.md index 83407a939a9..4fc59c96901 100644 --- a/docs/e2e-testing.md +++ b/docs/e2e-testing.md @@ -436,3 +436,72 @@ this.submitButton.click(); You can easily to use By(string) to locate a element which associated with testID in the app. It's recommended to define a `get` for each locator like above. + +## E2E Tests and masters + +E2E tests can be summarized as follows: +- they are tests that run the ReactUWPTestApp +- use UI Automation to navigate between pages, query the state of elements, click on them, etc. +- the ReactUWPTestApp has code to produce a dump of the its own visual tree ("tree dump output") and compares it with a checked in copy ("tree dump masters") to make sure nothing has regressed. The tree dumps are produced in Json format (there is also an option to produce them in a custom text format of key=value, but that is deprecated now). + +So you've added or updated some tests: great! you get a cookie*. But now you probably need to update the masters, or the tests will fail and break the CI. + +\* void where prohibited, prizes and participation may vary. + +![testFail](img/e2e-testfail.png) + +The best way to do this is by letting the CI run and fail, then downloading the generated tree dump output files, and comparing to the masters. Make sure the differences are expected, copy over them and check them in. The reason is that the masters will include things like the size elements rendered at, which can be dependent on DPI, scale factor, resolution, and in some cases (due to bugs) even differ based on bitness (see #4628). + +When an output doesn't match its master, a file with `.err` extension will be produced under the `TreeDump` folder in the `ReactUWPTestAppTreeDump` artifact. The content of the `.err` file will usually just say: + +```txt +Tree dump file does not match master at C:\Program Files\WindowsApps\ReactUWPTestApp_1.0.0.0_x64__kc2bncckyf4ap\Assets\TreeDump\masters\ControlStyleRoundBorder.json - See output at C:\Users\VssAdministrator\AppData\Local\Packages\ReactUWPTestApp_kc2bncckyf4ap\LocalState\TreeDump\ControlStyleRoundBorder.json +``` + +![Errors](img/e2e-errors.png) + +Find the corresponding `.json` file in that folder and compare it to its master. The masters live in [e2etest\windows\ReactUWPTestApp\Assets\TreeDump\masters](https://github.com/microsoft/react-native-windows/tree/master/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters). + +Sometimes you'll have an element in your test that produces output that should not be used for comparison. You can manually edit the generated json and set the output that you want to ignore to the `` value: + +```json +... +"Windows.UI.Xaml.Button": +{ + "Text": "", + ... +} +... +``` + +## run_wdio + +WDIO is not really built to be run within Azure DevOps, so I wrote a utility called `run_wdio` to adapt it to something that can run in ADO. +It can be found in [\packages\e2etest\run_wdio.js](https://github.com/microsoft/react-native-windows/blob/master/packages/E2ETest/run_wdio.js) +Its main features (which WDIO lacks) are: + +* reports success/failure to ADO so that test failures will break the CI +* supports test selection on the command line +* supports test metadata and filtering (e.g. you can mark a test as "do not run in the lab") +* prints out the list of tests and test cases that failed in the CI console + + +## Debugging E2E Tests in CI +If you have access to the AzureDevOps pipeline you'll be able to see test failures and debug crashes. +Here are the artifacts that are produced during the build: +- error screenshots of the app when a test failed +- test run XML - this contains some information like the name of the wdio test that failed and the JS stack +- tree dump outputs - you can compare these to the masters to see if there is a the difference responsible for the test failing. +- crash dumps of the e2e test app (ReactUWPTestApp) + +You can access these by going to the AzureDevOps run for your PR and clicking on the artifacts link: + +![Artifacts](img/e2e-artifacts.png) + +Then you can access crash dumps under the `ReactUWPTestAppTreeDump\CrashDumps` folder. +![CrashDumps](img/e2e-crashdumps.png) + +You can get the symbols from the `appxsym` (just download it and rename it to `.zip`): +![SymbolsPackage](img/e2e-syms.png) + + The `ReactUWPTestAppTreeDump` folder will also contain any tree dump outputs that were produced that did not match the masters. \ No newline at end of file diff --git a/docs/img/e2e-artifacts.png b/docs/img/e2e-artifacts.png new file mode 100644 index 00000000000..d722dad2543 Binary files /dev/null and b/docs/img/e2e-artifacts.png differ diff --git a/docs/img/e2e-crashdumps.png b/docs/img/e2e-crashdumps.png new file mode 100644 index 00000000000..3cb5ddf764c Binary files /dev/null and b/docs/img/e2e-crashdumps.png differ diff --git a/docs/img/e2e-errors.png b/docs/img/e2e-errors.png new file mode 100644 index 00000000000..6a49cbd1d5b Binary files /dev/null and b/docs/img/e2e-errors.png differ diff --git a/docs/img/e2e-syms.png b/docs/img/e2e-syms.png new file mode 100644 index 00000000000..aeb78a97b67 Binary files /dev/null and b/docs/img/e2e-syms.png differ diff --git a/docs/img/e2e-testfail.png b/docs/img/e2e-testfail.png new file mode 100644 index 00000000000..5ceadb196d2 Binary files /dev/null and b/docs/img/e2e-testfail.png differ diff --git a/packages/E2ETest/README.md b/packages/E2ETest/README.md index 0cf38b0e805..9d4b5ead9fe 100644 --- a/packages/E2ETest/README.md +++ b/packages/E2ETest/README.md @@ -1,3 +1,5 @@ # E2ETest project -This package is not published, and is just used to verify a standalone app \ No newline at end of file +This package is not published, and is just used to verify a standalone app + +For information on how to run and debug this project, see [e2e-testing](https://github.com/microsoft/react-native-windows/blob/master/docs/e2e-testing.md). \ No newline at end of file diff --git a/packages/E2ETest/app/ControlStyleTestPage.tsx b/packages/E2ETest/app/ControlStyleTestPage.tsx index 9e91fd263ba..f42b01b31a4 100644 --- a/packages/E2ETest/app/ControlStyleTestPage.tsx +++ b/packages/E2ETest/app/ControlStyleTestPage.tsx @@ -3,8 +3,7 @@ * Licensed under the MIT License. */ - // Bug:4596 Switch -import { /* Switch, */ CheckBox, TextInput, View, StyleSheet, Button } from 'react-native'; + import { Switch, CheckBox, TextInput, View, StyleSheet, Button } from 'react-native'; import { DatePicker, Picker } from 'react-native-windows'; import React, { useState } from 'react'; import { SHOWBORDER_ON_CONTROLSTYLE, TREE_DUMP_RESULT } from './Consts'; @@ -58,10 +57,8 @@ export function ControlStyleTestPage() { { - /* - // Bug:4596 Switch - */} + } 2) { specs = process.argv.splice(2).map(spec => spec + '.spec.ts'); } else { specs = fs.readdirSync(folder).filter(x => x.endsWith('.spec.ts')); } - specs = specs.map(spec => path.join(folder, spec)).filter(FilterSpec); + specs = specs.map(spec => path.join(folder, spec)).filter(filterSpec); return specs; } -let opts = SelectSpecs(specFolder); +let opts = selectSpecs(specFolder); console.log(`Selected tests: ${opts}`); -function OverrideHyperV() { +function ensureRunningInHyperV() { const baseboardMfr = child_process .execSync('powershell.exe (gwmi Win32_BaseBoard).Manufacturer') .toString() @@ -69,26 +70,41 @@ function OverrideHyperV() { } } -OverrideHyperV(); - const Launcher = require('@wdio/cli').default; const wdio = new Launcher('wdio.conf.js', { specs: opts }); function parseLog(logfile) { const xmlString = fs.readFileSync(logfile); - let name; + let failures = {}; parser.parseString(xmlString, (err, res) => { if (!res.testsuites) { - name = 'something went wrong'; + console.error(`Something went wrong processing file ${logfile}`); } else { - const attr = res.testsuites.testsuite[0].ATTR; - if (attr.errors > 0 || attr.failures > 0) { - name = attr.name; + for (const testsuite of res.testsuites.testsuite) { + const attr = testsuite.ATTR; + + if (attr.errors > 0 || attr.failures > 0) { + const name = attr.name; + failures[name] = {}; + + for (const testcase of testsuite.testcase) { + if (testcase.error && testcase.error[0].ATTR) { + failures[name].testcase = testcase.ATTR.name; + failures[name].error = testcase.error[0].ATTR.message; + const systemErr = testcase['system-err'][0]; + const stack = systemErr.substr( + systemErr.indexOf('\n at ') + 1 + ); + failures[name].stack = stack; + } + } + } } } }); - return name; + + return failures; } function parseLogs() { @@ -96,24 +112,57 @@ function parseLogs() { const logs = fs.readdirSync(reportsDir).filter(x => x.endsWith('.log')); const names = logs .map(x => parseLog(path.join(reportsDir, x))) - .filter(x => x != null); + .filter(x => x != null && x != ''); return names; } -function Process(code) { +function printFailedTests(ft) { + for (const key in ft) { + console.log(chalk.redBright(key)); + console.log( + ' ', + chalk.underline('testcase'), + chalk.bold(ft[key].testcase) + ); + console.log(' ', chalk.underline('error'), chalk.bold(ft[key].error)); + console.log(' ', chalk.underline('stack')); + console.log(ft[key].stack); + } +} + +function doProcess(code) { const failedTests = parseLogs(); - for (let i = 0; i < failedTests.length; i++) { - console.log(`Failed test: ${failedTests[i]}`); + for (const failedTest of failedTests) { + console.log('Failed tests: '); + printFailedTests(failedTest); } process.exit(code); } -wdio.run().then( - code => { - Process(code); - }, - error => { - console.error('Launcher failed to start the test', error.stacktrace); - process.exit(1); - } -); +function runWdio() { + ensureRunningInHyperV(); + + wdio.run().then( + code => { + try { + doProcess(code); + } catch (e) { + console.log(e); + // any exception that isn't handled is an error + process.exit(3); + } + }, + error => { + console.error('Launcher failed to start the test', error.stacktrace); + process.exit(1); + } + ); +} + +try { + runWdio(); +} catch (e) { + console.log(e); + // any exception that isn't handled is an error + process.exit(2); +} diff --git a/packages/E2ETest/windows/ReactUWPTestApp.sln b/packages/E2ETest/windows/ReactUWPTestApp.sln index fd7c8a089aa..d632b950855 100644 --- a/packages/E2ETest/windows/ReactUWPTestApp.sln +++ b/packages/E2ETest/windows/ReactUWPTestApp.sln @@ -43,6 +43,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.ReactNative.Manag EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\..\vnext\ReactWindowsCore\ReactWindowsCore.vcxitems*{11c084a3-a57c-4296-a679-cac17b603144}*SharedItemsImports = 4 ..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4 ..\..\..\vnext\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 ..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 diff --git a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRegularBorder.json b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRegularBorder.json index 818fa573d29..b6b323e4662 100644 --- a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRegularBorder.json +++ b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRegularBorder.json @@ -6,14 +6,237 @@ "Clip": null, "CornerRadius": "0,0,0,0", "FlowDirection": "LeftToRight", - "Height": 300, + "Height": 360, "HorizontalAlignment": "Stretch", "Margin": "0,0,0,0", - "RenderSize": [800, 300], + "RenderSize": [800, 360], "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 800, + "AutomationId": "ControlStyleView", "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.ToggleSwitch", + "Background": "#33E1E1E1", + "BorderBrush": "#55FF00FF", + "BorderThickness": "1,1,1,1", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#FFFFFFFF", + "Height": 50, + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Padding": "0,0,0,0", + "RenderSize": [51, 50], + "VerticalAlignment": "Center", + "Visibility": "Visible", + "Width": 51, + "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": "#33E1E1E1", + "BorderBrush": "#55FF00FF", + "BorderThickness": "1,1,1,1", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Padding": "0,0,0,0", + "RenderSize": [51, 50], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.ContentPresenter", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#66000000", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "HeaderContentPresenter", + "Padding": "0,0,0,0", + "RenderSize": [0, 0], + "VerticalAlignment": "Top", + "Visibility": "Collapsed" + }, + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Padding": "0,0,0,0", + "RenderSize": [154, 32], + "VerticalAlignment": "Top", + "Visibility": "Visible", + "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": "#00FFFFFF", + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Stretch", + "Margin": "0,5,0,5", + "Name": "SwitchAreaGrid", + "Padding": "0,0,0,0", + "RenderSize": [52, 22], + "VerticalAlignment": "Stretch", + "Visibility": "Visible" + }, + { + "XamlType": "Windows.UI.Xaml.Controls.ContentPresenter", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#FFFFFFFF", + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Name": "OffContentPresenter", + "Padding": "0,0,0,0", + "RenderSize": [0, 0], + "VerticalAlignment": "Center", + "Visibility": "Visible" + }, + { + "XamlType": "Windows.UI.Xaml.Controls.ContentPresenter", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#FFFFFFFF", + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Name": "OnContentPresenter", + "Padding": "0,0,0,0", + "RenderSize": [0, 0], + "VerticalAlignment": "Center", + "Visibility": "Visible" + }, + { + "XamlType": "Windows.UI.Xaml.Shapes.Rectangle", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 20, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "OuterBorder", + "RenderSize": [40, 20], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 40 + }, + { + "XamlType": "Windows.UI.Xaml.Shapes.Rectangle", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 20, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchKnobBounds", + "RenderSize": [40, 20], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 40 + }, + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Height": 20, + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Name": "SwitchKnob", + "Padding": "0,0,0,0", + "RenderSize": [20, 20], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 20, + "children": [ + { + "XamlType": "Windows.UI.Xaml.Shapes.Ellipse", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 10, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchKnobOn", + "RenderSize": [10, 10], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 10 + }, + { + "XamlType": "Windows.UI.Xaml.Shapes.Ellipse", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 10, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchKnobOff", + "RenderSize": [10, 10], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 10 + } + ] + }, + { + "XamlType": "Windows.UI.Xaml.Controls.Primitives.Thumb", + "Background": "#33000000", + "BorderBrush": "#00FFFFFF", + "BorderThickness": "1,1,1,1", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#FFFFFFFF", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchThumb", + "Padding": "0,0,0,0", + "RenderSize": [52, 32], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "children": [ + { + "XamlType": "Windows.UI.Xaml.Shapes.Rectangle", + "Clip": null, + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "RenderSize": [52, 32], + "VerticalAlignment": "Stretch", + "Visibility": "Visible" + } + ] + } + ] + } + ] + } + ] + }, { "XamlType": "Windows.UI.Xaml.Controls.CheckBox", "Background": "#33E1E1E1", @@ -113,6 +336,7 @@ "Margin": "0,0,0,0", "Padding": "0,0,0,0", "RenderSize": [20, 16], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Visible" } @@ -157,6 +381,7 @@ "Margin": "0,0,0,0", "Padding": "10,10,10,10", "RenderSize": [800, 50], + "Text": "", "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 800, @@ -283,56 +508,6 @@ "RenderSize": [798, 48], "VerticalAlignment": "Stretch", "Visibility": "Visible" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Right", - "Margin": "0,0,0,0", - "Name": "VerticalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "HorizontalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Border", - "Background": "#FFE6E6E6", - "BorderBrush": null, - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "0,0,0,0", - "FlowDirection": "LeftToRight", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "ScrollBarSeparator", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" } ] } @@ -350,6 +525,7 @@ "Name": "PlaceholderTextContentPresenter", "Padding": "10,10,10,10", "RenderSize": [798, 48], + "Text": "TextBox", "VerticalAlignment": "Stretch", "Visibility": "Visible" }, @@ -531,56 +707,6 @@ "RenderSize": [798, 48], "VerticalAlignment": "Stretch", "Visibility": "Visible" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Right", - "Margin": "0,0,0,0", - "Name": "VerticalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "HorizontalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Border", - "Background": "#FFE6E6E6", - "BorderBrush": null, - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "0,0,0,0", - "FlowDirection": "LeftToRight", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "ScrollBarSeparator", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" } ] } @@ -598,6 +724,7 @@ "Name": "PlaceholderTextContentPresenter", "Padding": "10,10,10,10", "RenderSize": [798, 48], + "Text": "Password", "VerticalAlignment": "Stretch", "Visibility": "Visible" }, @@ -699,6 +826,7 @@ "Name": "DateText", "Padding": "12,0,0,2", "RenderSize": [90, 21], + "Text": "select a date", "VerticalAlignment": "Center", "Visibility": "Visible" }, @@ -738,6 +866,7 @@ "Margin": "0,0,0,0", "Padding": "0,0,0,0", "RenderSize": [12, 12], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Visible" } @@ -780,6 +909,7 @@ "Margin": "0,0,0,0", "Padding": "12,5,0,7", "RenderSize": [800, 50], + "Text": "", "VerticalAlignment": "Top", "Visibility": "Visible", "Width": 800, @@ -859,6 +989,7 @@ "Name": "PlaceholderTextBlock", "Padding": "0,0,0,0", "RenderSize": [756, 19], + "Text": "", "VerticalAlignment": "Stretch", "Visibility": "Visible" } @@ -878,6 +1009,7 @@ "Name": "EditableText", "Padding": "11,5,32,6", "RenderSize": [0, 0], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Collapsed" }, @@ -934,6 +1066,7 @@ "Margin": "0,0,0,0", "Padding": "0,0,0,0", "RenderSize": [12, 12], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Visible" } diff --git a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRoundBorder.json b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRoundBorder.json index 3e26498c954..a236fcc7778 100644 --- a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRoundBorder.json +++ b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ControlStyleRoundBorder.json @@ -6,14 +6,237 @@ "Clip": null, "CornerRadius": "0,0,0,0", "FlowDirection": "LeftToRight", - "Height": 300, + "Height": 360, "HorizontalAlignment": "Stretch", "Margin": "0,0,0,0", - "RenderSize": [800, 300], + "RenderSize": [800, 360], "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 800, + "AutomationId": "ControlStyleView", "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.ToggleSwitch", + "Background": "#33000000", + "BorderBrush": "#5500FF00", + "BorderThickness": "10,10,10,10", + "Clip": null, + "CornerRadius": "10,10,10,10", + "FlowDirection": "LeftToRight", + "Foreground": "#FF000000", + "Height": 50, + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Padding": "0,0,0,0", + "RenderSize": [51, 50], + "VerticalAlignment": "Center", + "Visibility": "Visible", + "Width": 51, + "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": "#33000000", + "BorderBrush": "#5500FF00", + "BorderThickness": "10,10,10,10", + "Clip": null, + "CornerRadius": "10,10,10,10", + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Padding": "0,0,0,0", + "RenderSize": [51, 50], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.ContentPresenter", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#66000000", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "HeaderContentPresenter", + "Padding": "0,0,0,0", + "RenderSize": [0, 0], + "VerticalAlignment": "Top", + "Visibility": "Collapsed" + }, + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Padding": "0,0,0,0", + "RenderSize": [154, 32], + "VerticalAlignment": "Top", + "Visibility": "Visible", + "children": [ + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": "#00FFFFFF", + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Stretch", + "Margin": "0,5,0,5", + "Name": "SwitchAreaGrid", + "Padding": "0,0,0,0", + "RenderSize": [52, 22], + "VerticalAlignment": "Stretch", + "Visibility": "Visible" + }, + { + "XamlType": "Windows.UI.Xaml.Controls.ContentPresenter", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#66000000", + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Name": "OffContentPresenter", + "Padding": "0,0,0,0", + "RenderSize": [0, 0], + "VerticalAlignment": "Center", + "Visibility": "Visible" + }, + { + "XamlType": "Windows.UI.Xaml.Controls.ContentPresenter", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#66000000", + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Name": "OnContentPresenter", + "Padding": "0,0,0,0", + "RenderSize": [0, 0], + "VerticalAlignment": "Center", + "Visibility": "Visible" + }, + { + "XamlType": "Windows.UI.Xaml.Shapes.Rectangle", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 20, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "OuterBorder", + "RenderSize": [40, 20], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 40 + }, + { + "XamlType": "Windows.UI.Xaml.Shapes.Rectangle", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 20, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchKnobBounds", + "RenderSize": [40, 20], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 40 + }, + { + "XamlType": "Windows.UI.Xaml.Controls.Grid", + "Background": null, + "BorderBrush": null, + "BorderThickness": "0,0,0,0", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Height": 20, + "HorizontalAlignment": "Left", + "Margin": "0,0,0,0", + "Name": "SwitchKnob", + "Padding": "0,0,0,0", + "RenderSize": [20, 20], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 20, + "children": [ + { + "XamlType": "Windows.UI.Xaml.Shapes.Ellipse", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 10, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchKnobOn", + "RenderSize": [10, 10], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 10 + }, + { + "XamlType": "Windows.UI.Xaml.Shapes.Ellipse", + "Clip": null, + "FlowDirection": "LeftToRight", + "Height": 10, + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchKnobOff", + "RenderSize": [10, 10], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "Width": 10 + } + ] + }, + { + "XamlType": "Windows.UI.Xaml.Controls.Primitives.Thumb", + "Background": "#33000000", + "BorderBrush": "#00FFFFFF", + "BorderThickness": "1,1,1,1", + "Clip": null, + "CornerRadius": "0,0,0,0", + "FlowDirection": "LeftToRight", + "Foreground": "#FF000000", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "Name": "SwitchThumb", + "Padding": "0,0,0,0", + "RenderSize": [52, 32], + "VerticalAlignment": "Stretch", + "Visibility": "Visible", + "children": [ + { + "XamlType": "Windows.UI.Xaml.Shapes.Rectangle", + "Clip": null, + "FlowDirection": "LeftToRight", + "HorizontalAlignment": "Stretch", + "Margin": "0,0,0,0", + "RenderSize": [52, 32], + "VerticalAlignment": "Stretch", + "Visibility": "Visible" + } + ] + } + ] + } + ] + } + ] + }, { "XamlType": "Windows.UI.Xaml.Controls.CheckBox", "Background": "#33000000", @@ -113,6 +336,7 @@ "Margin": "0,0,0,0", "Padding": "0,0,0,0", "RenderSize": [20, 16], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Visible" } @@ -157,6 +381,7 @@ "Margin": "0,0,0,0", "Padding": "10,10,10,10", "RenderSize": [800, 50], + "Text": "", "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 800, @@ -283,56 +508,6 @@ "RenderSize": [780, 30], "VerticalAlignment": "Stretch", "Visibility": "Visible" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Right", - "Margin": "0,0,0,0", - "Name": "VerticalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "HorizontalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Border", - "Background": "#FFE6E6E6", - "BorderBrush": null, - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "0,0,0,0", - "FlowDirection": "LeftToRight", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "ScrollBarSeparator", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" } ] } @@ -350,6 +525,7 @@ "Name": "PlaceholderTextContentPresenter", "Padding": "10,10,10,10", "RenderSize": [780, 39], + "Text": "TextBox", "VerticalAlignment": "Stretch", "Visibility": "Visible" }, @@ -531,56 +707,6 @@ "RenderSize": [780, 30], "VerticalAlignment": "Stretch", "Visibility": "Visible" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Right", - "Margin": "0,0,0,0", - "Name": "VerticalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Primitives.ScrollBar", - "Background": "#00FFFFFF", - "BorderBrush": "#00FFFFFF", - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "2,2,2,2", - "FlowDirection": "LeftToRight", - "Foreground": "#00FFFFFF", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "HorizontalScrollBar", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" - }, - { - "XamlType": "Windows.UI.Xaml.Controls.Border", - "Background": "#FFE6E6E6", - "BorderBrush": null, - "BorderThickness": "0,0,0,0", - "Clip": null, - "CornerRadius": "0,0,0,0", - "FlowDirection": "LeftToRight", - "HorizontalAlignment": "Stretch", - "Margin": "0,0,0,0", - "Name": "ScrollBarSeparator", - "Padding": "0,0,0,0", - "RenderSize": [0, 0], - "VerticalAlignment": "Stretch", - "Visibility": "Collapsed" } ] } @@ -598,6 +724,7 @@ "Name": "PlaceholderTextContentPresenter", "Padding": "10,10,10,10", "RenderSize": [780, 39], + "Text": "Password", "VerticalAlignment": "Stretch", "Visibility": "Visible" }, @@ -699,6 +826,7 @@ "Name": "DateText", "Padding": "12,0,0,2", "RenderSize": [90, 21], + "Text": "select a date", "VerticalAlignment": "Center", "Visibility": "Visible" }, @@ -738,6 +866,7 @@ "Margin": "0,0,0,0", "Padding": "0,0,0,0", "RenderSize": [12, 12], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Visible" } @@ -780,6 +909,7 @@ "Margin": "0,0,0,0", "Padding": "12,5,0,7", "RenderSize": [800, 50], + "Text": "", "VerticalAlignment": "Top", "Visibility": "Visible", "Width": 800, @@ -859,6 +989,7 @@ "Name": "PlaceholderTextBlock", "Padding": "0,0,0,0", "RenderSize": [756, 19], + "Text": "", "VerticalAlignment": "Stretch", "Visibility": "Visible" } @@ -878,6 +1009,7 @@ "Name": "EditableText", "Padding": "11,5,32,6", "RenderSize": [0, 0], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Collapsed" }, @@ -934,6 +1066,7 @@ "Margin": "0,0,0,0", "Padding": "0,0,0,0", "RenderSize": [12, 12], + "Text": "", "VerticalAlignment": "Center", "Visibility": "Visible" } diff --git a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageRTL.json b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageRTL.json index c00ab2049da..e6560044fe8 100644 --- a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageRTL.json +++ b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageRTL.json @@ -14,6 +14,7 @@ "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 500, + "AutomationId": "ImageContainer", "children": [ { "XamlType": "Microsoft.ReactNative.ViewPanel", diff --git a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithBorder.json b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithBorder.json index 753214da826..9c87e39efe5 100644 --- a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithBorder.json +++ b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithBorder.json @@ -14,6 +14,7 @@ "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 500, + "AutomationId": "ImageContainer", "children": [ { "XamlType": "Microsoft.ReactNative.ViewPanel", diff --git a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder-Subsequent.json b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder-Subsequent.json index c00ab2049da..e6560044fe8 100644 --- a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder-Subsequent.json +++ b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder-Subsequent.json @@ -14,6 +14,7 @@ "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 500, + "AutomationId": "ImageContainer", "children": [ { "XamlType": "Microsoft.ReactNative.ViewPanel", diff --git a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder.json b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder.json index 9406a02556a..9be2fa56439 100644 --- a/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder.json +++ b/packages/E2ETest/windows/ReactUWPTestApp/Assets/TreeDump/masters/ImageWithoutBorder.json @@ -13,6 +13,7 @@ "VerticalAlignment": "Stretch", "Visibility": "Visible", "Width": 500, + "AutomationId": "ImageContainer", "children": [ { "XamlType": "Windows.UI.Xaml.DependencyObject" diff --git a/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpControlViewManager.cs b/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpControlViewManager.cs index 1e12a33264b..fea206cd49f 100644 --- a/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpControlViewManager.cs +++ b/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpControlViewManager.cs @@ -5,11 +5,8 @@ using Microsoft.ReactNative.Managed; using System; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Threading.Tasks; using Windows.ApplicationModel.DataTransfer; -using Windows.Data.Json; using Windows.Storage; using Windows.UI.ViewManagement; using Windows.UI.Xaml; @@ -126,180 +123,25 @@ private async void dispatcherTimer_Tick(object sender, object e) m_timer.Stop(); if (VisualTreeHelper.GetParent(m_textBlock) != null) { - await MatchTreeDumpFromLayoutUpdateAsync(); - } - } - private DependencyObject FindChildWithMatchingUIAID(DependencyObject element) - { - string automationId = (string)element.GetValue(Windows.UI.Xaml.Automation.AutomationProperties.AutomationIdProperty); - if (automationId == m_uiaID) - { - return element; - } - int childrenCount = VisualTreeHelper.GetChildrenCount(element); - for (int i = 0; i < childrenCount; i++) - { - var result = FindChildWithMatchingUIAID(VisualTreeHelper.GetChild(element, i)); - if (result != null) - { - return result; - } - } - - return null; - } - - private async Task MatchTreeDumpFromLayoutUpdateAsync() - { - // First find root - DependencyObject current = m_textBlock; - DependencyObject parent = VisualTreeHelper.GetParent(current); - while (parent != null) - { - current = parent; - parent = VisualTreeHelper.GetParent(current); - } - - DependencyObject dumpRoot = current; - // if UIAID is passed in from test, find the matching child as the root to dump - if (m_uiaID != null) - { - var matchingNode = FindChildWithMatchingUIAID(current); - if (matchingNode != null) + var matchSuccessful = await TreeDumpHelper.MatchTreeDumpFromLayoutUpdateAsync(m_dumpID, m_uiaID, m_textBlock, m_additionalProperties, DumpTreeMode.Json, m_dumpExpectedText); + if (!matchSuccessful) { - dumpRoot = matchingNode; - } - } - - string dumpText = VisualTreeDumper.DumpTree(dumpRoot, m_textBlock /* exclude */ , m_additionalProperties, mode); - if (dumpText != m_dumpExpectedText) - { - await MatchDump(dumpText); - } - } - - private readonly DumpTreeMode mode = DumpTreeMode.Json; - - private async Task MatchDump(string dumpText) - { - StorageFile masterFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync($@"Assets\{GetMasterFile()}"); - if (m_dumpExpectedText == null) - { - try - { - m_dumpExpectedText = await FileIO.ReadTextAsync(masterFile); - StorageFolder storageFolder = ApplicationData.Current.LocalFolder; - string copyFileName = GetMasterFile(); - var copyDumpFile = await storageFolder.CreateFileAsync(copyFileName, CreationCollisionOption.ReplaceExisting); - await FileIO.WriteTextAsync(copyDumpFile, m_dumpExpectedText); - } - catch (IOException) - { - UpdateResult(false /*matchDump*/ , "Tree dump master file not found in testapp package!"); - } - } + StorageFile outFile = await storageFolder.GetFileAsync(TreeDumpHelper.GetOutputFile(m_dumpID)); + StorageFile masterFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync($@"Assets\{TreeDumpHelper.GetMasterFile(m_dumpID)}"); - if (!DumpsAreEqual(m_dumpExpectedText, dumpText)) - { - StorageFolder storageFolder = ApplicationData.Current.LocalFolder; - string fileName = GetOutputFile(); - try - { - StorageFile outFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting); - await FileIO.WriteTextAsync(outFile, dumpText); UpdateResult(false /*matchDump*/ , $"Tree dump file does not match master at {masterFile.Path} - See output at {outFile.Path}", GetInlines(masterFile, outFile, m_textBlock)); } - catch (IOException) + else { - UpdateResult(false /*matchDump*/ , "Can't write dump output file:" + fileName); + UpdateResult(true /*matchDump*/ , ""); } - } - else - { - UpdateResult(true /*matchDump*/ , ""); - } - } - - private bool DumpsAreEqual(string dumpExpectedText, string dumpText) - { - if (mode == DumpTreeMode.Default) - { - return dumpExpectedText == dumpText; - } - else - { - JsonValue expected = JsonValue.Parse(dumpExpectedText); - JsonValue actual = JsonValue.Parse(dumpText); - return JsonComparesEqual(expected, actual); - } - } - private bool JsonComparesEqual(IJsonValue expected, IJsonValue actual) - { - if (expected.ValueType != actual.ValueType) - { - return false; - } - switch (expected.ValueType) - { - case JsonValueType.String: - if (expected.GetString() == actual.GetString()) - { - return true; - } - else { Debug.WriteLine($"Expected {expected.GetString()} got {actual.GetString()}"); return false; } - case JsonValueType.Number: - return expected.GetNumber() == actual.GetNumber(); - case JsonValueType.Boolean: - return expected.GetBoolean() == actual.GetBoolean(); - case JsonValueType.Null: - return true; - case JsonValueType.Array: - { - var ea = expected.GetArray(); - var aa = actual.GetArray(); - if (ea.Count != aa.Count) { return false; } - for (uint i = 0; i < ea.Count; i++) - { - if (!JsonComparesEqual(ea[(int)i], aa[(int)i])) - { - return false; - } - } - return true; - } - case JsonValueType.Object: - { - var eo = expected.GetObject(); - var ao = actual.GetObject(); - if (eo.Keys.Count != ao.Keys.Count) { return false; } - foreach (var key in eo.Keys) - { - if (!JsonComparesEqual(eo.GetNamedValue(key), ao.GetNamedValue(key))) - { - return false; - } - } - return true; - } - default: - throw new ArgumentException(); } } - private string GetOutputFile() - { - return "TreeDump\\" + m_dumpID + (mode == DumpTreeMode.Json ? ".json" : ".out"); - } - - private string GetMasterFile() - { - return "TreeDump\\masters\\" + m_dumpID + (mode == DumpTreeMode.Json ? ".json" : ".txt"); - } - private static IList GetInlines(StorageFile masterFile, StorageFile outFile, UIElement anchor) { Hyperlink masterLink = new Hyperlink(); diff --git a/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpHelper.cs b/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpHelper.cs new file mode 100644 index 00000000000..ec75dde5154 --- /dev/null +++ b/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpHelper.cs @@ -0,0 +1,249 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Windows.Data.Json; +using Windows.Storage; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace TreeDumpLibrary +{ + public static class TreeDumpHelper + { + + private static DependencyObject FindChildWithMatchingUIAID(DependencyObject element, string uiaId) + { + string automationId = (string)element.GetValue(Windows.UI.Xaml.Automation.AutomationProperties.AutomationIdProperty); + if (automationId == uiaId) + { + return element; + } + int childrenCount = VisualTreeHelper.GetChildrenCount(element); + for (int i = 0; i < childrenCount; i++) + { + var result = FindChildWithMatchingUIAID(VisualTreeHelper.GetChild(element, i), uiaId); + if (result != null) + { + return result; + } + } + + return null; + } + + internal static async Task MatchTreeDumpFromLayoutUpdateAsync(string dumpID, string uiaId, TextBlock textBlock, IList additionalProperties, DumpTreeMode mode, string dumpExpectedText) + { + // First find root + DependencyObject current = textBlock; + DependencyObject parent = VisualTreeHelper.GetParent(current); + while (parent != null) + { + current = parent; + parent = VisualTreeHelper.GetParent(current); + } + + DependencyObject dumpRoot = current; + // if UIAID is passed in from test, find the matching child as the root to dump + if (uiaId != null) + { + var matchingNode = FindChildWithMatchingUIAID(current, uiaId); + if (matchingNode != null) + { + dumpRoot = matchingNode; + } + } + + string dumpText = VisualTreeDumper.DumpTree(dumpRoot, textBlock /* exclude */, additionalProperties, mode); + if (dumpText != dumpExpectedText) + { + return await MatchDump(dumpText, dumpID); + } + return true; + } + + + internal static async Task MatchDump(string outputJson, string dumpID) + { + Debug.WriteLine($"master file = {Windows.ApplicationModel.Package.Current.InstalledLocation.Path}\\Assets\\{GetMasterFile(dumpID)}"); + StorageFolder storageFolder = ApplicationData.Current.LocalFolder; + string fileName = GetOutputFile(dumpID); + Debug.WriteLine($"output file = {storageFolder.Path + "\\" + fileName}"); + + StorageFile outFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting); + await FileIO.WriteTextAsync(outFile, outputJson); + + StorageFile masterFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync($@"Assets\{GetMasterFile(dumpID)}"); + + string masterJson = await FileIO.ReadTextAsync(masterFile); + + if (!DumpsAreEqual(masterJson, outputJson)) + { + return false; + } + else + { + return true; + } + } + + public static bool DumpsAreEqual(string dumpExpectedText, string dumpText) + { + JsonValue expected = JsonValue.Parse(dumpExpectedText); + JsonValue actual = JsonValue.Parse(dumpText); + return JsonComparesEqual(expected, actual, "root"); + } + + static readonly double epsilon = 1.0; + + private static bool JsonComparesEqual(IJsonValue expected, IJsonValue actual, string keyName) + { + const string Anything = ""; + if (expected.ValueType == JsonValueType.String && Regex.Replace(expected.GetString(), @"\p{C}", "") == Anything) + { + Debug.WriteLine($"Skipping ignored value: {actual.ValueType} {actual}"); + return true; + } + //Debug.WriteLine($"keyname: {keyName} {expected.ValueType} {actual.ValueType}"); + if (expected.ValueType != actual.ValueType) + { + Debug.WriteLine($"Expected {expected} got {actual}"); + return false; + } + switch (expected.ValueType) + { + case JsonValueType.String: + if (expected.GetString() != actual.GetString()) + { + Debug.WriteLine($"string:Expected {expected.GetString()} got {actual.GetString()}"); + return false; + } + return true; + case JsonValueType.Number: + if (Math.Abs(expected.GetNumber() - actual.GetNumber()) > epsilon) + { + Debug.WriteLine($"number: {keyName} {expected.GetNumber()} {actual.GetNumber()}"); + return false; + } + return true; + case JsonValueType.Boolean: + return expected.GetBoolean() == actual.GetBoolean(); + case JsonValueType.Null: + return true; + case JsonValueType.Array: + { + var ea = expected.GetArray(); + var aa = actual.GetArray(); + if (!JsonCompareArray(ea, aa)) + { + Debug.WriteLine("in key " + keyName); + return false; + } + return true; + } + case JsonValueType.Object: + { + if (!JsonCompareObject(expected.GetObject(), actual.GetObject())) + { + Debug.WriteLine("in key " + keyName); + return false; + } + return true; + } + default: + throw new ArgumentException(); + } + } + + private static bool JsonCompareArray(JsonArray ea, JsonArray aa) + { + var efiltered = ea.Where(x => IsNonCollapsed(x)).ToArray(); + var afiltered = aa.Where(x => IsNonCollapsed(x)).ToArray(); + + if (efiltered.Length != afiltered.Length) + { + Debug.WriteLine($"Array count expected {ea.Count} got {aa.Count}"); + return false; + } + for (int i = 0; i < efiltered.Length; i++) + { + var _e = efiltered[i]; + var _a = afiltered[i]; + if (!JsonComparesEqual(_e, _a, "array element")) + { + Debug.WriteLine($"Array element {i} expected {_e.ValueType} got {_a.ValueType}"); + return false; + } + } + return true; + } + + private const string visibilityProperty = "Visibility"; + private const string visibilityPropertyVisible = "Visible"; + + private static bool IsNonCollapsed(IJsonValue x) + { + return x.ValueType != JsonValueType.Object || + !x.GetObject().ContainsKey(visibilityProperty) || + x.GetObject().GetNamedString(visibilityProperty) == visibilityPropertyVisible; + } + + private static bool JsonCompareObject(JsonObject eo, JsonObject ao) + { + var evisible = true; + if (eo.Keys.Contains(visibilityProperty)) + { + evisible = eo[visibilityProperty].GetString() == visibilityPropertyVisible; + eo.Remove(visibilityProperty); + } + var avisible = true; + if (ao.Keys.Contains(visibilityProperty)) + { + avisible = ao[visibilityProperty].GetString() == visibilityPropertyVisible; + ao.Remove(visibilityProperty); + } + if (avisible != evisible) { return false; } + if (avisible == false) + { + // both are collapsed (or nonexistent) so don't compare children + ao.Remove("children"); + eo.Remove("children"); + } + + if (eo.Keys.Count != ao.Keys.Count) + { + Debug.WriteLine($"Expected {eo.Keys.Count} but got {ao.Keys.Count}"); + Debug.WriteLine(string.Join(", ", eo.Keys)); + Debug.WriteLine(string.Join(", ", ao.Keys)); + return false; + } + foreach (var key in eo.Keys) + { + JsonValue evalue = eo.GetNamedValue(key); + JsonValue avalue = ao.GetNamedValue(key); + if (!JsonComparesEqual(evalue, avalue, key)) + { + Debug.WriteLine($"Property {key} compared differently {evalue.ValueType} {avalue.ValueType}: expected {evalue} got {avalue}"); + return false; + } + } + return true; + } + + public static string GetOutputFile(string dumpID) + { + return "TreeDump\\" + dumpID + ".json"; + } + + public static string GetMasterFile(string dumpID) + { + return "TreeDump\\masters\\" + dumpID + ".json"; + } + } +} diff --git a/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpLibrary.csproj b/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpLibrary.csproj index e8ca1d3675b..6d7628538f4 100644 --- a/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpLibrary.csproj +++ b/packages/E2ETest/windows/TreeDumpLibrary/TreeDumpLibrary.csproj @@ -1,6 +1,5 @@  - + Debug @@ -112,6 +111,7 @@ + diff --git a/packages/E2ETest/windows/TreeDumpLibrary/VisualTreeDumper.cs b/packages/E2ETest/windows/TreeDumpLibrary/VisualTreeDumper.cs index 32a43aa7bd2..fd95352ebb4 100644 --- a/packages/E2ETest/windows/TreeDumpLibrary/VisualTreeDumper.cs +++ b/packages/E2ETest/windows/TreeDumpLibrary/VisualTreeDumper.cs @@ -3,10 +3,15 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Windows.Data.Json; using Windows.Foundation; using Windows.UI.Xaml; +using Windows.UI.Xaml.Automation; using Windows.UI.Xaml.Media; namespace TreeDumpLibrary @@ -51,12 +56,15 @@ public override string ToString() public bool ShouldVisitPropertiesForNode(DependencyObject node) { - return (node as UIElement) != null; + var fe = node as FrameworkElement; + string[] excludedNames = new string[] { "VerticalScrollBar", "HorizontalScrollBar", "ScrollBarSeparator" }; + if (fe != null && excludedNames.Contains(fe.Name)) { return false; } + return true; } - public bool ShouldVisitProperty(PropertyInfo propertyInfo) + public bool ShouldVisitProperty(string propertyName) { - return _filter.ShouldVisitProperty(propertyInfo.Name); + return _filter.ShouldVisitProperty(propertyName); } public void VisitProperty(string propertyName, object value, bool isLast) @@ -88,6 +96,50 @@ public bool ShouldVisitPropertyValue(string propertyName, object value) } } + private static JsonObject FindElementByAutomationId(JsonObject obj, string automationId) + { + if (obj.Keys.Contains("AutomationId") && obj["AutomationId"].GetString() == automationId) + { + return obj; + } + if (obj.Keys.Contains("children")) + { + var array = obj.GetNamedArray("children"); + foreach (var i in array) + { + var element = FindElementByAutomationId(i.GetObject(), automationId); + if (element != null) + { + return element; + } + } + } + return null; + } + + public static IAsyncOperation DoesTreeDumpMatchForRNTester(DependencyObject root) + { + string json = DumpTree(root, null, new string[] { }, DumpTreeMode.Json); + try + { + var obj = JsonValue.Parse(json).GetObject(); + var element = FindElementByAutomationId(obj, "PageHeader"); + if (element == null) + { + return Task.Run(() => false).AsAsyncOperation(); + } + var value = element.GetNamedString("Text"); + var pageName = new Regex(@"[<|>]").Replace(value, ""); + var match = TreeDumpHelper.MatchDump(json, pageName); + return match.AsAsyncOperation(); + } + catch + { + Debug.WriteLine("JSON ERROR:\n" + json); + throw; + } + } + public static string DumpTree(DependencyObject root, DependencyObject excludedNode, IList additionalProperties, DumpTreeMode mode) { var propertyFilter = new DefaultFilter(); @@ -111,20 +163,26 @@ private static void WalkThroughProperties(DependencyObject node, Visitor visitor if (visitor.ShouldVisitPropertiesForNode(node)) { var properties = (from property in node.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) - where visitor.ShouldVisitProperty(property) && + where visitor.ShouldVisitProperty(property.Name) && visitor.ShouldVisitPropertyValue(property.Name, GetObjectProperty(node, property)) orderby property.Name select property).ToArray(); + var automationId = node.GetValue(AutomationProperties.AutomationIdProperty); for (int i = 0; i < properties.Length; i++) { var property = properties[i]; object value = null; value = GetObjectProperty(node, property); - bool isLast = (i == properties.Length - 1) && !hasChildren; + bool isLast = (i == properties.Length - 1) && !hasChildren && (automationId == null); visitor.VisitProperty(property.Name, value, isLast); } + + if (automationId != null) + { + visitor.VisitProperty("AutomationId", automationId, !hasChildren); + } } } @@ -143,24 +201,36 @@ private static object GetObjectProperty(DependencyObject node, PropertyInfo prop return value; } + private static DependencyObject[] GetChildren(DependencyObject node, Visitor visitor) + { + DependencyObject[] dos = new DependencyObject[VisualTreeHelper.GetChildrenCount(node)]; + for (int i = 0; i < dos.Length; i++) + { + dos[i] = VisualTreeHelper.GetChild(node, i); + } + return dos.Where((n) => visitor.ShouldVisitPropertiesForNode(n)).ToArray(); + } + private static void WalkThroughTree(DependencyObject node, DependencyObject excludedNode, Visitor visitor, bool isLast = true) { - if (node != null) + if (node != null && visitor.ShouldVisitPropertiesForNode(node)) { // Assume that if we have a UIElement, we'll have some properties - visitor.BeginVisitNode(node, node is UIElement); + var children = GetChildren(node, visitor); + bool hasProperties = node is UIElement || (children.Length > 0); + + visitor.BeginVisitNode(node, hasProperties); - var childrenCount = VisualTreeHelper.GetChildrenCount(node); - WalkThroughProperties(node, visitor, childrenCount != 0); - if (childrenCount != 0) + WalkThroughProperties(node, visitor, children.Length != 0); + if (children.Length != 0) { visitor.BeginChildren(); - for (int i = 0; i < childrenCount; i++) + for (int i = 0; i < children.Length; i++) { - var child = VisualTreeHelper.GetChild(node, i); + var child = children[i]; if (child != excludedNode) { - bool isLastChild = (i == childrenCount - 1); + bool isLastChild = (i == children.Length - 1); WalkThroughTree(child, excludedNode, visitor, isLastChild); } } @@ -194,6 +264,7 @@ public DefaultFilter() "Clip", "FlowDirection", "Name", + "Text" /*"ActualOffset" 19h1*/ }; } @@ -251,13 +322,17 @@ public string PropertyValueToString(string propertyName, object propertyObject) { // comparing doubles is numerically unstable so just compare their integer parts Size size = (Size)propertyObject; - return $"[{(int)size.Width}, {(int)size.Height}]"; + int width = (int)size.Width; + int height = (int)size.Height; + return $"[{width}, {height}]"; } return Quote(propertyObject.ToString()); } public static string Quote(string s) { + s = s.Replace('\t', ' ').Replace("\n", @"\n"); + s = Regex.Replace(s, @"\p{Cs}", ""); // remove surrogate pairs e.g. emojis return '"' + s.Replace("\"", "\\\"") + '"'; } } diff --git a/packages/microsoft-reactnative-sampleapps/package.json b/packages/microsoft-reactnative-sampleapps/package.json index 512f96ab9ca..9192adc47a2 100644 --- a/packages/microsoft-reactnative-sampleapps/package.json +++ b/packages/microsoft-reactnative-sampleapps/package.json @@ -16,7 +16,7 @@ "dependencies": { "react": "16.11.0", "react-native": "0.62.2", - "react-native-windows": "0.0.0-master.62" + "react-native-windows": "0.0.0-master.65" }, "devDependencies": { "@babel/core": "^7.8.4", diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h index 87f071637f2..161adc8f9c8 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h @@ -36,8 +36,8 @@ struct SampleModuleCppImpl { void Initialize(ReactContext const &reactContext) noexcept { const ReactPropertyId myProp1{L"Prop1"}; const ReactPropertyId myProp2{L"Prop2"}; - DEBUG_OUTPUT("globalProps.Prop1:", *reactContext.Properties().Get(myProp1)); - DEBUG_OUTPUT("instanceProps.Prop2:", winrt::to_string(*reactContext.Properties().Get(myProp2))); + DEBUG_OUTPUT("C++ Properties.Prop1:", *reactContext.Properties().Get(myProp1)); + DEBUG_OUTPUT("C++ Properties.Prop2:", winrt::to_string(*reactContext.Properties().Get(myProp2))); m_timer = winrt::Windows::System::Threading::ThreadPoolTimer::CreatePeriodicTimer( [this](const winrt::Windows::System::Threading::ThreadPoolTimer) noexcept { diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs index 655e8391d7b..732219ba1e4 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs @@ -26,8 +26,8 @@ internal sealed class SampleModuleCS [ReactInitializer] public void Initialize(ReactContext reactContext) { - Debug.WriteLine($"C# globalProps.Prop1: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop1"))}"); - Debug.WriteLine($"C# instanceProps.Prop2: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop2"))}"); + Debug.WriteLine($"C# Properties.Prop1: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop1"))}"); + Debug.WriteLine($"C# Properties.Prop2: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop2"))}"); _timer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler((timer) => { diff --git a/packages/playground/package.json b/packages/playground/package.json index 429fc214d7b..64b1cc263d6 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -11,7 +11,7 @@ "dependencies": { "react": "16.11.0", "react-native": "0.62.2", - "react-native-windows": "0.0.0-master.62" + "react-native-windows": "0.0.0-master.65" }, "devDependencies": { "@babel/core": "^7.8.4", diff --git a/packages/playground/windows/playground-win32.sln b/packages/playground/windows/playground-win32.sln index 3339e428ab1..705febd73f7 100644 --- a/packages/playground/windows/playground-win32.sln +++ b/packages/playground/windows/playground-win32.sln @@ -40,6 +40,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\..\..\vnext\Co EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\..\..\vnext\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\..\..\vnext\ReactWindowsCore\ReactWindowsCore.vcxitems*{11c084a3-a57c-4296-a679-cac17b603144}*SharedItemsImports = 4 @@ -47,6 +49,7 @@ Global ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 ..\..\..\vnext\Mso\Mso.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 ..\..\..\vnext\Shared\Shared.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 + ..\..\..\vnext\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{8b88ffae-4dbc-49a2-afa5-d2477d4ad189}*SharedItemsImports = 4 ..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4 ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 diff --git a/packages/playground/windows/playground-win32/Playground-win32.vcxproj b/packages/playground/windows/playground-win32/Playground-win32.vcxproj index 9e4ec292c29..19af51184fe 100644 --- a/packages/playground/windows/playground-win32/Playground-win32.vcxproj +++ b/packages/playground/windows/playground-win32/Playground-win32.vcxproj @@ -74,8 +74,6 @@ pch.h true Level3 - USE_V8;%(PreprocessorDefinitions) - USE_HERMES;%(PreprocessorDefinitions) shell32.lib;user32.lib;windowsapp.lib;$(ReactNativeWindowsDir)target\$(PlatformTarget)\$(Configuration)\ReactUWP\React.uwp.lib;%(AdditionalDependenices) diff --git a/packages/playground/windows/playground.sln b/packages/playground/windows/playground.sln index 6c610df8452..62a9c082eae 100644 --- a/packages/playground/windows/playground.sln +++ b/packages/playground/windows/playground.sln @@ -43,6 +43,7 @@ EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9 + ..\..\..\vnext\ReactWindowsCore\ReactWindowsCore.vcxitems*{11c084a3-a57c-4296-a679-cac17b603144}*SharedItemsImports = 4 ..\..\..\vnext\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 ..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{67a1076f-7790-4203-86ea-4402ccb5e782}*SharedItemsImports = 13 ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{6b6aa847-b32f-41ac-9d3b-48a8cdfa8ade}*SharedItemsImports = 4 diff --git a/packages/scripts/formatFiles.js b/packages/scripts/formatFiles.js index 86c16d088e0..7bce4725db7 100644 --- a/packages/scripts/formatFiles.js +++ b/packages/scripts/formatFiles.js @@ -35,6 +35,7 @@ function queryNoOpenFiles() { if (opened) { console.error('The following files have incorrect formatting:'); console.error(opened); + console.error('Running `yarn format` from the repo root should fix this.'); process.exit(2); } } diff --git a/vnext/CHANGELOG.json b/vnext/CHANGELOG.json index e5de6cf161b..f5bd3760b42 100644 --- a/vnext/CHANGELOG.json +++ b/vnext/CHANGELOG.json @@ -1,6 +1,83 @@ { "name": "react-native-windows", "entries": [ + { + "date": "Wed, 13 May 2020 00:04:21 GMT", + "tag": "react-native-windows_v0.0.0-master.65", + "version": "0.0.0-master.65", + "comments": { + "none": [ + { + "comment": "test bundle build logic, JS function call test", + "author": "aeulitz@microsoft.com", + "commit": "56e08530db3e751a8813785bb51aeb728aad98b0", + "package": "react-native-windows" + }, + { + "comment": "Handle HTTP connect exceptions in DevSupportManager", + "author": "julio.rocha@microsoft.com", + "commit": "dbe875f5f1445d4a3967b466ad83d0c33d76a53c", + "package": "react-native-windows" + } + ], + "prerelease": [ + { + "comment": "add ReactNativeHost to Win32 C++/WinRT ABI", + "author": "aeulitz@microsoft.com", + "commit": "49e65479d70ab1df738cb7bf21e915e9453eaf13", + "package": "react-native-windows" + }, + { + "comment": "Fixed ReactContext copy/move semantic", + "author": "vmorozov@microsoft.com", + "commit": "d5e8bdcbf63a8ee2e9221bfa344faa227ef892e3", + "package": "react-native-windows" + }, + { + "comment": "Use spec file for DevSettings NativeModule", + "author": "acoates@microsoft.com", + "commit": "8033b548f81e48d904b4dd1b1f81df33dbb232c4", + "package": "react-native-windows" + } + ] + } + }, + { + "date": "Tue, 12 May 2020 00:04:39 GMT", + "tag": "react-native-windows_v0.0.0-master.64", + "version": "0.0.0-master.64", + "comments": { + "prerelease": [ + { + "comment": "Fire onLoad event when a bitmap image is opeed", + "author": "lamdoan@microsoft.com", + "commit": "c40cc0ea1de8b4acb830939ba79146739f438111", + "package": "react-native-windows" + }, + { + "comment": "Expose ability for apps to provide their own RedBox implementation", + "author": "acoates@microsoft.com", + "commit": "55091b9447d741fc0e9c23acb8dfdee53ad11598", + "package": "react-native-windows" + } + ] + } + }, + { + "date": "Sun, 10 May 2020 00:05:02 GMT", + "tag": "react-native-windows_v0.0.0-master.63", + "version": "0.0.0-master.63", + "comments": { + "prerelease": [ + { + "comment": "Reenable V8 for desktop projects", + "author": "tudorm@microsoft.com", + "commit": "858fdce423bb4a0eac46dd4eca97adb3b6b042e9", + "package": "react-native-windows" + } + ] + } + }, { "date": "Sat, 09 May 2020 00:04:42 GMT", "tag": "react-native-windows_v0.0.0-master.62", diff --git a/vnext/CHANGELOG.md b/vnext/CHANGELOG.md index 740bdc37cfe..2ebe842eed9 100644 --- a/vnext/CHANGELOG.md +++ b/vnext/CHANGELOG.md @@ -1,9 +1,36 @@ # Change Log - react-native-windows -This log was last generated on Sat, 09 May 2020 00:04:42 GMT and should not be manually modified. +This log was last generated on Wed, 13 May 2020 00:04:21 GMT and should not be manually modified. +## 0.0.0-master.65 + +Wed, 13 May 2020 00:04:21 GMT + +### Changes + +- add ReactNativeHost to Win32 C++/WinRT ABI (aeulitz@microsoft.com) +- Fixed ReactContext copy/move semantic (vmorozov@microsoft.com) +- Use spec file for DevSettings NativeModule (acoates@microsoft.com) + +## 0.0.0-master.64 + +Tue, 12 May 2020 00:04:39 GMT + +### Changes + +- Fire onLoad event when a bitmap image is opeed (lamdoan@microsoft.com) +- Expose ability for apps to provide their own RedBox implementation (acoates@microsoft.com) + +## 0.0.0-master.63 + +Sun, 10 May 2020 00:05:02 GMT + +### Changes + +- Reenable V8 for desktop projects (tudorm@microsoft.com) + ## 0.0.0-master.62 Sat, 09 May 2020 00:04:42 GMT diff --git a/vnext/Desktop.ABITests/ActivationFactory.cpp b/vnext/Desktop.ABITests/ActivationFactory.cpp index 81ff78b6cf7..19915c7e683 100644 --- a/vnext/Desktop.ABITests/ActivationFactory.cpp +++ b/vnext/Desktop.ABITests/ActivationFactory.cpp @@ -26,7 +26,7 @@ int32_t __stdcall WINRT_RoGetActivationFactory(void *classId, guid const &iid, v std::wstring_view const name{*reinterpret_cast(&classId)}; HMODULE library{nullptr}; - if (starts_with(name, L"facebook.react.")) { + if (starts_with(name, L"facebook.react.") || starts_with(name, L"Microsoft.React")) { library = LoadLibraryExW(RNDLLPATH, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); } else { return OS_RoGetActivationFactory(classId, iid, factory); diff --git a/vnext/Desktop.ABITests/ActivationFactory.h b/vnext/Desktop.ABITests/ActivationFactory.h new file mode 100644 index 00000000000..e53f76d24ce --- /dev/null +++ b/vnext/Desktop.ABITests/ActivationFactory.h @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "pch.h" +#include +#include + +extern int32_t __stdcall WINRT_RoGetActivationFactory(void *classId, winrt::guid const &iid, void **factory) noexcept; diff --git a/vnext/Desktop.ABITests/MemoryTrackerTests.cpp b/vnext/Desktop.ABITests/MemoryTrackerTests.cpp index ce3da0f7549..79ad888716c 100644 --- a/vnext/Desktop.ABITests/MemoryTrackerTests.cpp +++ b/vnext/Desktop.ABITests/MemoryTrackerTests.cpp @@ -7,14 +7,13 @@ #include #include +#include "ActivationFactory.h" #include "MessageQueueShim.h" using namespace Microsoft::React::Test; using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace winrt::facebook::react; -int32_t __stdcall WINRT_RoGetActivationFactory(void *classId, winrt::guid const &iid, void **factory) noexcept; - namespace ABITests { // We turn clang format off here because it does not work with some of the diff --git a/vnext/Desktop.ABITests/NativeLogEventTests.cpp b/vnext/Desktop.ABITests/NativeLogEventTests.cpp index d23fd4ae2ad..859b84dca56 100644 --- a/vnext/Desktop.ABITests/NativeLogEventTests.cpp +++ b/vnext/Desktop.ABITests/NativeLogEventTests.cpp @@ -5,13 +5,12 @@ #include #include #include +#include "ActivationFactory.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace winrt::facebook::react; using namespace winrt; -int32_t __stdcall WINRT_RoGetActivationFactory(void *classId, winrt::guid const &iid, void **factory) noexcept; - namespace ABITests { TEST_CLASS (NativeLogEventTests) { diff --git a/vnext/Desktop.ABITests/NativeTraceEventTests.cpp b/vnext/Desktop.ABITests/NativeTraceEventTests.cpp index 371589c30a1..16348920fa3 100644 --- a/vnext/Desktop.ABITests/NativeTraceEventTests.cpp +++ b/vnext/Desktop.ABITests/NativeTraceEventTests.cpp @@ -6,13 +6,12 @@ #include #include #include +#include "ActivationFactory.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace winrt::facebook::react; using namespace winrt; -int32_t __stdcall WINRT_RoGetActivationFactory(void *classId, winrt::guid const &iid, void **factory) noexcept; - namespace ABITests { TEST_CLASS (NativeTraceEventTests) { diff --git a/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj b/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj index ec0af2a58ba..c4972ba5924 100644 --- a/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj +++ b/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj @@ -103,6 +103,9 @@ $(OutputPath)..\React.Windows.Desktop\facebook.react.winmd + + $(OutputPath)..\React.Windows.Desktop\Microsoft.ReactNative.winmd + @@ -133,9 +136,11 @@ Create + + diff --git a/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj.filters b/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj.filters index 49daa4eb2a2..76bd4b11819 100644 --- a/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj.filters +++ b/vnext/Desktop.ABITests/React.Windows.Desktop.ABITests.vcxproj.filters @@ -36,6 +36,9 @@ Source Files + + Source Files + @@ -44,6 +47,9 @@ Header Files + + Header Files + diff --git a/vnext/Desktop.ABITests/ReactNativeHostTests.cpp b/vnext/Desktop.ABITests/ReactNativeHostTests.cpp new file mode 100644 index 00000000000..7206f5b8daf --- /dev/null +++ b/vnext/Desktop.ABITests/ReactNativeHostTests.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include +#include +#include "ActivationFactory.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace ABITests { + +TEST_CLASS (ReactNativeHostTests) { + public: + ReactNativeHostTests() noexcept { + winrt_activation_handler = WINRT_RoGetActivationFactory; + } + + TEST_METHOD(Activation_Succeeds) { + try { + winrt::Microsoft::ReactNative::ReactNativeHost host{}; + Assert::IsTrue(true); + } catch (...) { + Assert::Fail(); + } + } +}; + +} // namespace ABITests diff --git a/vnext/Desktop.DLL/React.Windows.Desktop.DLL.vcxproj b/vnext/Desktop.DLL/React.Windows.Desktop.DLL.vcxproj index c598ae4e5e7..5376bcd160f 100644 --- a/vnext/Desktop.DLL/React.Windows.Desktop.DLL.vcxproj +++ b/vnext/Desktop.DLL/React.Windows.Desktop.DLL.vcxproj @@ -63,10 +63,8 @@ WIN32; _WINDOWS; _USRDLL; - REACTNATIVEWIN32_EXPORTS; FOLLY_NO_CONFIG; NOMINMAX; - GLOG_NO_ABBREVIATED_SEVERITIES; _HAS_AUTO_PTR_ETC; CHAKRACORE; RN_EXPORT=; @@ -122,21 +120,6 @@ - - {fca38f3c-7c73-4c47-be4e-32f77fa8538d} - - - {A990658C-CE31-4BCC-976F-0FC6B1AF693D} - - - {74085F13-2DDE-45E5-A0CA-927AC9D0B953} - - - {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} - - - {11C084A3-A57C-4296-A679-CAC17B603144} - {95048601-C3DC-475F-ADF8-7C0C764C10D5} @@ -169,6 +152,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + diff --git a/vnext/Desktop.DLL/packages.config b/vnext/Desktop.DLL/packages.config index 50494a3bc9d..aa1174628fc 100644 --- a/vnext/Desktop.DLL/packages.config +++ b/vnext/Desktop.DLL/packages.config @@ -4,4 +4,5 @@ + \ No newline at end of file diff --git a/vnext/Desktop.IntegrationTests/React.Windows.Desktop.IntegrationTests.vcxproj b/vnext/Desktop.IntegrationTests/React.Windows.Desktop.IntegrationTests.vcxproj index 44603739225..a44671c4fd1 100644 --- a/vnext/Desktop.IntegrationTests/React.Windows.Desktop.IntegrationTests.vcxproj +++ b/vnext/Desktop.IntegrationTests/React.Windows.Desktop.IntegrationTests.vcxproj @@ -55,10 +55,8 @@ _WIN32_WINNT=$(WinVer); WIN32; _WINDOWS; - REACTNATIVEWIN32_EXPORTS; FOLLY_NO_CONFIG; NOMINMAX; - GLOG_NO_ABBREVIATED_SEVERITIES; _HAS_AUTO_PTR_ETC; CHAKRACORE; RN_PLATFORM=windesktop; @@ -102,21 +100,9 @@ - - {fca38f3c-7c73-4c47-be4e-32f77fa8538d} - {A990658C-CE31-4BCC-976F-0FC6B1AF693D} - - {74085F13-2DDE-45E5-A0CA-927AC9D0B953} - - - {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} - - - {11C084A3-A57C-4296-A679-CAC17B603144} - {700A84FD-F92A-43F1-8D06-B0E0745DF9B5} diff --git a/vnext/Desktop.UnitTests/React.Windows.Desktop.UnitTests.vcxproj b/vnext/Desktop.UnitTests/React.Windows.Desktop.UnitTests.vcxproj index 1048078ca0f..84f5784e4f1 100644 --- a/vnext/Desktop.UnitTests/React.Windows.Desktop.UnitTests.vcxproj +++ b/vnext/Desktop.UnitTests/React.Windows.Desktop.UnitTests.vcxproj @@ -53,10 +53,8 @@ _WIN32_WINNT=$(WinVer); WIN32; _WINDOWS; - REACTNATIVEWIN32_EXPORTS; FOLLY_NO_CONFIG; NOMINMAX; - GLOG_NO_ABBREVIATED_SEVERITIES; _HAS_AUTO_PTR_ETC; CHAKRACORE; RN_EXPORT=; diff --git a/vnext/Desktop/ABI/ReactNativeHost.cpp b/vnext/Desktop/ABI/ReactNativeHost.cpp new file mode 100644 index 00000000000..4486d9a209f --- /dev/null +++ b/vnext/Desktop/ABI/ReactNativeHost.cpp @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include "ReactNativeHost.h" + +#include "Microsoft.ReactNative.ReactNativeHost.g.cpp" + +using namespace winrt::Windows::Foundation::Collections; + +namespace winrt::Microsoft::ReactNative::implementation { + +ReactNativeHost::ReactNativeHost() noexcept { + // TODO: implement +} + +IVector ReactNativeHost::PackageProviders() noexcept { + if (!m_packageProviders) { + m_packageProviders = single_threaded_vector(); + } + + return m_packageProviders; +} + +void ReactNativeHost::PackageProviders(IVector const &value) noexcept { + m_packageProviders = value; +} + +ReactNative::ReactInstanceSettings ReactNativeHost::InstanceSettings() noexcept { + if (!m_instanceSettings) { + // TODO: implement + // m_instanceSettings = make(); + } + + return m_instanceSettings; +} + +void ReactNativeHost::InstanceSettings(ReactNative::ReactInstanceSettings const &value) noexcept { + m_instanceSettings = value; +} + +void ReactNativeHost::ReloadInstance() noexcept { + // TODO: implement +} + +void ReactNativeHost::OnSuspend() noexcept { + // TODO: implement +} + +void ReactNativeHost::OnEnteredBackground() noexcept { + // TODO: implement +} + +void ReactNativeHost::OnLeavingBackground() noexcept { + // TODO: implement +} + +void ReactNativeHost::OnResume(OnResumeAction const & /*action*/) noexcept { + // TODO: implement +} + +void ReactNativeHost::OnBackPressed() noexcept { + // TODO: implement +} + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Desktop/ABI/ReactNativeHost.h b/vnext/Desktop/ABI/ReactNativeHost.h new file mode 100644 index 00000000000..f1596c4b49b --- /dev/null +++ b/vnext/Desktop/ABI/ReactNativeHost.h @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include + +#include "Microsoft.ReactNative.ReactNativeHost.g.h" + +namespace winrt::Microsoft::ReactNative::implementation { + +struct ReactNativeHost : ReactNativeHostT { + ReactNativeHost() noexcept; + + // property PackageProviders + Windows::Foundation::Collections::IVector PackageProviders() noexcept; + void PackageProviders(Windows::Foundation::Collections::IVector const &value) noexcept; + + // property InstanceSettings + ReactNative::ReactInstanceSettings InstanceSettings() noexcept; + void InstanceSettings(ReactNative::ReactInstanceSettings const &value) noexcept; + + void ReloadInstance() noexcept; + + void OnSuspend() noexcept; + void OnEnteredBackground() noexcept; + void OnLeavingBackground() noexcept; + void OnResume(OnResumeAction const &action) noexcept; + void OnBackPressed() noexcept; + + private: + ReactNative::ReactInstanceSettings m_instanceSettings{nullptr}; + Windows::Foundation::Collections::IVector m_packageProviders; +}; + +} // namespace winrt::Microsoft::ReactNative::implementation + +namespace winrt::Microsoft::ReactNative::factory_implementation { + +struct ReactNativeHost : ReactNativeHostT {}; + +} // namespace winrt::Microsoft::ReactNative::factory_implementation diff --git a/vnext/Desktop/DevSupportManager.cpp b/vnext/Desktop/DevSupportManager.cpp index d5c8d5c7dba..2877c043fa2 100644 --- a/vnext/Desktop/DevSupportManager.cpp +++ b/vnext/Desktop/DevSupportManager.cpp @@ -59,7 +59,12 @@ string DevSupportManager::GetJavaScriptFromServer( Url url(bundleUrl); auto const resolveResult = m_resolver.resolve(url.host, url.port); tcp::socket socket{m_context}; - connect(socket, resolveResult); + boost::system::error_code ec; + connect(socket, resolveResult, ec); + if (ec) { + m_exceptionCaught = true; + return R"({"error:")"s + ec.message() + R"("})"s; + } request request{verb::get, url.Target(), 11}; request.set(field::host, url.host); @@ -119,7 +124,12 @@ task DevSupportManager::LaunchDevToolsAsync(const string &debugHost, const Url url(DevServerHelper::get_LaunchDevToolsCommandUrl(debugHost)); auto const resolveResult = m_resolver.resolve(url.host, url.port); tcp::socket socket{m_context}; - connect(socket, resolveResult); + boost::system::error_code ec; + connect(socket, resolveResult, ec); + if (ec) { + m_exceptionCaught = true; + return; + } request request{verb::get, url.Target(), 11}; request.set(field::host, url.host); diff --git a/vnext/Desktop/React.Windows.Desktop.vcxproj b/vnext/Desktop/React.Windows.Desktop.vcxproj index 41a7846d4fd..304f44e661b 100644 --- a/vnext/Desktop/React.Windows.Desktop.vcxproj +++ b/vnext/Desktop/React.Windows.Desktop.vcxproj @@ -72,10 +72,8 @@ _WIN32_WINNT=$(WinVer); WIN32; _WINDOWS; - REACTNATIVEWIN32_EXPORTS; FOLLY_NO_CONFIG; NOMINMAX; - GLOG_NO_ABBREVIATED_SEVERITIES; _HAS_AUTO_PTR_ETC; CHAKRACORE; RN_PLATFORM=win32; @@ -92,6 +90,9 @@ Shlwapi.lib;%(AdditionalDependencies) -minpdbpathlen:256 + + $(ReactNativeWindowsDir)\Microsoft.ReactNative;$(ReactNativeWindowsDir)\Microsoft.ReactNative.Cxx; + @@ -99,6 +100,17 @@ + + + + + + + + + + + @@ -117,6 +129,10 @@ ABI\NativeTracing.idl $(IntDir)\ABI\ + + ..\Microsoft.ReactNative\ReactNativeHost.idl + $(IntDir)\ABI\ + @@ -147,6 +163,10 @@ ABI\NativeTracing.idl + + ..\Microsoft.ReactNative\ReactNativeHost.idl + Code + @@ -196,6 +216,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + diff --git a/vnext/Desktop/React.Windows.Desktop.vcxproj.filters b/vnext/Desktop/React.Windows.Desktop.vcxproj.filters index e150e18d4fa..6a4be8c5886 100644 --- a/vnext/Desktop/React.Windows.Desktop.vcxproj.filters +++ b/vnext/Desktop/React.Windows.Desktop.vcxproj.filters @@ -42,6 +42,39 @@ ABI + + ABI + + + ABI + + + ABI + + + ABI + + + ABI + + + ABI + + + ABI + + + ABI + + + ABI + + + ABI + + + ABI + diff --git a/vnext/Desktop/module.g.cpp b/vnext/Desktop/module.g.cpp index 84d71805b03..a1ed4613ebe 100644 --- a/vnext/Desktop/module.g.cpp +++ b/vnext/Desktop/module.g.cpp @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// WARNING: Please don't edit this file. It was generated by C++/WinRT v2.0.200316.3 + #include "pch.h" #include "winrt/base.h" +void* winrt_make_Microsoft_ReactNative_ReactNativeHost(); void* winrt_make_facebook_react_MemoryTracker(); void* winrt_make_facebook_react_NativeLogEventSource(); void* winrt_make_facebook_react_NativeTraceEventSource(); @@ -25,6 +28,11 @@ void* __stdcall winrt_get_activation_factory([[maybe_unused]] std::wstring_view return std::equal(left.rbegin(), left.rend(), right.rbegin(), right.rend()); }; + if (requal(name, L"Microsoft.ReactNative.ReactNativeHost")) + { + return winrt_make_Microsoft_ReactNative_ReactNativeHost(); + } + if (requal(name, L"facebook.react.MemoryTracker")) { return winrt_make_facebook_react_MemoryTracker(); diff --git a/vnext/IntegrationTests/React.Windows.IntegrationTests.vcxproj b/vnext/IntegrationTests/React.Windows.IntegrationTests.vcxproj index 470ab3aadbd..1023d7466b2 100644 --- a/vnext/IntegrationTests/React.Windows.IntegrationTests.vcxproj +++ b/vnext/IntegrationTests/React.Windows.IntegrationTests.vcxproj @@ -64,9 +64,6 @@ {fca38f3c-7c73-4c47-be4e-32f77fa8538d} - - {11C084A3-A57C-4296-A679-CAC17B603144} - diff --git a/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj b/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj index 6dbf207ef0d..c51a6f46a48 100644 --- a/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj +++ b/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj @@ -26,16 +26,9 @@ - - - - - - - $(ReactNativeWindowsDir);$(ReactNativeWindowsDir)Common;$(ReactNativeWindowsDir)stubs;$(FollyDir);$(ReactNativeDir)\ReactCommon;$(JSI_Source);$(ReactNativeWindowsDir)ReactWindowsCore;$(ReactNativeWindowsDir)\ReactWindowsCore\tracing;$(ReactNativeWindowsDir)include\ReactWindowsCore;$(ReactNativeWindowsDir)Desktop;$(ReactNativeWindowsDir)IntegrationTests;$(ReactNativeWindowsDir)JSI\Shared;$(MSBuildProjectDirectory);$(ReactNativeDir)\ReactCommon\callinvoker;$(IncludePath) true @@ -53,10 +46,8 @@ _WIN32_WINNT=$(WinVer); WIN32; _WINDOWS; - REACTNATIVEWIN32_EXPORTS; FOLLY_NO_CONFIG; NOMINMAX; - GLOG_NO_ABBREVIATED_SEVERITIES; _HAS_AUTO_PTR_ETC; CHAKRACORE; RN_EXPORT=; @@ -73,16 +64,8 @@ -minpdbpathlen:256 - - comsuppw.lib; delayimp.lib; - Shlwapi.lib; - Winhttp.lib; %(AdditionalDependencies) DebugFull @@ -119,21 +102,9 @@ - - {fca38f3c-7c73-4c47-be4e-32f77fa8538d} - - - {A990658C-CE31-4BCC-976F-0FC6B1AF693D} - - - {74085F13-2DDE-45E5-A0CA-927AC9D0B953} - {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} - - {11C084A3-A57C-4296-A679-CAC17B603144} - {95048601-C3DC-475F-ADF8-7C0C764C10D5} @@ -158,6 +129,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + diff --git a/vnext/JSI.Desktop.UnitTests/packages.config b/vnext/JSI.Desktop.UnitTests/packages.config index d9a6b100a5f..87f1304896e 100644 --- a/vnext/JSI.Desktop.UnitTests/packages.config +++ b/vnext/JSI.Desktop.UnitTests/packages.config @@ -6,4 +6,5 @@ + \ No newline at end of file diff --git a/vnext/JSI/Desktop/JSI.Desktop.vcxproj b/vnext/JSI/Desktop/JSI.Desktop.vcxproj index 0737e735265..58788a35444 100644 --- a/vnext/JSI/Desktop/JSI.Desktop.vcxproj +++ b/vnext/JSI/Desktop/JSI.Desktop.vcxproj @@ -74,11 +74,6 @@ - - - {fca38f3c-7c73-4c47-be4e-32f77fa8538d} - - @@ -89,6 +84,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + diff --git a/vnext/JSI/Universal/JSI.Universal.vcxproj b/vnext/JSI/Universal/JSI.Universal.vcxproj index 2dbbf006cb2..8ed94f58f44 100644 --- a/vnext/JSI/Universal/JSI.Universal.vcxproj +++ b/vnext/JSI/Universal/JSI.Universal.vcxproj @@ -115,11 +115,6 @@ - - - {fca38f3c-7c73-4c47-be4e-32f77fa8538d} - - diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj index 1849911ed19..79d66fa1e3a 100644 --- a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj @@ -126,6 +126,7 @@ Create + diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactContextTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactContextTest.cpp new file mode 100644 index 00000000000..c2d28914c93 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactContextTest.cpp @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include +#include "ReactModuleBuilderMock.h" + +using namespace winrt::Microsoft::ReactNative; + +struct ReactContextStub : implements { + IReactPropertyBag Properties() noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } + + void DispatchEvent( + xaml::FrameworkElement const & /*view*/, + hstring const & /*eventName*/, + JSValueArgWriter const & /*eventDataArgWriter*/) noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } + + void CallJSFunction( + hstring const & /*moduleName*/, + hstring const & /*functionName*/, + JSValueArgWriter const & /*paramsArgWriter*/) noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } + + void EmitJSEvent( + hstring const & /*eventEmitterName*/, + hstring const & /*eventName*/, + JSValueArgWriter const & /*paramsArgWriter*/) noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } +}; + +namespace ReactNativeTests { + +TEST_CLASS (ReactContextTest) { + TEST_METHOD(Test_ctor_Default) { + ReactContext context; + TestCheck(!context); + } + + TEST_METHOD(Test_ctor_IReactContext) { + auto reactContextMock = winrt::make(); + ReactContext context{reactContextMock}; + TestCheck(context); + } + + TEST_METHOD(Test_ctor_copy) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2{context1}; + TestCheck(context1); + TestCheck(context2); + TestCheckEqual(context1, context2); + } + + TEST_METHOD(Test_ctor_move) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2{std::move(context1)}; + TestCheck(!context1); + TestCheck(context2); + } + + TEST_METHOD(Test_assign_nullptr) { + ReactContext context; + context = nullptr; + TestCheck(!context); + } + + TEST_METHOD(Test_assign_copy) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2; + context2 = context1; + TestCheck(context1); + TestCheck(context2); + TestCheckEqual(context1, context2); + } + + TEST_METHOD(Test_assign_move) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2; + context2 = std::move(context1); + TestCheck(!context1); + TestCheck(context2); + } + + TEST_METHOD(Test_compare) { + auto reactContextMock1 = winrt::make(); + auto reactContextMock2 = winrt::make(); + ReactContext context11{reactContextMock1}; + ReactContext context12{context11}; + ReactContext context2{reactContextMock2}; + ReactContext context3; + TestCheck(context11 == context12); + TestCheck(context12 == context11); + TestCheck(context11 != context2); + TestCheck(context11 != context3); + TestCheck(context2 != context11); + TestCheck(context3 != context11); + TestCheck(context3 == nullptr); + TestCheck(nullptr == context3); + TestCheck(context11 != nullptr); + TestCheck(nullptr != context11); + } +}; + +} // namespace ReactNativeTests diff --git a/vnext/Microsoft.ReactNative.Cxx/BoxedValue.h b/vnext/Microsoft.ReactNative.Cxx/BoxedValue.h new file mode 100644 index 00000000000..841391a718b --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx/BoxedValue.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#ifndef MICROSOFT_REACTNATIVE_BOXEDVALUE +#define MICROSOFT_REACTNATIVE_BOXEDVALUE + +#include + +namespace winrt::Microsoft::ReactNative { + +template +struct BoxedValue : implements, IBoxedValue> { + template + BoxedValue(TArgs &&... args) noexcept : m_value(std::forward(args)...) {} + + int64_t GetPtr() const noexcept { + return reinterpret_cast(&m_value); + } + + static T &GetValueUnsafe(IBoxedValue const &boxedValue) noexcept { + return *reinterpret_cast(boxedValue.GetPtr()); + } + + private: + T m_value{}; +}; + +} // namespace winrt::Microsoft::ReactNative + +#endif // MICROSOFT_REACTNATIVE_BOXEDVALUE diff --git a/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h b/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h index 547ac7ead65..80b6cb68748 100644 --- a/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h +++ b/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h @@ -5,8 +5,9 @@ #ifndef MICROSOFT_REACTNATIVE_JSVALUEWRITER #define MICROSOFT_REACTNATIVE_JSVALUEWRITER +#include +#include "JSValue.h" #include "StructInfo.h" -#include "winrt/Microsoft.ReactNative.h" namespace winrt::Microsoft::ReactNative { diff --git a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems index 9a476a959fb..38328ca6aa6 100644 --- a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems +++ b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems @@ -17,6 +17,7 @@ + diff --git a/vnext/Microsoft.ReactNative.Cxx/NativeModules.h b/vnext/Microsoft.ReactNative.Cxx/NativeModules.h index 19ab45c0080..23532730abc 100644 --- a/vnext/Microsoft.ReactNative.Cxx/NativeModules.h +++ b/vnext/Microsoft.ReactNative.Cxx/NativeModules.h @@ -4,6 +4,7 @@ #pragma once #include "winrt/Microsoft.ReactNative.h" +#include "BoxedValue.h" #include "JSValueReader.h" #include "JSValueWriter.h" #include "ModuleRegistration.h" @@ -1105,23 +1106,6 @@ struct TurboModuleSpec { } }; -template -struct BoxedValue : implements, IBoxedValue> { - template - BoxedValue(TArgs &&... args) noexcept : m_value(std::forward(args)...) {} - - int64_t GetPtr() noexcept { - return reinterpret_cast(&m_value); - } - - static T &GetValueUnsafe(IBoxedValue const &boxedValue) noexcept { - return *reinterpret_cast(boxedValue.GetPtr()); - } - - private: - T m_value{}; -}; - template inline ReactModuleProvider MakeModuleProvider() noexcept { return [](IReactModuleBuilder const &moduleBuilder) noexcept { diff --git a/vnext/Microsoft.ReactNative.Cxx/ReactContext.h b/vnext/Microsoft.ReactNative.Cxx/ReactContext.h index a40c869f3c6..3dc09ee14ad 100644 --- a/vnext/Microsoft.ReactNative.Cxx/ReactContext.h +++ b/vnext/Microsoft.ReactNative.Cxx/ReactContext.h @@ -16,6 +16,8 @@ namespace winrt::Microsoft::ReactNative { // It wraps up the IReactContext and adds convenience methods for // working with C++ types. struct ReactContext { + ReactContext(std::nullptr_t = nullptr) noexcept {} + ReactContext(IReactContext const &handle) noexcept : m_handle{handle} {} IReactContext const &Handle() const noexcept { @@ -66,8 +68,32 @@ struct ReactContext { m_handle.DispatchEvent(view, eventName, paramsArgWriter); } + friend bool operator==(ReactContext const &left, ReactContext const &right) noexcept { + return left.m_handle == right.m_handle; + } + + friend bool operator!=(ReactContext const &left, ReactContext const &right) noexcept { + return left.m_handle != right.m_handle; + } + + friend bool operator==(ReactContext const &left, std::nullptr_t) noexcept { + return !static_cast(left.m_handle); + } + + friend bool operator!=(ReactContext const &left, std::nullptr_t) noexcept { + return static_cast(left.m_handle); + } + + friend bool operator==(std::nullptr_t, ReactContext const &right) noexcept { + return !static_cast(right.m_handle); + } + + friend bool operator!=(std::nullptr_t, ReactContext const &right) noexcept { + return static_cast(right.m_handle); + } + private: - const IReactContext m_handle; + IReactContext m_handle; }; } // namespace winrt::Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h b/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h index 1dc6d09bcdf..fcf75ff7a22 100644 --- a/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h +++ b/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h @@ -37,12 +37,14 @@ // directly because their null value may indicated absent property value. // For other types we return std::optional. It has std::nullopt value in case if // no property value is found or if it has a wrong type. -// To avoid compilation errors the non-IInspectable types must be WinRT types which are described here: +// To pass values through the ABI boundary the non-IInspectable types must be WinRT types +// which are described here: // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.propertytype?view=winrt-18362 // #include #include +#include "BoxedValue.h" namespace winrt::Microsoft::ReactNative { @@ -113,10 +115,10 @@ struct ReactPropertyId { IReactPropertyName m_handle; }; -// ReactPropertyBag is a wrapper for IReactPropertyBag to stores strongly-typed properties in a thread-safe way. +// ReactPropertyBag is a wrapper for IReactPropertyBag to store strongly-typed properties in a thread-safe way. // Types inherited from IInspectable are stored directly. // Values of other types are boxed with help of winrt::box_value. -// Only WinRT types can be stored. +// Non-WinRT types are wrapped with the help of BoxedValue template. struct ReactPropertyBag { // Property result type is either T or std::optional. // T is returned for types inherited from IInspectable. @@ -235,7 +237,11 @@ struct ReactPropertyBag { private: template static Windows::Foundation::IInspectable ToObject(T const &value) noexcept { - return winrt::box_value(value); + if constexpr (impl::has_category_v) { + return box_value(value); + } else { + return make>(value); + } } template @@ -247,7 +253,7 @@ struct ReactPropertyBag { static auto FromObject(Windows::Foundation::IInspectable const &obj) noexcept { if constexpr (std::is_base_of_v) { return obj.try_as(); - } else { + } else if constexpr (impl::has_category_v) { if (obj) { #ifdef WINRT_IMPL_IUNKNOWN_DEFINED if constexpr (std::is_same_v) { @@ -267,6 +273,14 @@ struct ReactPropertyBag { } } + return std::optional{}; + } else { + if (obj) { + if (auto temp = obj.try_as()) { + return std::optional{BoxedValue::GetValueUnsafe(temp)}; + } + } + return std::optional{}; } } diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp b/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp index 11d16009c32..2f11c1d9291 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp +++ b/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp @@ -1,5 +1,7 @@ #include "pch.h" +#include + using namespace React; namespace ReactNativeIntegrationTests { diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp b/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp index 7553d4be66a..f96cee63e26 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp +++ b/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp @@ -6,6 +6,7 @@ using namespace winrt; using namespace Microsoft::ReactNative; +using namespace Windows::Foundation; namespace ReactNativeIntegrationTests { @@ -491,7 +492,6 @@ TEST_CLASS (ReactPropertyBagTests) { } TEST_METHOD(PropertyBag_Property_string) { - // We only support enums defined in IDL. ReactPropertyId fooName{L"Foo"}; ReactPropertyBag pb{ReactPropertyBagHelper::CreatePropertyBag()}; @@ -504,6 +504,46 @@ TEST_CLASS (ReactPropertyBagTests) { pb.Remove(fooName); TestCheck(!pb.Get(fooName)); } + + TEST_METHOD(PropertyBag_Property_delegate) { + ReactPropertyId fooName{L"Foo"}; + ReactPropertyBag pb{ReactPropertyBagHelper::CreatePropertyBag()}; + + TestCheck(!pb.Get(fooName)); + ReactCreatePropertyValue createValue1 = []() { return winrt::box_value(5); }; + TestCheckEqual(createValue1, *pb.GetOrCreate(fooName, [&createValue1]() { return createValue1; })); + TestCheckEqual(createValue1, *pb.Get(fooName)); + TestCheckEqual(5, winrt::unbox_value((*pb.Get(fooName))())); + + ReactCreatePropertyValue createValue2 = []() { return winrt::box_value(10); }; + pb.Set(fooName, createValue2); + TestCheckEqual(createValue2, *pb.Get(fooName)); + TestCheckEqual(10, winrt::unbox_value((*pb.Get(fooName))())); + TestCheck(createValue1 != *pb.Get(fooName)); + pb.Remove(fooName); + TestCheck(!pb.Get(fooName)); + } + + TEST_METHOD(PropertyBag_Property_Functor) { + ReactPropertyId> fooName{L"Foo"}; + ReactPropertyBag pb{ReactPropertyBagHelper::CreatePropertyBag()}; + + TestCheck(!pb.Get(fooName)); + Mso::Functor createValue1 = []() noexcept { + return winrt::box_value(5); + }; + TestCheckEqual(createValue1, *pb.GetOrCreate(fooName, [&createValue1]() { return createValue1; })); + TestCheckEqual(createValue1, *pb.Get(fooName)); + TestCheckEqual(5, winrt::unbox_value((*pb.Get(fooName))())); + + Mso::Functor createValue2 = []() { return winrt::box_value(10); }; + pb.Set(fooName, createValue2); + TestCheckEqual(createValue2, *pb.Get(fooName)); + TestCheckEqual(10, winrt::unbox_value((*pb.Get(fooName))())); + TestCheck(createValue1 != *pb.Get(fooName)); + pb.Remove(fooName); + TestCheck(!pb.Get(fooName)); + } }; } // namespace ReactNativeIntegrationTests diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/pch.h b/vnext/Microsoft.ReactNative.IntegrationTests/pch.h index cde08a53662..19f70f55b7b 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/pch.h +++ b/vnext/Microsoft.ReactNative.IntegrationTests/pch.h @@ -3,7 +3,6 @@ #define NOMINMAX #include -#include "NativeModules.h" #include "functional/functor.h" #include "motifCpp/testCheck.h" diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.cpp b/vnext/Microsoft.ReactNative/IReactDispatcher.cpp new file mode 100644 index 00000000000..4c394e94825 --- /dev/null +++ b/vnext/Microsoft.ReactNative/IReactDispatcher.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "IReactDispatcher.h" +#include "ReactDispatcherHelper.g.cpp" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace winrt::Microsoft::ReactNative { + +ReactDispatcher::ReactDispatcher(Mso::DispatchQueue &&queue) noexcept : m_queue{std::move(queue)} {} + +bool ReactDispatcher::HasThreadAccess() noexcept { + return m_queue.HasThreadAccess(); +} + +void ReactDispatcher::Post(ReactDispatcherCallback const &callback) noexcept { + return m_queue.Post([callback]() noexcept { callback(); }); +} + +/*static*/ Mso::DispatchQueue ReactDispatcher::GetUIDispatchQueue(IReactPropertyBag const &properties) noexcept { + return GetUIDispatcher(properties).as()->m_queue; +} + +/*static*/ IReactDispatcher ReactDispatcher::UIThreadDispatcher() noexcept { + return make(Mso::DispatchQueue::MakeCurrentThreadUIQueue()); +} + +/*static*/ ReactPropertyId ReactDispatcher::UIDispatcherProperty() noexcept { + static ReactPropertyId uiThreadDispatcherProperty{L"ReactNative.Dispatcher", L"UIDispatcher"}; + return uiThreadDispatcherProperty; +} + +/*static*/ IReactDispatcher ReactDispatcher::GetUIDispatcher(IReactPropertyBag const &properties) noexcept { + return ReactPropertyBag{properties}.Get(UIDispatcherProperty()); +} + +/*static*/ void ReactDispatcher::SetUIThreadDispatcher(IReactPropertyBag const &properties) noexcept { + ReactPropertyBag{properties}.Set(UIDispatcherProperty(), UIThreadDispatcher()); +} + +} // namespace winrt::Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.h b/vnext/Microsoft.ReactNative/IReactDispatcher.h new file mode 100644 index 00000000000..e71c0526012 --- /dev/null +++ b/vnext/Microsoft.ReactNative/IReactDispatcher.h @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#include "ReactDispatcherHelper.g.h" +#include +#include +#include + +namespace winrt::Microsoft::ReactNative { + +struct ReactDispatcher : implements { + ReactDispatcher() = default; + ReactDispatcher(Mso::DispatchQueue &&queue) noexcept; + + bool HasThreadAccess() noexcept; + void Post(ReactDispatcherCallback const &callback) noexcept; + + static Mso::DispatchQueue GetUIDispatchQueue(IReactPropertyBag const &properties) noexcept; + + static IReactDispatcher UIThreadDispatcher() noexcept; + static ReactPropertyId UIDispatcherProperty() noexcept; + static IReactDispatcher GetUIDispatcher(IReactPropertyBag const &properties) noexcept; + static void SetUIThreadDispatcher(IReactPropertyBag const &properties) noexcept; + + private: + Mso::DispatchQueue m_queue; +}; + +} // namespace winrt::Microsoft::ReactNative + +namespace winrt::Microsoft::ReactNative::implementation { + +struct ReactDispatcherHelper { + ReactDispatcherHelper() = default; + + static IReactDispatcher UIThreadDispatcher() noexcept { + return ReactDispatcher::UIThreadDispatcher(); + } + + static IReactPropertyName UIDispatcherProperty() noexcept { + return ReactDispatcher::UIDispatcherProperty().Handle(); + } +}; + +} // namespace winrt::Microsoft::ReactNative::implementation + +namespace winrt::Microsoft::ReactNative::factory_implementation { + +struct ReactDispatcherHelper : ReactDispatcherHelperT {}; + +} // namespace winrt::Microsoft::ReactNative::factory_implementation diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.idl b/vnext/Microsoft.ReactNative/IReactDispatcher.idl new file mode 100644 index 00000000000..e5f3d9b6d5f --- /dev/null +++ b/vnext/Microsoft.ReactNative/IReactDispatcher.idl @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import "IReactPropertyBag.idl"; + +namespace Microsoft.ReactNative { + + // The delegate is used to create property value on-demand. + [webhosthidden] + delegate void ReactDispatcherCallback(); + + [webhosthidden] + interface IReactDispatcher + { + // True if dispatcher uses current thread. + Boolean HasThreadAccess { get; }; + + // Post task for the asynchronous execution. + void Post(ReactDispatcherCallback callback); + } + + // Helper methods for the property bag implementation. + [webhosthidden] + static runtimeclass ReactDispatcherHelper + { + // Get or create IReactDispatcher for the current UI thread. + static IReactDispatcher UIThreadDispatcher{ get; }; + + // Get name of the UIDispatcher property for the IReactPropertyBag. + static IReactPropertyName UIDispatcherProperty(); + } +} // namespace ReactNative diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index 4558c0c563e..3a981cd2ef6 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -1,4 +1,4 @@ - + @@ -147,8 +147,6 @@ --> CHAKRACORE;CHAKRACORE_UWP;%(PreprocessorDefinitions) USE_EDGEMODE_JSRT;%(PreprocessorDefinitions) - USE_HERMES;%(PreprocessorDefinitions) - USE_V8;%(PreprocessorDefinitions) REACTWINDOWS_BUILD; RN_PLATFORM=windows; @@ -301,6 +299,9 @@ IJSValueWriter.idl + + IReactDispatcher.idl + IReactModuleBuilder.idl @@ -342,6 +343,10 @@ ReactInstanceSettings.idl Code + + RedBoxHandler.idl + Code + ReactNativeHost.idl Code @@ -465,6 +470,9 @@ IJSValueWriter.idl + + IReactDispatcher.idl + IReactModuleBuilder.idl @@ -503,6 +511,10 @@ ReactInstanceSettings.idl Code + + RedBoxHandler.idl + Code + ReactNativeHost.idl Code @@ -541,6 +553,7 @@ + @@ -560,6 +573,9 @@ Designer + + Designer + Designer diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters index f5cee04be05..49caa030be9 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters @@ -265,6 +265,7 @@ + Modules @@ -298,7 +299,6 @@ ReactHost - @@ -599,6 +599,7 @@ Base + Modules @@ -648,7 +649,6 @@ ReactHost - @@ -685,9 +685,11 @@ + + diff --git a/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp b/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp index 3f980dcbd45..b3e32f5ca5b 100644 --- a/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp +++ b/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp @@ -11,8 +11,8 @@ using namespace xaml; namespace react::uwp { -AppStateData::AppStateData(Mso::React::IReactContext &reactContext) noexcept - : Super(Mso::DispatchQueue::MainUIQueue()), m_lastState{"active"}, m_reactContext{&reactContext} {} +AppStateData::AppStateData(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept + : Super(uiQueue), m_lastState{"active"}, m_reactContext{&reactContext} {} AppStateData::~AppStateData() = default; @@ -59,8 +59,8 @@ void AppStateData::RaiseEvent(char const *newState) noexcept { "RCTDeviceEventEmitter", "emit", folly::dynamic::array("appStateDidChange", std::move(parameters))); } -AppState2::AppState2(Mso::React::IReactContext &reactContext) noexcept - : m_data{Mso::Make(reactContext)} {} +AppState2::AppState2(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept + : m_data{Mso::Make(reactContext, uiQueue)} {} AppState2::~AppState2() = default; diff --git a/vnext/Microsoft.ReactNative/Modules/AppStateData.h b/vnext/Microsoft.ReactNative/Modules/AppStateData.h index ff400f8af81..48bc55672c5 100644 --- a/vnext/Microsoft.ReactNative/Modules/AppStateData.h +++ b/vnext/Microsoft.ReactNative/Modules/AppStateData.h @@ -15,7 +15,7 @@ namespace react::uwp { struct AppStateData : Mso::ActiveObject<> { using Super = ActiveObjectType; - AppStateData(Mso::React::IReactContext &reactContext) noexcept; + AppStateData(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept; ~AppStateData() override; void Initialize() noexcept override; void Finalize() noexcept override; @@ -36,7 +36,7 @@ struct AppStateData : Mso::ActiveObject<> { // It is a temporary class that we need to keep until we remove ReactUWP class AppState2 : public facebook::react::AppState { public: - AppState2(Mso::React::IReactContext &reactContext) noexcept; + AppState2(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept; public: // facebook::react::AppState ~AppState2() override; diff --git a/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.cpp b/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.cpp index 6d4312211c7..90a86c23900 100644 --- a/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.cpp +++ b/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.cpp @@ -6,53 +6,66 @@ namespace Microsoft::ReactNative { -auto DevSettingsModule::getConstants() -> std::map { - return {}; +void DevSettings::Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept { + m_context = reactContext; } -const char *DevSettingsModule::name = "DevSettings"; -std::string DevSettingsModule::getName() { - return name; +struct ReloadFunctor + : winrt::implements> { + ReloadFunctor(Mso::VoidFunctor &&func) : m_func(std::move(func)) {} + + void operator()() { + m_func(); + } + + private: + Mso::VoidFunctor m_func; +}; + +/*static*/ void DevSettings::SetReload(Mso::React::ReactOptions const &options, Mso::VoidFunctor &&func) noexcept { + options.Properties.Set(ReloadProperty(), winrt::make(std::move(func))); } -DevSettingsModule::DevSettingsModule(Mso::VoidFunctor &&reload) : m_reload(std::move(reload)) {} +/*static*/ winrt::Microsoft::ReactNative::IReactPropertyName DevSettings::ReloadProperty() noexcept { + return winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetName( + winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetNamespace(L"DevSettings"), L"Reload"); +} -void DevSettingsModule::reload() { - m_reload(); +void DevSettings::reload() noexcept { + (*winrt::get_self(m_context.Properties().Get( + winrt::Microsoft::ReactNative::ReactPropertyId(ReloadProperty()))))(); } -void DevSettingsModule::setHotLoadingEnabled(bool /*isHotLoadingEnabled*/) { - assert(false); + +void DevSettings::reloadWithReason(std::string /*reason*/) noexcept { + reload(); } -void DevSettingsModule::setIsDebuggingRemotely(bool /*isDebuggingRemotelyEnabled*/) { + +void DevSettings::onFastRefresh() noexcept { + // noop +} + +void DevSettings::setHotLoadingEnabled(bool isHotLoadingEnabled) noexcept { assert(false); } -void DevSettingsModule::setLiveReloadEnabled(bool /*setLiveReloadEnabled*/) { + +void DevSettings::setIsDebuggingRemotely(bool isDebuggingRemotelyEnabled) noexcept { assert(false); } -void DevSettingsModule::setProfilingEnabled(bool /*setProfilingEnabled*/) { + +void DevSettings::setProfilingEnabled(bool isProfilingEnabled) noexcept { assert(false); } -void DevSettingsModule::toggleElementInspector() { + +void DevSettings::toggleElementInspector() noexcept { assert(false); } -// iOS only. -void DevSettingsModule::setIsShakeToShowDevMenuEnabled(bool /*enabled*/) { +void DevSettings::addMenuItem(std::string title) noexcept { assert(false); } -auto DevSettingsModule::getMethods() -> std::vector { - return { - Method("reload", [this](folly::dynamic args) { reload(); }), - Method("setHotLoadingEnabled", [this](folly::dynamic args) { setHotLoadingEnabled(args[0].getBool()); }), - Method("setIsDebuggingRemotely", [this](folly::dynamic args) { setIsDebuggingRemotely(args[0].getBool()); }), - Method("setLiveReloadEnabled", [this](folly::dynamic args) { setLiveReloadEnabled(args[0].getBool()); }), - Method("setProfilingEnabled", [this](folly::dynamic args) { setProfilingEnabled(args[0].getBool()); }), - Method("toggleElementInspector", [this](folly::dynamic args) { toggleElementInspector(); }), - Method( - "setIsShakeToShowDevMenuEnabled", - [this](folly::dynamic args) { setIsShakeToShowDevMenuEnabled(args[0].getBool()); }), - }; +void DevSettings::setIsShakeToShowDevMenuEnabled(bool enabled) noexcept { + assert(false); } } // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.h b/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.h index 531aec8024a..aeddf6a7c3d 100644 --- a/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.h +++ b/vnext/Microsoft.ReactNative/Modules/DevSettingsModule.h @@ -5,30 +5,30 @@ #include #include -namespace Microsoft::ReactNative { - -class DevSettingsModule : public facebook::xplat::module::CxxModule { - public: - DevSettingsModule(Mso::VoidFunctor &&reload); +#include +#include - static const char *name; +namespace Microsoft::ReactNative { - std::string getName() override; - std::map getConstants() override; - std::vector getMethods() override; +REACT_MODULE(DevSettings) +struct DevSettings { + REACT_INIT(Initialize) void Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept; - void reload(); - void setHotLoadingEnabled(bool isHotLoadingEnabled); - void setIsDebuggingRemotely(bool isDebuggingRemotelyEnabled); - void setLiveReloadEnabled(bool setLiveReloadEnabled); - void setProfilingEnabled(bool setProfilingEnabled); - void toggleElementInspector(); + REACT_METHOD(reload) void reload() noexcept; + REACT_METHOD(reloadWithReason) void reloadWithReason(std::string reason) noexcept; + REACT_METHOD(onFastRefresh) void onFastRefresh() noexcept; + REACT_METHOD(setHotLoadingEnabled) void setHotLoadingEnabled(bool isHotLoadingEnabled) noexcept; + REACT_METHOD(setIsDebuggingRemotely) void setIsDebuggingRemotely(bool isDebuggingRemotelyEnabled) noexcept; + REACT_METHOD(setProfilingEnabled) void setProfilingEnabled(bool isProfilingEnabled) noexcept; + REACT_METHOD(toggleElementInspector) void toggleElementInspector() noexcept; + REACT_METHOD(addMenuItem) void addMenuItem(std::string title) noexcept; + REACT_METHOD(setIsShakeToShowDevMenuEnabled) void setIsShakeToShowDevMenuEnabled(bool enabled) noexcept; - // iOS only. - void setIsShakeToShowDevMenuEnabled(bool enabled); + static void SetReload(Mso::React::ReactOptions const &options, Mso::VoidFunctor &&func) noexcept; private: - Mso::VoidFunctor m_reload; + static winrt::Microsoft::ReactNative::IReactPropertyName ReloadProperty() noexcept; + winrt::Microsoft::ReactNative::ReactContext m_context; }; } // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/ReactApplication.cpp b/vnext/Microsoft.ReactNative/ReactApplication.cpp index a4f2d1d4e93..ee7263ae52f 100644 --- a/vnext/Microsoft.ReactNative/ReactApplication.cpp +++ b/vnext/Microsoft.ReactNative/ReactApplication.cpp @@ -5,6 +5,7 @@ #include "ReactApplication.h" #include "ReactApplication.g.cpp" +#include "IReactDispatcher.h" #include "Modules/LinkingManagerModule.h" #include "ReactNativeHost.h" @@ -48,6 +49,7 @@ ReactApplication::ReactApplication(IInspectable const &outer) noexcept : ReactAp ReactNative::ReactInstanceSettings ReactApplication::InstanceSettings() noexcept { if (!m_instanceSettings) { m_instanceSettings = make(); + ReactDispatcher::SetUIThreadDispatcher(m_instanceSettings.Properties()); } return m_instanceSettings; diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp index 957aca066f2..6676d038690 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp @@ -14,12 +14,14 @@ #include "Microsoft.ReactNative/Threading/MessageQueueThreadFactory.h" #include "../../codegen/NativeClipboardSpec.g.h" +#include "../../codegen/NativeDevSettingsSpec.g.h" #include "NativeModules.h" #include "NativeModulesProvider.h" #include "Unicode.h" #include #include +#include "IReactDispatcher.h" #include "Modules/AppStateData.h" #include "Modules/ClipboardModule.h" #include "Modules/DevSettingsModule.h" @@ -153,7 +155,7 @@ void ReactInstanceWin::Initialize() noexcept { InitUIManager(); Mso::PostFuture( - Mso::DispatchQueue::MainUIQueue(), + m_uiQueue, [weakThis = Mso::WeakPtr{this}]() noexcept { // Objects that must be created on the UI thread if (auto strongThis = weakThis.GetStrongPtr()) { @@ -162,7 +164,8 @@ void ReactInstanceWin::Initialize() noexcept { strongThis->m_appTheme = std::make_shared(legacyInstance, strongThis->m_uiMessageThread.LoadWithLock()); react::uwp::I18nHelper().Instance().setInfo(react::uwp::I18nModule::GetI18nInfo()); - strongThis->m_appearanceListener = Mso::Make(legacyInstance); + strongThis->m_appearanceListener = + Mso::Make(legacyInstance, strongThis->m_uiQueue); } }) .Then(Queue(), [ this, weakThis = Mso::WeakPtr{this} ]() noexcept { @@ -198,7 +201,7 @@ void ReactInstanceWin::Initialize() noexcept { devSettings->debuggerConsoleRedirection = false; // JSHost::ChangeGate::ChakraCoreDebuggerConsoleRedirection(); - m_appState = std::make_shared(*m_reactContext); + m_appState = std::make_shared(*m_reactContext, m_uiQueue); // Acquire default modules and then populate with custom modules std::vector cxxModules = react::uwp::GetCoreModules( @@ -218,21 +221,23 @@ void ReactInstanceWin::Initialize() noexcept { ::Microsoft::ReactNative::Clipboard, ::Microsoft::ReactNativeSpecs::ClipboardSpec>()); + ::Microsoft::ReactNative::DevSettings::SetReload( + strongThis->Options(), [weakReactHost = m_weakReactHost]() noexcept { + if (auto reactHost = weakReactHost.GetStrongPtr()) { + reactHost->ReloadInstance(); + } + }); + + nmp->AddModuleProvider( + L"DevSettings", + winrt::Microsoft::ReactNative::MakeTurboModuleProvider< + ::Microsoft::ReactNative::DevSettings, + ::Microsoft::ReactNativeSpecs::DevSettingsSpec>()); + auto modules = nmp->GetModules(m_reactContext, m_batchingUIThread); cxxModules.insert( cxxModules.end(), std::make_move_iterator(modules.begin()), std::make_move_iterator(modules.end())); - cxxModules.emplace_back( - Microsoft::ReactNative::DevSettingsModule::name, - [weakReactHost = strongThis->m_weakReactHost]() { - return std::make_unique([weakReactHost]() noexcept { - if (auto reactHost = weakReactHost.GetStrongPtr()) { - reactHost->ReloadInstance(); - } - }); - }, - m_batchingUIThread); - if (m_options.ModuleProvider != nullptr) { std::vector customCxxModules = m_options.ModuleProvider->GetModules(m_reactContext, m_batchingUIThread); @@ -454,8 +459,9 @@ void ReactInstanceWin::InitNativeMessageThread() noexcept { void ReactInstanceWin::InitUIMessageThread() noexcept { // Native queue was already given us in constructor. - m_uiMessageThread.Exchange(std::make_shared( - Mso::DispatchQueue::MainUIQueue(), Mso::MakeWeakMemberFunctor(this, &ReactInstanceWin::OnError))); + m_uiQueue = winrt::Microsoft::ReactNative::ReactDispatcher::GetUIDispatchQueue(m_options.Properties); + m_uiMessageThread.Exchange( + std::make_shared(m_uiQueue, Mso::MakeWeakMemberFunctor(this, &ReactInstanceWin::OnError))); m_batchingUIThread = react::uwp::MakeBatchingQueueThread(m_uiMessageThread.Load()); } @@ -517,7 +523,7 @@ std::shared_ptr ReactInstanceWin::GetRedBoxHandler() noexcept { return m_options.RedBoxHandler; } else if (m_options.DeveloperSettings.IsDevModeEnabled) { auto localWkReactHost = m_weakReactHost; - return CreateRedBoxHandler(std::move(localWkReactHost), m_uiMessageThread.LoadWithLock()); + return CreateDefaultRedBoxHandler(std::move(localWkReactHost), Mso::Copy(m_uiQueue)); } else { return {}; } diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h index 7a90f7da41b..5f2957f8389 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h @@ -161,6 +161,7 @@ class ReactInstanceWin final : public Mso::ActiveObject m_appTheme; Mso::CntPtr m_appearanceListener; std::string m_bundleRootPath; + Mso::DispatchQueue m_uiQueue; }; } // namespace Mso::React diff --git a/vnext/Microsoft.ReactNative/ReactInstanceSettings.h b/vnext/Microsoft.ReactNative/ReactInstanceSettings.h index daeb91102b9..1e828273511 100644 --- a/vnext/Microsoft.ReactNative/ReactInstanceSettings.h +++ b/vnext/Microsoft.ReactNative/ReactInstanceSettings.h @@ -80,6 +80,9 @@ struct ReactInstanceSettings : ReactInstanceSettingsT { uint16_t DebuggerPort() noexcept; void DebuggerPort(uint16_t value) noexcept; + IRedBoxHandler RedBoxHandler() noexcept; + void RedBoxHandler(IRedBoxHandler const &value) noexcept; + private: IReactPropertyBag m_properties{ReactPropertyBagHelper::CreatePropertyBag()}; hstring m_mainComponentName{}; @@ -100,6 +103,7 @@ struct ReactInstanceSettings : ReactInstanceSettingsT { hstring m_debugBundlePath{}; hstring m_bundleRootPath{}; uint16_t m_debuggerPort{9229}; + IRedBoxHandler m_redBoxHandler{nullptr}; }; } // namespace winrt::Microsoft::ReactNative::implementation @@ -264,4 +268,12 @@ inline void ReactInstanceSettings::DebuggerPort(uint16_t value) noexcept { m_debuggerPort = value; } +inline IRedBoxHandler ReactInstanceSettings::RedBoxHandler() noexcept { + return m_redBoxHandler; +} + +inline void ReactInstanceSettings::RedBoxHandler(IRedBoxHandler const &value) noexcept { + m_redBoxHandler = value; +} + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl b/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl index 22887666ca3..fff4532c2d4 100644 --- a/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl +++ b/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import "RedBoxHandler.idl"; import "IReactPropertyBag.idl"; namespace Microsoft.ReactNative { @@ -29,5 +30,6 @@ namespace Microsoft.ReactNative { String DebugBundlePath { get; set; }; String BundleRootPath { get; set; }; UInt16 DebuggerPort { get; set; }; + IRedBoxHandler RedBoxHandler { get; set; }; } } diff --git a/vnext/Microsoft.ReactNative/ReactNativeHost.cpp b/vnext/Microsoft.ReactNative/ReactNativeHost.cpp index 6812a46bef3..a7a1c9f3c99 100644 --- a/vnext/Microsoft.ReactNative/ReactNativeHost.cpp +++ b/vnext/Microsoft.ReactNative/ReactNativeHost.cpp @@ -6,6 +6,7 @@ #include "ReactNativeHost.g.cpp" #include "ReactPackageBuilder.h" +#include "RedBox.h" using namespace winrt; using namespace Windows::Foundation::Collections; @@ -78,6 +79,10 @@ void ReactNativeHost::ReloadInstance() noexcept { legacySettings.UseWebDebugger = m_instanceSettings.UseWebDebugger(); legacySettings.DebuggerPort = m_instanceSettings.DebuggerPort(); + if (m_instanceSettings.RedBoxHandler()) { + legacySettings.RedBoxHandler = std::move(Mso::React::CreateRedBoxHandler(m_instanceSettings.RedBoxHandler())); + } + Mso::React::ReactOptions reactOptions{}; reactOptions.Properties = m_instanceSettings.Properties(); reactOptions.DeveloperSettings.IsDevModeEnabled = legacySettings.EnableDeveloperMenu; @@ -91,6 +96,7 @@ void ReactNativeHost::ReloadInstance() noexcept { reactOptions.DeveloperSettings.DebugHost = legacySettings.DebugHost; reactOptions.BundleRootPath = legacySettings.BundleRootPath; reactOptions.DeveloperSettings.DebuggerPort = legacySettings.DebuggerPort; + reactOptions.RedBoxHandler = legacySettings.RedBoxHandler; reactOptions.LegacySettings = std::move(legacySettings); diff --git a/vnext/Microsoft.ReactNative/RedBox.cpp b/vnext/Microsoft.ReactNative/RedBox.cpp index 0c1c327cb24..0491a64fc9a 100644 --- a/vnext/Microsoft.ReactNative/RedBox.cpp +++ b/vnext/Microsoft.ReactNative/RedBox.cpp @@ -4,6 +4,7 @@ #include "RedBox.h" #include #include +#include #include #include #include @@ -18,10 +19,10 @@ namespace Mso::React { struct RedBox : public std::enable_shared_from_this { RedBox( - Mso::WeakPtr weakReactHost, + const Mso::WeakPtr &weakReactHost, Mso::Functor &&onClosedCallback, ErrorInfo &&errorInfo) noexcept - : m_weakReactHost(std::move(weakReactHost)), + : m_weakReactHost(weakReactHost), m_onClosedCallback(std::move(onClosedCallback)), m_errorInfo(std::move(errorInfo)) {} @@ -356,26 +357,22 @@ struct RedBox : public std::enable_shared_from_this { /* * This class is implemented such that the methods on IRedBoxHandler are thread safe. */ -struct RedBoxHandler : public std::enable_shared_from_this, IRedBoxHandler { - RedBoxHandler( - Mso::WeakPtr weakReactHost, - std::shared_ptr uiMessageQueue) noexcept - : m_weakReactHost(std::move(weakReactHost)), m_wkUIMessageQueue(std::move(uiMessageQueue)) {} - - ~RedBoxHandler() { - // Hide any currently showing redboxes - std::vector> redboxes; +struct DefaultRedBoxHandler : public std::enable_shared_from_this, IRedBoxHandler { + DefaultRedBoxHandler(Mso::WeakPtr &&weakReactHost, Mso::DispatchQueue &&uiQueue) noexcept + : m_weakReactHost{std::move(weakReactHost)}, m_uiQueue{std::move(uiQueue)} {} + + ~DefaultRedBoxHandler() { + // Hide any currently showing redBoxes + std::vector> redBoxes; { std::scoped_lock lock{m_lockRedBox}; - std::swap(m_redboxes, redboxes); - } - if (auto uiQueue = m_wkUIMessageQueue.lock()) { - uiQueue->runOnQueue([redboxes = std::move(redboxes)]() { - for (const auto redbox : redboxes) { - redbox->Dismiss(); - } - }); + std::swap(m_redBoxes, redBoxes); } + m_uiQueue.Post([redBoxes = std::move(redBoxes)]() { + for (const auto redBox : redBoxes) { + redBox->Dismiss(); + } + }); } virtual void showNewError(ErrorInfo &&info, ErrorType /*exceptionType*/) override { @@ -389,12 +386,12 @@ struct RedBoxHandler : public std::enable_shared_from_this, IRedB std::move(info))); { std::scoped_lock lock{m_lockRedBox}; - m_redboxes.push_back(std::move(redbox)); + m_redBoxes.push_back(std::move(redbox)); } showTopJSError(); } - virtual bool isDevSupportEnabled() override { + virtual bool isDevSupportEnabled() const override { if (auto reactHost = m_weakReactHost.GetStrongPtr()) { return reactHost->Options().DeveloperSettings.IsDevModeEnabled; } @@ -406,45 +403,44 @@ struct RedBoxHandler : public std::enable_shared_from_this, IRedB std::shared_ptr redbox; { std::scoped_lock lock{m_lockRedBox}; - for (auto it = m_redboxes.begin(); it != m_redboxes.end(); ++it) { + for (auto it = m_redBoxes.begin(); it != m_redBoxes.end(); ++it) { if ((*it)->GetId() == info.Id) { redbox = *it; break; } } } + if (redbox) { - if (auto uiQueue = m_wkUIMessageQueue.lock()) { - uiQueue->runOnQueue([redboxCaptured = std::move(redbox), errorInfo = std::move(info)]() { - redboxCaptured->UpdateError(std::move(errorInfo)); - }); - } + m_uiQueue.Post([redboxCaptured = std::move(redbox), errorInfo = std::move(info)]() { + redboxCaptured->UpdateError(std::move(errorInfo)); + }); } } virtual void dismissRedbox() override { - if (auto uiQueue = m_wkUIMessageQueue.lock()) { - uiQueue->runOnQueue([&]() { - std::scoped_lock lock{m_lockRedBox}; - if (!m_redboxes.empty()) - m_redboxes[0]->Dismiss(); - }); - } + m_uiQueue.Post([wkthis = std::weak_ptr(shared_from_this())]() { + if (auto pthis = wkthis.lock()) { + std::scoped_lock lock{pthis->m_lockRedBox}; + if (!pthis->m_redBoxes.empty()) + pthis->m_redBoxes[0]->Dismiss(); + } + }); } private: - // When notified by a redbox that its been dismisssed + // When notified by a redbox that its been dismissed void onDismissedCallback(uint32_t id) noexcept { - // Save a local ref, so if we are removing the last redbox which could hold the last ref to the RedBoxHandler + // Save a local ref, so if we are removing the last redbox which could hold the last ref to the DefaultRedBoxHandler // We ensure that we exit the mutex before the handler is destroyed. std::shared_ptr redbox; { { std::scoped_lock lock{m_lockRedBox}; - for (auto it = m_redboxes.begin(); it != m_redboxes.end(); ++it) { + for (auto it = m_redBoxes.begin(); it != m_redBoxes.end(); ++it) { if ((*it)->GetId() == id) { redbox = *it; - it = m_redboxes.erase(it); + it = m_redBoxes.erase(it); break; } } @@ -458,30 +454,122 @@ struct RedBoxHandler : public std::enable_shared_from_this, IRedB std::shared_ptr redbox; { std::scoped_lock lock{m_lockRedBox}; - if (!m_redboxes.empty()) { - redbox = m_redboxes[0]; + if (!m_redBoxes.empty()) { + redbox = m_redBoxes[0]; } } - if (auto uiQueue = m_wkUIMessageQueue.lock()) { - if (m_showingRedBox || !redbox) // Only show one redbox at a time - return; - m_showingRedBox = true; - uiQueue->runOnQueue([redboxCaptured = std::move(redbox)]() { redboxCaptured->ShowNewJSError(); }); - } + if (m_showingRedBox || !redbox) // Only show one redbox at a time + return; + m_showingRedBox = true; + + m_uiQueue.Post([redboxCaptured = std::move(redbox)]() { redboxCaptured->ShowNewJSError(); }); } - bool m_showingRedBox = false; // Access from UI Thread only + private: + const Mso::DispatchQueue m_uiQueue; + bool m_showingRedBox{false}; // Access from UI Thread only std::mutex m_lockRedBox; - std::vector> m_redboxes; // Protected by m_lockRedBox + std::vector> m_redBoxes; // Protected by m_lockRedBox const Mso::WeakPtr m_weakReactHost; - const std::weak_ptr m_wkUIMessageQueue; +}; + +struct RedBoxErrorFrameInfo + : public winrt::implements { + RedBoxErrorFrameInfo(Mso::React::ErrorFrameInfo &&errorFrameInfo) : m_frame(std::move(errorFrameInfo)) {} + + winrt::hstring File() const noexcept { + return ::Microsoft::Common::Unicode::Utf8ToUtf16(m_frame.File).c_str(); + } + + winrt::hstring Method() const noexcept { + return ::Microsoft::Common::Unicode::Utf8ToUtf16(m_frame.Method).c_str(); + } + + uint32_t Line() const noexcept { + return m_frame.Line; + } + + uint32_t Column() const noexcept { + return m_frame.Column; + } + + private: + Mso::React::ErrorFrameInfo m_frame; +}; + +struct RedBoxErrorInfo : public winrt::implements { + RedBoxErrorInfo(Mso::React::ErrorInfo &&errorInfo) : m_errorInfo(std::move(errorInfo)) {} + + winrt::hstring Message() const noexcept { + return ::Microsoft::Common::Unicode::Utf8ToUtf16(m_errorInfo.Message).c_str(); + } + + uint32_t Id() const noexcept { + return m_errorInfo.Id; + } + + winrt::Windows::Foundation::Collections::IVectorView + Callstack() noexcept { + if (!m_callstack) { + m_callstack = winrt::single_threaded_vector(); + for (auto frame : m_errorInfo.Callstack) { + m_callstack.Append(winrt::make(std::move(frame))); + } + } + + return m_callstack.GetView(); + } + + private: + winrt::Windows::Foundation::Collections::IVector m_callstack{ + nullptr}; + + Mso::React::ErrorInfo m_errorInfo; +}; + +struct RedBoxHandler : public Mso::React::IRedBoxHandler { + RedBoxHandler(winrt::Microsoft::ReactNative::IRedBoxHandler const &redBoxHandler) : m_redBoxHandler(redBoxHandler) {} + + static_assert( + static_cast(Mso::React::ErrorType::JSFatal) == + static_cast(winrt::Microsoft::ReactNative::RedBoxErrorType::JavaScriptFatal)); + static_assert( + static_cast(Mso::React::ErrorType::JSSoft) == + static_cast(winrt::Microsoft::ReactNative::RedBoxErrorType::JavaScriptSoft)); + static_assert( + static_cast(Mso::React::ErrorType::Native) == + static_cast(winrt::Microsoft::ReactNative::RedBoxErrorType::Native)); + + virtual void showNewError(Mso::React::ErrorInfo &&info, Mso::React::ErrorType errorType) override { + m_redBoxHandler.ShowNewError( + winrt::make(std::move(info)), + static_cast(static_cast(errorType))); + } + virtual bool isDevSupportEnabled() const override { + return m_redBoxHandler.IsDevSupportEnabled(); + } + virtual void updateError(Mso::React::ErrorInfo &&info) override { + m_redBoxHandler.UpdateError(winrt::make(std::move(info))); + } + + virtual void dismissRedbox() override { + m_redBoxHandler.DismissRedBox(); + } + + private: + winrt::Microsoft::ReactNative::IRedBoxHandler m_redBoxHandler; }; std::shared_ptr CreateRedBoxHandler( + winrt::Microsoft::ReactNative::IRedBoxHandler const &redBoxHandler) noexcept { + return std::make_shared(redBoxHandler); +} + +std::shared_ptr CreateDefaultRedBoxHandler( Mso::WeakPtr &&weakReactHost, - std::shared_ptr &&uiMessageQueue) noexcept { - return std::make_shared(std::move(weakReactHost), std::move(uiMessageQueue)); + Mso::DispatchQueue &&uiQueue) noexcept { + return std::make_shared(std::move(weakReactHost), std::move(uiQueue)); } } // namespace Mso::React diff --git a/vnext/Microsoft.ReactNative/RedBox.h b/vnext/Microsoft.ReactNative/RedBox.h index 8be0393b921..8d0672a2a54 100644 --- a/vnext/Microsoft.ReactNative/RedBox.h +++ b/vnext/Microsoft.ReactNative/RedBox.h @@ -2,13 +2,16 @@ // Licensed under the MIT License. #pragma once #include +#include "IRedBoxHandler.h" #include "ReactHost/React.h" -#include "RedBoxHandler.h" namespace Mso::React { std::shared_ptr CreateRedBoxHandler( + winrt::Microsoft::ReactNative::IRedBoxHandler const &redBoxHandler) noexcept; + +std::shared_ptr CreateDefaultRedBoxHandler( Mso::WeakPtr &&weakReactHost, - std::shared_ptr &&uiMessageQueue) noexcept; + Mso::DispatchQueue &&uiQueue) noexcept; } // namespace Mso::React diff --git a/vnext/Microsoft.ReactNative/RedBoxHandler.cpp b/vnext/Microsoft.ReactNative/RedBoxHandler.cpp new file mode 100644 index 00000000000..a368c5bb21f --- /dev/null +++ b/vnext/Microsoft.ReactNative/RedBoxHandler.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "RedBoxHandler.h" +#if __has_include("RedBoxHelper.g.cpp") +#include "RedBoxHelper.g.cpp" +#endif + +#include +#include +#include +#include + +namespace winrt::Microsoft::ReactNative::implementation { + +Mso::React::ErrorInfo CreateErrorInfo(const IRedBoxErrorInfo &info) { + Mso::React::ErrorInfo ei; + ei.Id = info.Id(); + ei.Message = ::Microsoft::Common::Unicode::Utf16ToUtf8(info.Message()); + for (const auto &frame : info.Callstack()) { + Mso::React::ErrorFrameInfo efi; + efi.Method = ::Microsoft::Common::Unicode::Utf16ToUtf8(frame.Method()); + efi.File = ::Microsoft::Common::Unicode::Utf16ToUtf8(frame.File()); + efi.Line = frame.Line(); + efi.Column = frame.Column(); + ei.Callstack.push_back(std::move(efi)); + } + return ei; +} + +struct DefaultRedBoxHandler : winrt::implements { + DefaultRedBoxHandler(winrt::Microsoft::ReactNative::ReactNativeHost const &host) noexcept { + auto hostImpl = winrt::get_self(host); + Mso::WeakPtr wkHost(hostImpl->ReactHost()); + m_redBoxHandler = Mso::React::CreateDefaultRedBoxHandler( + std::move(wkHost), ReactDispatcher::GetUIDispatchQueue(host.InstanceSettings().Properties())); + } + + void ShowNewError(IRedBoxErrorInfo const &info, RedBoxErrorType type) noexcept { + m_redBoxHandler->showNewError( + CreateErrorInfo(info), static_cast(static_cast(type))); + } + + bool IsDevSupportEnabled() noexcept { + return m_redBoxHandler->isDevSupportEnabled(); + } + + void UpdateError(IRedBoxErrorInfo const &info) noexcept { + m_redBoxHandler->updateError(std::move(CreateErrorInfo(info))); + } + + void DismissRedBox() noexcept { + m_redBoxHandler->dismissRedbox(); + } + + private: + std::shared_ptr m_redBoxHandler; +}; + +IRedBoxHandler RedBoxHelper::CreateDefaultHandler(winrt::Microsoft::ReactNative::ReactNativeHost const &host) noexcept { + return winrt::make(host); +} + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/RedBoxHandler.h b/vnext/Microsoft.ReactNative/RedBoxHandler.h new file mode 100644 index 00000000000..e8aa4b1f6ac --- /dev/null +++ b/vnext/Microsoft.ReactNative/RedBoxHandler.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "RedBoxHelper.g.h" + +namespace Mso::React { +struct IRedBoxHandler; +} + +namespace winrt::Microsoft::ReactNative::implementation { + +struct RedBoxHelper : RedBoxHelperT { + RedBoxHelper() = default; + + static IRedBoxHandler CreateDefaultHandler(winrt::Microsoft::ReactNative::ReactNativeHost const &host) noexcept; +}; + +} // namespace winrt::Microsoft::ReactNative::implementation + +namespace winrt::Microsoft::ReactNative::factory_implementation { + +struct RedBoxHelper : RedBoxHelperT {}; + +} // namespace winrt::Microsoft::ReactNative::factory_implementation diff --git a/vnext/Microsoft.ReactNative/RedBoxHandler.idl b/vnext/Microsoft.ReactNative/RedBoxHandler.idl new file mode 100644 index 00000000000..0092086437e --- /dev/null +++ b/vnext/Microsoft.ReactNative/RedBoxHandler.idl @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import "ReactNativeHost.idl"; + +namespace Microsoft.ReactNative { + + enum RedBoxErrorType { + JavaScriptFatal, // A JS Exception was thrown or otherwise fatal error + JavaScriptSoft, // An error coming from JS that isn't fatal, such as console.error + Native, + }; + + [webhosthidden] interface IRedBoxErrorFrameInfo { + String File { get; }; + String Method { get; }; + UInt32 Line { get; }; + UInt32 Column { get; }; + } + + [webhosthidden] interface IRedBoxErrorInfo { + String Message { get; }; + UInt32 Id { get; }; + IVectorView Callstack { get; }; + } + + [webhosthidden] + interface IRedBoxHandler + { + void ShowNewError(IRedBoxErrorInfo info, RedBoxErrorType type); + Boolean IsDevSupportEnabled { get; }; + void UpdateError(IRedBoxErrorInfo info); + void DismissRedBox(); + } + + [webhosthidden] + [default_interface] + runtimeclass RedBoxHelper { + RedBoxHelper(); + static IRedBoxHandler CreateDefaultHandler(Microsoft.ReactNative.ReactNativeHost host); + } + +} diff --git a/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp b/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp index cae018a7568..1dfd9e4f3c5 100644 --- a/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp +++ b/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp @@ -12,7 +12,8 @@ std::shared_ptr MakeJSQueueThread() noexcep } std::shared_ptr MakeUIQueueThread() noexcept { - return std::make_shared(Mso::DispatchQueue::MainUIQueue(), nullptr, nullptr); + return std::make_shared( + Mso::DispatchQueue::MakeCurrentThreadUIQueue(), nullptr, nullptr); } std::shared_ptr MakeSerialQueueThread() noexcept { diff --git a/vnext/Mso/dispatchQueue/dispatchQueue.h b/vnext/Mso/dispatchQueue/dispatchQueue.h index a7c1c0a4a20..50e2ecbe1e0 100644 --- a/vnext/Mso/dispatchQueue/dispatchQueue.h +++ b/vnext/Mso/dispatchQueue/dispatchQueue.h @@ -113,9 +113,6 @@ struct DispatchQueue { //! demand. static DispatchQueue const &ConcurrentQueue() noexcept; - //! Get DispatchQueue associated with the main UI thread. It is created on demand. - static DispatchQueue const &MainUIQueue() noexcept; - //! Create new serial DispatchQueue on top of platform specific thread pool. static DispatchQueue MakeSerialQueue() noexcept; @@ -158,8 +155,7 @@ struct DispatchQueue { //! True if tasks are invoked in a serial order by the queue. bool IsSerial() const noexcept; - //! True if queue is running on current thread or associated with it. E.g. MainUIQueue always returns true for the - //! main UI thread. + //! True if queue is running on current thread or associated with it. bool HasThreadAccess() const noexcept; //! Check if a long running task should yield. @@ -416,9 +412,6 @@ struct IDispatchQueueStatic : IUnknown { //! demand. virtual DispatchQueue const &ConcurrentQueue() noexcept = 0; - //! Get DispatchQueue associated with the main UI thread. It is created on demand. - virtual DispatchQueue const &MainUIQueue() noexcept = 0; - //! Create new serial DispatchQueue on top of platform specific thread pool. virtual DispatchQueue MakeSerialQueue() noexcept = 0; @@ -545,10 +538,6 @@ inline /*static*/ DispatchQueue const &DispatchQueue::ConcurrentQueue() noexcept return IDispatchQueueStatic::Instance()->ConcurrentQueue(); } -inline /*static*/ DispatchQueue const &DispatchQueue::MainUIQueue() noexcept { - return IDispatchQueueStatic::Instance()->MainUIQueue(); -} - inline /*static*/ DispatchQueue DispatchQueue::MakeSerialQueue() noexcept { return IDispatchQueueStatic::Instance()->MakeSerialQueue(); } diff --git a/vnext/Mso/src/dispatchQueue/queueService.cpp b/vnext/Mso/src/dispatchQueue/queueService.cpp index b560a732173..d30f444bdc0 100644 --- a/vnext/Mso/src/dispatchQueue/queueService.cpp +++ b/vnext/Mso/src/dispatchQueue/queueService.cpp @@ -269,11 +269,6 @@ DispatchQueue const &DispatchQueueStatic::ConcurrentQueue() noexcept { return *static_cast(static_cast(&concurrentQueue)); } -DispatchQueue const &DispatchQueueStatic::MainUIQueue() noexcept { - static auto mainUIQueue{Mso::Make(MakeMainUIScheduler())}; - return *static_cast(static_cast(&mainUIQueue)); -} - DispatchQueue DispatchQueueStatic::MakeSerialQueue() noexcept { return Mso::Make(MakeThreadPoolScheduler(/*maxThreads:*/ 1)); } diff --git a/vnext/Mso/src/dispatchQueue/queueService.h b/vnext/Mso/src/dispatchQueue/queueService.h index 0d1b30a9734..a80f72a669b 100644 --- a/vnext/Mso/src/dispatchQueue/queueService.h +++ b/vnext/Mso/src/dispatchQueue/queueService.h @@ -82,14 +82,12 @@ struct QueueLocalValueEntry { struct DispatchQueueStatic : Mso::UnknownObject { static DispatchQueueStatic *Instance() noexcept; static Mso::CntPtr MakeLooperScheduler() noexcept; - static Mso::CntPtr MakeMainUIScheduler() noexcept; static Mso::CntPtr MakeCurrentThreadUIScheduler() noexcept; static Mso::CntPtr MakeThreadPoolScheduler(uint32_t maxThreads) noexcept; public: // IDispatchQueueStatic DispatchQueue CurrentQueue() noexcept override; DispatchQueue const &ConcurrentQueue() noexcept override; - DispatchQueue const &MainUIQueue() noexcept override; DispatchQueue MakeSerialQueue() noexcept override; DispatchQueue MakeLooperQueue() noexcept override; DispatchQueue MakeCurrentThreadUIQueue() noexcept override; diff --git a/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp b/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp index 1753023d3a1..70981a780a1 100644 --- a/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp +++ b/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp @@ -5,38 +5,36 @@ #include "object/refCountedObject.h" #include "queueService.h" #include "taskQueue.h" -#include "winrt/Windows.ApplicationModel.Core.h" -#include "winrt/Windows.UI.Core.h" +#include "winrt/Windows.System.h" using namespace winrt; -using namespace Windows::ApplicationModel::Core; -using namespace Windows::UI::Core; +using namespace Windows::System; namespace Mso { -struct UISchdulerWinRT; +struct UISchedulerWinRT; -//! TaskDispatchedHandler is a DispatchedHandler delegate that we pass to CoreDispatcher. +//! TaskDispatcherHandler is a DispatcherQueueHandler delegate that we pass to DispatcherQueue. //! We use custom ref counting to avoid extra memory allocations and to handle reference to DispatchTask. -struct TaskDispatchedHandler final : impl::abi_t { - TaskDispatchedHandler(UISchdulerWinRT *scheduler) noexcept; +struct TaskDispatcherHandler final : impl::abi_t { + TaskDispatcherHandler(UISchedulerWinRT *scheduler) noexcept; int32_t __stdcall QueryInterface(guid const &id, void **result) noexcept final; uint32_t __stdcall AddRef() noexcept final; uint32_t __stdcall Release() noexcept final; int32_t __stdcall Invoke() noexcept final; private: - UISchdulerWinRT *m_scheduler; + UISchedulerWinRT *m_scheduler; }; -struct UISchdulerWinRT : Mso::UnknownObject { - UISchdulerWinRT(CoreDispatcher &&coreDispatcher) noexcept; - ~UISchdulerWinRT() noexcept override; +struct UISchedulerWinRT : Mso::UnknownObject { + UISchedulerWinRT(DispatcherQueue &&dispatcher) noexcept; + ~UISchedulerWinRT() noexcept override; uint32_t AddHandlerRef() noexcept; uint32_t ReleaseHandlerRef() noexcept; - DispatchedHandler MakeDispatchedHandler() noexcept; + DispatcherQueueHandler MakeDispatcherQueueHandler() noexcept; bool TryTakeTask(Mso::CntPtr &queue, DispatchTask &task) noexcept; public: // IDispatchQueueScheduler @@ -49,39 +47,40 @@ struct UISchdulerWinRT : Mso::UnknownObject m_queue; - Mso::CntPtr m_self; + Mso::CntPtr m_self; uint32_t m_handlerRefCount{0}; uint32_t m_taskCount{0}; bool m_isShutdown{false}; + std::thread::id m_threadId{std::this_thread::get_id()}; }; //============================================================================= -// TaskDispatchedHandler implementation +// TaskDispatcherHandler implementation //============================================================================= -TaskDispatchedHandler::TaskDispatchedHandler(UISchdulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} +TaskDispatcherHandler::TaskDispatcherHandler(UISchedulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} -int32_t __stdcall TaskDispatchedHandler::QueryInterface(guid const &id, void **result) noexcept { - if (is_guid_of(id) || is_guid_of(id) || +int32_t __stdcall TaskDispatcherHandler::QueryInterface(guid const &id, void **result) noexcept { + if (is_guid_of(id) || is_guid_of(id) || is_guid_of(id)) { - *result = static_cast *>(this); + *result = static_cast *>(this); AddRef(); return impl::error_ok; } @@ -94,15 +93,15 @@ int32_t __stdcall TaskDispatchedHandler::QueryInterface(guid const &id, void **r return impl::error_no_interface; } -uint32_t __stdcall TaskDispatchedHandler::AddRef() noexcept { +uint32_t __stdcall TaskDispatcherHandler::AddRef() noexcept { return m_scheduler->AddHandlerRef(); } -uint32_t __stdcall TaskDispatchedHandler::Release() noexcept { +uint32_t __stdcall TaskDispatcherHandler::Release() noexcept { return m_scheduler->ReleaseHandlerRef(); } -int32_t __stdcall TaskDispatchedHandler::Invoke() noexcept { +int32_t __stdcall TaskDispatcherHandler::Invoke() noexcept { Mso::CntPtr queue; DispatchTask task; if (m_scheduler->TryTakeTask(queue, task)) { @@ -113,23 +112,22 @@ int32_t __stdcall TaskDispatchedHandler::Invoke() noexcept { } //============================================================================= -// UISchdulerWinRT implementation +// UISchedulerWinRT implementation //============================================================================= -UISchdulerWinRT::UISchdulerWinRT(CoreDispatcher &&coreDispatcher) noexcept - : m_coreDispatcher{std::move(coreDispatcher)} {} +UISchedulerWinRT::UISchedulerWinRT(DispatcherQueue &&dispatcher) noexcept : m_dispatcher{std::move(dispatcher)} {} -UISchdulerWinRT::~UISchdulerWinRT() noexcept { +UISchedulerWinRT::~UISchedulerWinRT() noexcept { AwaitTermination(); } -uint32_t UISchdulerWinRT::AddHandlerRef() noexcept { +uint32_t UISchedulerWinRT::AddHandlerRef() noexcept { std::lock_guard lock{m_mutex}; return ++m_handlerRefCount; } -uint32_t UISchdulerWinRT::ReleaseHandlerRef() noexcept { - Mso::CntPtr self; +uint32_t UISchedulerWinRT::ReleaseHandlerRef() noexcept { + Mso::CntPtr self; CleanupContext context{this}; { @@ -147,7 +145,7 @@ uint32_t UISchdulerWinRT::ReleaseHandlerRef() noexcept { } } -bool UISchdulerWinRT::TryTakeTask(Mso::CntPtr &queue, DispatchTask &task) noexcept { +bool UISchedulerWinRT::TryTakeTask(Mso::CntPtr &queue, DispatchTask &task) noexcept { { std::lock_guard lock{m_mutex}; VerifyElseCrashSz(m_taskCount, "Task count cannot be negative"); @@ -161,45 +159,47 @@ bool UISchdulerWinRT::TryTakeTask(Mso::CntPtr &queue, Dis return false; } -DispatchedHandler UISchdulerWinRT::MakeDispatchedHandler() noexcept { +DispatcherQueueHandler UISchedulerWinRT::MakeDispatcherQueueHandler() noexcept { VerifyElseCrash(m_mutex.IsLockedByMe()); if (m_handlerRefCount == 0) { - m_self = this; // Keep reference to self while CoreDispatcher owns DispatchedHandler. + m_self = this; // Keep reference to self while DispatcherQueue owns DispatcherQueueHandler. } ++m_handlerRefCount; - return {static_cast(&m_dispatchedHandler), take_ownership_from_abi}; + return {static_cast(&m_dispatcherHandler), take_ownership_from_abi}; } -void UISchdulerWinRT::IntializeScheduler(Mso::WeakPtr &&queue) noexcept { +void UISchedulerWinRT::IntializeScheduler(Mso::WeakPtr &&queue) noexcept { m_queue = std::move(queue); } -bool UISchdulerWinRT::HasThreadAccess() noexcept { - return m_coreDispatcher.HasThreadAccess(); +bool UISchedulerWinRT::HasThreadAccess() noexcept { + // m_dispatcher.HasThreadAccess() is implemented only in Windows 19H1. + // We must use an alternative implementation. + return m_threadId == std::this_thread::get_id(); } -bool UISchdulerWinRT::IsSerial() noexcept { +bool UISchedulerWinRT::IsSerial() noexcept { return true; } -void UISchdulerWinRT::Post() noexcept { - DispatchedHandler handler; +void UISchedulerWinRT::Post() noexcept { + DispatcherQueueHandler handler; { std::lock_guard lock{m_mutex}; if (!m_isShutdown) { ++m_taskCount; - handler = MakeDispatchedHandler(); + handler = MakeDispatcherQueueHandler(); } } if (handler) { - m_coreDispatcher.RunAsync(CoreDispatcherPriority::Normal, std::move(handler)); + m_dispatcher.TryEnqueue(handler); } } -void UISchdulerWinRT::Shutdown() noexcept { +void UISchedulerWinRT::Shutdown() noexcept { CleanupContext context{this}; { std::lock_guard lock{m_mutex}; @@ -208,18 +208,18 @@ void UISchdulerWinRT::Shutdown() noexcept { } } -void UISchdulerWinRT::AwaitTermination() noexcept { +void UISchedulerWinRT::AwaitTermination() noexcept { Shutdown(); m_terminationEvent.Wait(); } //============================================================================= -// UISchdulerWinRT::CleanupContext implementation +// UISchedulerWinRT::CleanupContext implementation //============================================================================= -UISchdulerWinRT::CleanupContext::CleanupContext(UISchdulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} +UISchedulerWinRT::CleanupContext::CleanupContext(UISchedulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} -UISchdulerWinRT::CleanupContext::~CleanupContext() noexcept { +UISchedulerWinRT::CleanupContext::~CleanupContext() noexcept { if (m_isTerminated) { m_scheduler->m_terminationEvent.Set(); } @@ -231,8 +231,8 @@ UISchdulerWinRT::CleanupContext::~CleanupContext() noexcept { } } -void UISchdulerWinRT::CleanupContext::CheckShutdown() noexcept { - // See if core dispatcher released all handlers without invoking them. +void UISchedulerWinRT::CleanupContext::CheckShutdown() noexcept { + // See if dispatcher queue released all handlers without invoking them. if (m_scheduler->m_taskCount != 0 && m_scheduler->m_handlerRefCount == 0) { m_isShutdown = true; m_scheduler->m_taskCount = 0; @@ -240,20 +240,16 @@ void UISchdulerWinRT::CleanupContext::CheckShutdown() noexcept { } } -void UISchdulerWinRT::CleanupContext::CheckTermination() noexcept { +void UISchedulerWinRT::CleanupContext::CheckTermination() noexcept { m_isTerminated = m_scheduler->m_isShutdown && (m_scheduler->m_handlerRefCount == 0); } //============================================================================= -// DispatchQueueStatic::MakeThreadPoolScheduler implementation +// DispatchQueueStatic::MakeCurrentThreadUIScheduler implementation //============================================================================= -/*static*/ Mso::CntPtr DispatchQueueStatic::MakeMainUIScheduler() noexcept { - return Mso::Make(CoreApplication::MainView().CoreWindow().Dispatcher()); -} - /*static*/ Mso::CntPtr DispatchQueueStatic::MakeCurrentThreadUIScheduler() noexcept { - return Mso::Make(CoreWindow::GetForCurrentThread().Dispatcher()); + return Mso::Make(DispatcherQueue::GetForCurrentThread()); } } // namespace Mso diff --git a/vnext/PropertySheets/React.Cpp.props b/vnext/PropertySheets/React.Cpp.props index 2bb87442726..f191493c0f9 100644 --- a/vnext/PropertySheets/React.Cpp.props +++ b/vnext/PropertySheets/React.Cpp.props @@ -27,7 +27,7 @@ 0.1.6 $(SolutionDir)packages\ReactNative.Hermes.Windows.$(HERMES_Version) - false + false 0.2.7 $(SolutionDir)packages\ReactNative.V8Jsi.Windows.$(V8_Version) @@ -46,6 +46,8 @@ ENABLE_ETW_TRACING;%(PreprocessorDefinitions) ENABLE_JS_SYSTRACE_TO_ETW;WITH_FBSYSTRACE;%(PreprocessorDefinitions) + USE_HERMES;%(PreprocessorDefinitions) + USE_V8;%(PreprocessorDefinitions) diff --git a/vnext/ReactUWP/Base/UwpReactInstance.cpp b/vnext/ReactUWP/Base/UwpReactInstance.cpp index 8fbabcb20ce..3f447c313b0 100644 --- a/vnext/ReactUWP/Base/UwpReactInstance.cpp +++ b/vnext/ReactUWP/Base/UwpReactInstance.cpp @@ -51,7 +51,7 @@ #include "V8JSIRuntimeHolder.h" #endif // USE_V8 -#include +#include #include #include "ChakraRuntimeHolder.h" @@ -87,7 +87,7 @@ struct UwpReactRedBoxHandler : Mso::React::IRedBoxHandler { Microsoft::Common::Unicode::Utf8ToUtf16(ss.str().c_str()), L"RedBox Error"); dlg.ShowAsync(); } - virtual bool isDevSupportEnabled() override { + virtual bool isDevSupportEnabled() const override { return true; } virtual void updateError(Mso::React::ErrorInfo &&) override {} @@ -115,7 +115,7 @@ void UwpReactInstance::Start(const std::shared_ptr &spThis, cons std::shared_ptr appTheme = std::make_shared(spThis, m_defaultNativeThread); I18nHelper::Instance().setInfo(I18nModule::GetI18nInfo()); - auto appearanceListener = Mso::Make(spThis); + auto appearanceListener = Mso::Make(spThis, Mso::DispatchQueue::MakeCurrentThreadUIQueue()); // TODO: Figure out threading. What thread should this really be on? m_initThread = std::make_unique(); diff --git a/vnext/ReactUWP/Modules/AppearanceModule.cpp b/vnext/ReactUWP/Modules/AppearanceModule.cpp index 8f4bba5fa6e..0405de975a2 100644 --- a/vnext/ReactUWP/Modules/AppearanceModule.cpp +++ b/vnext/ReactUWP/Modules/AppearanceModule.cpp @@ -14,10 +14,12 @@ using Method = facebook::xplat::module::CxxModule::Method; namespace react::uwp { -AppearanceChangeListener::AppearanceChangeListener(std::weak_ptr &&reactInstance) noexcept - : Mso::ActiveObject<>(Mso::DispatchQueue::MainUIQueue()), m_weakReactInstance(std::move(reactInstance)) { +AppearanceChangeListener::AppearanceChangeListener( + std::weak_ptr &&reactInstance, + Mso::DispatchQueue const &uiQueue) noexcept + : Mso::ActiveObject<>(uiQueue), m_weakReactInstance(std::move(reactInstance)) { // Ensure we're constructed on the UI thread - VerifyIsInQueueElseCrash(); + VerifyElseCrash(uiQueue.HasThreadAccess()); m_currentTheme = Application::Current().RequestedTheme(); diff --git a/vnext/ReactUWP/Modules/AppearanceModule.h b/vnext/ReactUWP/Modules/AppearanceModule.h index 275c36a3819..1e3b7c6268e 100644 --- a/vnext/ReactUWP/Modules/AppearanceModule.h +++ b/vnext/ReactUWP/Modules/AppearanceModule.h @@ -17,7 +17,7 @@ class AppearanceChangeListener final : public Mso::ActiveObject<> { using UISettings = winrt::Windows::UI::ViewManagement::UISettings; public: - AppearanceChangeListener(std::weak_ptr &&reactInstance) noexcept; + AppearanceChangeListener(std::weak_ptr &&reactInstance, Mso::DispatchQueue const &uiQueue) noexcept; const char *GetColorScheme() const noexcept; private: diff --git a/vnext/ReactUWP/ReactUWP.vcxproj b/vnext/ReactUWP/ReactUWP.vcxproj index ffddc8d89cb..7045c842381 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj +++ b/vnext/ReactUWP/ReactUWP.vcxproj @@ -90,8 +90,6 @@ --> CHAKRACORE;CHAKRACORE_UWP;%(PreprocessorDefinitions) USE_EDGEMODE_JSRT;%(PreprocessorDefinitions) - USE_HERMES;%(PreprocessorDefinitions) - USE_V8;%(PreprocessorDefinitions) REACTWINDOWS_BUILD; RN_PLATFORM=windows; diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index 30f2d7c7f54..cc8de5168af 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -106,6 +106,9 @@ Modules\Animated + + Modules + Modules @@ -166,6 +169,7 @@ Polyester + Threading @@ -322,8 +326,6 @@ Views - - @@ -482,6 +484,9 @@ Modules\Animated + + Modules + Modules @@ -657,7 +662,6 @@ Views - diff --git a/vnext/ReactUWP/Views/ControlViewManager.cpp b/vnext/ReactUWP/Views/ControlViewManager.cpp index 0b26c602d03..e91cb4a0226 100644 --- a/vnext/ReactUWP/Views/ControlViewManager.cpp +++ b/vnext/ReactUWP/Views/ControlViewManager.cpp @@ -58,15 +58,9 @@ bool ControlViewManager::UpdateProperty( } else if (propertyName == "tabIndex") { if (propertyValue.isNumber()) { auto tabIndex = propertyValue.asDouble(); - if (tabIndex == static_cast(tabIndex)) { - if (tabIndex < 0) { - control.IsTabStop(false); - control.ClearValue(xaml::Controls::Control::TabIndexProperty()); - } else { - control.IsTabStop(true); - control.TabIndex(static_cast(tabIndex)); - } - } + if (tabIndex == static_cast(tabIndex)) + control.ClearValue(xaml::Controls::Control::TabIndexProperty()); + control.TabIndex(static_cast(tabIndex)); } else if (propertyValue.isNull()) { control.ClearValue(xaml::Controls::Control::TabIndexProperty()); } diff --git a/vnext/ReactUWP/Views/Image/ReactImage.cpp b/vnext/ReactUWP/Views/Image/ReactImage.cpp index 791a3776ecc..7ec70a89aca 100644 --- a/vnext/ReactUWP/Views/Image/ReactImage.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImage.cpp @@ -213,10 +213,8 @@ winrt::fire_and_forget ReactImage::SetBackground(bool fireLoadEndEvent) { if (createImageBrush) { imageBrush = winrt::ImageBrush{}; - // ImageOpened and ImageFailed are mutually exclusive. One event of the other will - // always fire whenever an ImageBrush has the ImageSource value set or reset. - strong_this->m_imageBrushOpenedRevoker = imageBrush.ImageOpened( - winrt::auto_revoke, [weak_this, imageBrush, fireLoadEndEvent](const auto &, const auto &) { + strong_this->m_imageBrushOpenedRevoker = + imageBrush.ImageOpened(winrt::auto_revoke, [weak_this, imageBrush](const auto &, const auto &) { if (auto strong_this{weak_this.get()}) { if (auto bitmap{imageBrush.ImageSource().try_as()}) { strong_this->m_imageSource.height = bitmap.PixelHeight(); @@ -224,18 +222,6 @@ winrt::fire_and_forget ReactImage::SetBackground(bool fireLoadEndEvent) { } imageBrush.Stretch(strong_this->ResizeModeToStretch(strong_this->m_resizeMode)); - - if (fireLoadEndEvent) { - strong_this->m_onLoadEndEvent(*strong_this, true); - } - } - }); - - strong_this->m_imageBrushFailedRevoker = - imageBrush.ImageFailed(winrt::auto_revoke, [weak_this, fireLoadEndEvent](const auto &, const auto &) { - const auto strong_this{weak_this.get()}; - if (strong_this && fireLoadEndEvent) { - strong_this->m_onLoadEndEvent(*strong_this, false); } }); } @@ -274,7 +260,24 @@ winrt::fire_and_forget ReactImage::SetBackground(bool fireLoadEndEvent) { bitmapImage = winrt::BitmapImage{}; strong_this->m_bitmapImageOpened = bitmapImage.ImageOpened( - winrt::auto_revoke, [imageBrush](const auto &, const auto &) { imageBrush.Opacity(1); }); + winrt::auto_revoke, [imageBrush, weak_this, fireLoadEndEvent](const auto &, const auto &) { + imageBrush.Opacity(1); + + auto strong_this{weak_this.get()}; + if (strong_this && fireLoadEndEvent) { + strong_this->m_onLoadEndEvent(*strong_this, true); + } + }); + + strong_this->m_bitmapImageFailed = bitmapImage.ImageFailed( + winrt::auto_revoke, [imageBrush, weak_this, fireLoadEndEvent](const auto &, const auto &) { + imageBrush.Opacity(1); + + auto strong_this{weak_this.get()}; + if (strong_this && fireLoadEndEvent) { + strong_this->m_onLoadEndEvent(*strong_this, false); + } + }); imageBrush.ImageSource(bitmapImage); } diff --git a/vnext/ReactUWP/Views/Image/ReactImage.h b/vnext/ReactUWP/Views/Image/ReactImage.h index 4a8ac619460..e73ca246581 100644 --- a/vnext/ReactUWP/Views/Image/ReactImage.h +++ b/vnext/ReactUWP/Views/Image/ReactImage.h @@ -67,8 +67,8 @@ struct ReactImage : xaml::Controls::GridT { xaml::FrameworkElement::SizeChanged_revoker m_sizeChangedRevoker; xaml::Media::LoadedImageSurface::LoadCompleted_revoker m_surfaceLoadedRevoker; xaml::Media::Imaging::BitmapImage::ImageOpened_revoker m_bitmapImageOpened; + xaml::Media::Imaging::BitmapImage::ImageFailed_revoker m_bitmapImageFailed; xaml::Media::ImageBrush::ImageOpened_revoker m_imageBrushOpenedRevoker; - xaml::Media::ImageBrush::ImageFailed_revoker m_imageBrushFailedRevoker; xaml::Media::Imaging::SvgImageSource::Opened_revoker m_svgImageSourceOpenedRevoker; xaml::Media::Imaging::SvgImageSource::OpenFailed_revoker m_svgImageSourceOpenFailedRevoker; }; diff --git a/vnext/ReactUWP/Views/ViewViewManager.cpp b/vnext/ReactUWP/Views/ViewViewManager.cpp index efc490184a1..74b4efe12a8 100644 --- a/vnext/ReactUWP/Views/ViewViewManager.cpp +++ b/vnext/ReactUWP/Views/ViewViewManager.cpp @@ -87,15 +87,8 @@ class ViewShadowNode : public ShadowNodeBase { void TabIndex(int32_t tabIndex) { m_tabIndex = tabIndex; - if (IsControl()) { - if (tabIndex < 0) { - GetControl().IsTabStop(false); - GetControl().ClearValue(xaml::Controls::Control::TabIndexProperty()); - } else { - GetControl().IsTabStop(true); - GetControl().TabIndex(tabIndex); - } - } + if (IsControl()) + GetControl().TabIndex(m_tabIndex); } bool OnClick() { @@ -213,7 +206,7 @@ class ViewShadowNode : public ShadowNodeBase { bool m_enableFocusRing = true; bool m_onClick = false; - int32_t m_tabIndex = -1; + int32_t m_tabIndex = std::numeric_limits::max(); xaml::Controls::ContentControl::GotFocus_revoker m_contentControlGotFocusRevoker{}; xaml::Controls::ContentControl::LostFocus_revoker m_contentControlLostFocusRevoker{}; @@ -375,7 +368,7 @@ bool ViewViewManager::UpdateProperty( pViewShadowNode->TabIndex(static_cast(tabIndex)); } } else if (propertyValue.isNull()) { - pViewShadowNode->TabIndex(-1); + pViewShadowNode->TabIndex(std::numeric_limits::max()); } } else { ret = Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); diff --git a/vnext/ReactWindows-Desktop.sln b/vnext/ReactWindows-Desktop.sln index 8e60ffd4d45..6e3ddae5d0a 100644 --- a/vnext/ReactWindows-Desktop.sln +++ b/vnext/ReactWindows-Desktop.sln @@ -128,7 +128,6 @@ Global Mso\Mso.vcxitems*{1958ceaa-fbe0-44e3-8a99-90ad85531ffe}*SharedItemsImports = 4 Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 Chakra\Chakra.vcxitems*{6f354505-fe3a-4bd2-a9a6-d12bbf37a85c}*SharedItemsImports = 4 - Shared\Shared.vcxitems*{6f354505-fe3a-4bd2-a9a6-d12bbf37a85c}*SharedItemsImports = 4 Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 Chakra\Chakra.vcxitems*{95048601-c3dc-475f-adf8-7c0c764c10d5}*SharedItemsImports = 4 Mso\Mso.vcxitems*{95048601-c3dc-475f-adf8-7c0c764c10d5}*SharedItemsImports = 4 diff --git a/vnext/ReactWindowsCore/DevSettings.h b/vnext/ReactWindowsCore/DevSettings.h index 44808a5e4d9..061ea4259c4 100644 --- a/vnext/ReactWindowsCore/DevSettings.h +++ b/vnext/ReactWindowsCore/DevSettings.h @@ -5,7 +5,7 @@ #include "Logging.h" #include "MemoryTracker.h" -#include +#include #include #include #include diff --git a/vnext/ReactWindowsCore/RedBoxHandler.h b/vnext/ReactWindowsCore/IRedBoxHandler.h similarity index 94% rename from vnext/ReactWindowsCore/RedBoxHandler.h rename to vnext/ReactWindowsCore/IRedBoxHandler.h index aeabb4f9f57..fea578a782b 100644 --- a/vnext/ReactWindowsCore/RedBoxHandler.h +++ b/vnext/ReactWindowsCore/IRedBoxHandler.h @@ -28,7 +28,7 @@ struct ErrorInfo { struct IRedBoxHandler { virtual void showNewError(ErrorInfo &&, ErrorType) = 0; - virtual bool isDevSupportEnabled() = 0; + virtual bool isDevSupportEnabled() const = 0; virtual void updateError(ErrorInfo &&) = 0; virtual void dismissRedbox() = 0; }; diff --git a/vnext/ReactWindowsCore/Modules/ExceptionsManagerModule.h b/vnext/ReactWindowsCore/Modules/ExceptionsManagerModule.h index be44080f41e..80f314a5918 100644 --- a/vnext/ReactWindowsCore/Modules/ExceptionsManagerModule.h +++ b/vnext/ReactWindowsCore/Modules/ExceptionsManagerModule.h @@ -4,7 +4,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/vnext/ReactWindowsCore/ReactWindowsCore.vcxitems b/vnext/ReactWindowsCore/ReactWindowsCore.vcxitems index 13e56fc783b..0380d8cc221 100644 --- a/vnext/ReactWindowsCore/ReactWindowsCore.vcxitems +++ b/vnext/ReactWindowsCore/ReactWindowsCore.vcxitems @@ -66,10 +66,6 @@ - - - - @@ -102,12 +98,10 @@ CHAKRACORE; %(PreprocessorDefinitions) - USE_HERMES;%(PreprocessorDefinitions) $(ReactNativeWindowsDir); $(ReactNativeWindowsDir)Common; $(ReactNativeWindowsDir)Shared; - $(ReactNativeWindowsDir)include; $(ReactNativeWindowsDir)include\ReactWindowsCore; $(ReactNativeDir)\ReactCommon; $(ReactNativeDir)\ReactCommon\callinvoker; @@ -174,7 +168,7 @@ - + @@ -232,12 +226,6 @@ {fca38f3c-7c73-4c47-be4e-32f77fa8538d} - - {a990658c-ce31-4bcc-976f-0fc6b1af693d} - - - {a9d95a91-4db7-4f72-beb6-fe8a5c89bfbd} - diff --git a/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters b/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters index 7db04cf3419..4c1a4081cd6 100644 --- a/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters +++ b/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters @@ -239,7 +239,7 @@ Header Files\Pch - + Header Files diff --git a/vnext/Scripts/ReactWin32.nuspec b/vnext/Scripts/ReactWin32.nuspec index a61bbe5631d..c54093c805e 100644 --- a/vnext/Scripts/ReactWin32.nuspec +++ b/vnext/Scripts/ReactWin32.nuspec @@ -34,7 +34,7 @@ - + diff --git a/vnext/Scripts/rnw-dependencies.ps1 b/vnext/Scripts/rnw-dependencies.ps1 new file mode 100644 index 00000000000..c274c09006f --- /dev/null +++ b/vnext/Scripts/rnw-dependencies.ps1 @@ -0,0 +1,155 @@ +# Troubleshoot RNW dependencies +param([switch]$Install = $false, [switch]$NoPrompt = $false) +$vsWorkloads = @( 'Microsoft.Component.MSBuild', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.ComponentGroup.UWP.Support'); + +$v = [System.Environment]::OSVersion.Version; +if ($env:Agent_BuildDirectory) { + $drive = (Resolve-Path $env:Agent_BuildDirectory).Drive +} else { + if ($PSCommandPath) { + $drive = (Resolve-Path $PSCommandPath).Drive + } else { + $drive = (Resolve-Path $env:SystemDrive).Drive + } +} + +function CheckVS { + $vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + if (!(Test-Path $vsWhere)) { + return $false; + } + $output = & $vsWhere -version 16 -requires $vsWorkloads -property productPath + return Test-Path $output; +} + +function InstallVS { + $installerPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer"; + $vsWhere = "$installerPath\vswhere.exe" + if (!(Test-Path $vsWhere)) { + # No VSWhere / VS_Installer + & choco install -y visualstudio2019community + } + $channelId = & $vsWhere -version 16 -property channelId + $productId = & $vsWhere -version 16 -property productId + $vsInstaller = "$installerPath\vs_installer.exe" + $addWorkloads = $vsWorkloads | % { '--add', $_ }; + Start-Process -PassThru -Wait -Path $vsInstaller -ArgumentList ("install --channelId $channelId --productId $productId $addWorkloads --quiet" -split ' ') + +} + +function CheckNode { + try { + $v = (Get-Command node).Version.Major + return $v -eq 12 -or $v -eq 13 + } catch { + return $false; + } +} + +function GetChocoPkgVersion{ + params([string]$packageId) + [version]$version = (& choco list --local-only $packageId -r -e).Substring($packageId.Length + 1); + return $version; +} + +$requiredFreeSpaceGB = 15; + +$requirements = @( + @{ + Name = "Free space on $drive`: > $requiredFreeSpaceGB GB"; + Valid = $drive.Free/1GB -gt $requiredFreeSpaceGB; + Optional = $true # this requirement is fuzzy + }, + @{ + Name = 'Windows version > 10.0.16299.0'; + Valid = ($v.Major -eq 10 -and $v.Minor -eq 0 -and $v.Build -ge 16299) + }, + @{ + Name = 'Developer mode is on'; + Valid = try { (Get-WindowsDeveloperLicense).IsValid } catch { $false } + }, + @{ + Name = 'git'; + Valid = try { (Get-Command git.exe) -ne $null } catch { $false } + } + @{ + Name = 'Choco'; + Valid = try { (Get-Command choco) -ne $null } catch { $false }; + Install = { + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; + iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + }; + }, + @{ + Name = 'VS 2019 with UWP and Desktop/C++'; + Valid = CheckVS + }, + @{ + Name = 'NodeJS 12 or 13 installed'; + Valid = CheckNode + Install = { choco install -y nodejs.install --version=12.9.1 } + }, + @{ + Name = 'Chrome'; + Valid = try { ((Get-Item (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe').'(Default)').VersionInfo).ProductMajorPart + } catch { $false } ; + Install = { choco install -y GoogleChrome } + }, + @{ + Name = 'Yarn'; + Valid = try { (Get-Command yarn) -ne $null } catch { $false }; + Install = { choco install -y yarn } + }, + @{ + Name = 'Appium'; + Valid = (Test-Path "${env:ProgramFiles}\Appium\Appium.exe"); + Install = { choco install -y Appium-desktop }; + Optional = $true + }, + @{ + Name = 'WinAppDriver'; + Valid = (Test-Path "${env:ProgramFiles(x86)}\Windows Application Driver\WinAppDriver.exe"); + Install = { choco install -y WinAppDriver }; + } + + ); + +function IsElevated { + return [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544"); +} + +if (!(IsElevated)) { + Write-Output "rnw-dependencies - this script must run elevated. Exiting."; + return; +} + +$NeedsRerun = $false +foreach ($req in $requirements) +{ + Write-Host -NoNewline "Checking $($req.Name) "; + if (!($req.Valid)) { + Write-Host -ForegroundColor Red " Failed"; + if ($req.Install) { + if ($Install -or (!$NoPrompt -and (Read-Host "Do you want to install? ").ToUpperInvariant() -eq 'Y')) { + Invoke-Command $req.Install -ErrorAction Stop + if ($LASTEXITCODE -ne 0) { throw "Last exit code was non-zero: $LASTEXITCODE"; } + } elseif (!$req.Optional) { + $NeedsRerun = $true; + } + } else { + $NeedsRerun = !($req.Optional); + } + } else { + Write-Host -ForegroundColor Green " OK"; + } +} + +if ($NeedsRerun) { + Write-Error "Some dependencies are not met. Re-run with -Install to install them."; + if (!$NoPrompt) { + [System.Console]::ReadKey(); + } + throw; +} else { + Write-Output "All mandatory requirements met"; +} diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index c426153725d..4d31e11fc82 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -8,8 +8,6 @@ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) - USE_HERMES;%(PreprocessorDefinitions) - USE_V8;%(PreprocessorDefinitions) diff --git a/vnext/Universal.UnitTests/React.Windows.Universal.UnitTests.vcxproj b/vnext/Universal.UnitTests/React.Windows.Universal.UnitTests.vcxproj index 51e9f3f11bd..c138c10390e 100644 --- a/vnext/Universal.UnitTests/React.Windows.Universal.UnitTests.vcxproj +++ b/vnext/Universal.UnitTests/React.Windows.Universal.UnitTests.vcxproj @@ -121,9 +121,6 @@ {a990658c-ce31-4bcc-976f-0fc6b1af693d} - - {a9d95a91-4db7-4f72-beb6-fe8a5c89bfbd} - {2d5d43d9-cffc-4c40-b4cd-02efb4e2742b} diff --git a/vnext/V8Inspector/V8Inspector.vcxproj b/vnext/V8Inspector/V8Inspector.vcxproj deleted file mode 100644 index 3522fe72dde..00000000000 --- a/vnext/V8Inspector/V8Inspector.vcxproj +++ /dev/null @@ -1,122 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - {F1B91D19-79E4-461B-AC8E-E3E6E747F65F} - Win32Proj - V8Inspector - 10.0.18362.0 - - - - StaticLibrary - true - Unicode - - - StaticLibrary - false - true - Unicode - - - StaticLibrary - true - Unicode - - - StaticLibrary - false - true - Unicode - - - - - - - - - - - - - - - - - - - - - - NotUsing - false - true - $(ReactNativeDir)\ReactCommon;$(JSI_Source);%(AdditionalIncludeDirectories) - - BOOST_ASIO_DISABLE_BOOST_REGEX; - BOOST_ERROR_CODE_HEADER_ONLY; - BOOST_ASIO_HAS_IOCP; - _WIN32_WINNT=$(WinVer); - WIN32; - _WINDOWS; - _WIN32; - JSI_EXPORT=; - %(PreprocessorDefinitions) - - - false - - - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file diff --git a/vnext/V8Inspector/V8Inspector.vcxproj.filters b/vnext/V8Inspector/V8Inspector.vcxproj.filters deleted file mode 100644 index 1097daff606..00000000000 --- a/vnext/V8Inspector/V8Inspector.vcxproj.filters +++ /dev/null @@ -1,63 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/vnext/package.json b/vnext/package.json index b55fcc9940c..e8d93f226d0 100644 --- a/vnext/package.json +++ b/vnext/package.json @@ -1,6 +1,6 @@ { "name": "react-native-windows", - "version": "0.0.0-master.62", + "version": "0.0.0-master.65", "license": "MIT", "repository": { "type": "git", diff --git a/yarn.lock b/yarn.lock index 8dccb129849..6633ac2c3d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1722,14 +1722,14 @@ dependencies: core-js "^2.5.7" -"@lerna/add@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.20.0.tgz#bea7edf36fc93fb72ec34cb9ba854c48d4abf309" - integrity sha512-AnH1oRIEEg/VDa3SjYq4x1/UglEAvrZuV0WssHUMN81RTZgQk3we+Mv3qZNddrZ/fBcZu2IAdN/EQ3+ie2JxKQ== +"@lerna/add@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" + integrity sha512-vhUXXF6SpufBE1EkNEXwz1VLW03f177G9uMOFMQkp6OJ30/PWg4Ekifuz9/3YfgB2/GH8Tu4Lk3O51P2Hskg/A== dependencies: "@evocateur/pacote" "^9.6.3" - "@lerna/bootstrap" "3.20.0" - "@lerna/command" "3.18.5" + "@lerna/bootstrap" "3.21.0" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/npm-conf" "3.16.0" "@lerna/validation-error" "3.13.0" @@ -1738,12 +1738,12 @@ p-map "^2.1.0" semver "^6.2.0" -"@lerna/bootstrap@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.20.0.tgz#635d71046830f208e851ab429a63da1747589e37" - integrity sha512-Wylullx3uthKE7r4izo09qeRGL20Y5yONlQEjPCfnbxCC2Elu+QcPu4RC6kqKQ7b+g7pdC3OOgcHZjngrwr5XQ== +"@lerna/bootstrap@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.21.0.tgz#bcd1b651be5b0970b20d8fae04c864548123aed6" + integrity sha512-mtNHlXpmvJn6JTu0KcuTTPl2jLsDNud0QacV/h++qsaKbhAaJr/FElNZ5s7MwZFUM3XaDmvWzHKaszeBMHIbBw== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/has-npm-version" "3.16.5" "@lerna/npm-install" "3.16.5" @@ -1767,13 +1767,13 @@ read-package-tree "^5.1.6" semver "^6.2.0" -"@lerna/changed@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.20.0.tgz#66b97ebd6c8f8d207152ee524a0791846a9097ae" - integrity sha512-+hzMFSldbRPulZ0vbKk6RD9f36gaH3Osjx34wrrZ62VB4pKmjyuS/rxVYkCA3viPLHoiIw2F8zHM5BdYoDSbjw== +"@lerna/changed@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.21.0.tgz#108e15f679bfe077af500f58248c634f1044ea0b" + integrity sha512-hzqoyf8MSHVjZp0gfJ7G8jaz+++mgXYiNs9iViQGA8JlN/dnWLI5sWDptEH3/B30Izo+fdVz0S0s7ydVE3pWIw== dependencies: "@lerna/collect-updates" "3.20.0" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/listable" "3.18.5" "@lerna/output" "3.13.0" @@ -1795,12 +1795,12 @@ execa "^1.0.0" strong-log-transformer "^2.0.0" -"@lerna/clean@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.20.0.tgz#ba777e373ddeae63e57860df75d47a9e5264c5b2" - integrity sha512-9ZdYrrjQvR5wNXmHfDsfjWjp0foOkCwKe3hrckTzkAeQA1ibyz5llGwz5e1AeFrV12e2/OLajVqYfe+qdkZUgg== +"@lerna/clean@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.21.0.tgz#c0b46b5300cc3dae2cda3bec14b803082da3856d" + integrity sha512-b/L9l+MDgE/7oGbrav6rG8RTQvRiZLO1zTcG17zgJAAuhlsPxJExMlh2DFwJEVi2les70vMhHfST3Ue1IMMjpg== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/prompt" "3.18.5" "@lerna/pulse-till-done" "3.13.0" @@ -1840,14 +1840,14 @@ npmlog "^4.1.2" slash "^2.0.0" -"@lerna/command@3.18.5": - version "3.18.5" - resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.18.5.tgz#14c6d2454adbfd365f8027201523e6c289cd3cd9" - integrity sha512-36EnqR59yaTU4HrR1C9XDFti2jRx0BgpIUBeWn129LZZB8kAB3ov1/dJNa1KcNRKp91DncoKHLY99FZ6zTNpMQ== +"@lerna/command@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.21.0.tgz#9a2383759dc7b700dacfa8a22b2f3a6e190121f7" + integrity sha512-T2bu6R8R3KkH5YoCKdutKv123iUgUbW8efVjdGCDnCMthAQzoentOJfDeodBwn0P2OqCl3ohsiNVtSn9h78fyQ== dependencies: "@lerna/child-process" "3.16.5" "@lerna/package-graph" "3.18.5" - "@lerna/project" "3.18.0" + "@lerna/project" "3.21.0" "@lerna/validation-error" "3.13.0" "@lerna/write-log-file" "3.13.0" clone-deep "^4.0.1" @@ -1882,14 +1882,14 @@ fs-extra "^8.1.0" npmlog "^4.1.2" -"@lerna/create@3.18.5": - version "3.18.5" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.18.5.tgz#11ac539f069248eaf7bc4c42e237784330f4fc47" - integrity sha512-cHpjocbpKmLopCuZFI7cKEM3E/QY8y+yC7VtZ4FQRSaLU8D8i2xXtXmYaP1GOlVNavji0iwoXjuNpnRMInIr2g== +"@lerna/create@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.21.0.tgz#e813832adf3488728b139e5a75c8b01b1372e62f" + integrity sha512-cRIopzKzE2vXJPmsiwCDMWo4Ct+KTmX3nvvkQLDoQNrrRK7w+3KQT3iiorbj1koD95RsVQA7mS2haWok9SIv0g== dependencies: "@evocateur/pacote" "^9.6.3" "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/npm-conf" "3.16.0" "@lerna/validation-error" "3.13.0" camelcase "^5.0.0" @@ -1914,23 +1914,23 @@ "@lerna/child-process" "3.16.5" npmlog "^4.1.2" -"@lerna/diff@3.18.5": - version "3.18.5" - resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.18.5.tgz#e9e2cb882f84d5b84f0487c612137305f07accbc" - integrity sha512-u90lGs+B8DRA9Z/2xX4YaS3h9X6GbypmGV6ITzx9+1Ga12UWGTVlKaCXBgONMBjzJDzAQOK8qPTwLA57SeBLgA== +"@lerna/diff@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.21.0.tgz#e6df0d8b9916167ff5a49fcb02ac06424280a68d" + integrity sha512-5viTR33QV3S7O+bjruo1SaR40m7F2aUHJaDAC7fL9Ca6xji+aw1KFkpCtVlISS0G8vikUREGMJh+c/VMSc8Usw== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/validation-error" "3.13.0" npmlog "^4.1.2" -"@lerna/exec@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.20.0.tgz#29f0c01aee2340eb46f90706731fef2062a49639" - integrity sha512-pS1mmC7kzV668rHLWuv31ClngqeXjeHC8kJuM+W2D6IpUVMGQHLcCTYLudFgQsuKGVpl0DGNYG+sjLhAPiiu6A== +"@lerna/exec@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.21.0.tgz#17f07533893cb918a17b41bcc566dc437016db26" + integrity sha512-iLvDBrIE6rpdd4GIKTY9mkXyhwsJ2RvQdB9ZU+/NhR3okXfqKc6py/24tV111jqpXTtZUW6HNydT4dMao2hi1Q== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/profiler" "3.20.0" "@lerna/run-topologically" "3.18.5" @@ -2006,13 +2006,13 @@ "@lerna/child-process" "3.16.5" semver "^6.2.0" -"@lerna/import@3.18.5": - version "3.18.5" - resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.18.5.tgz#a9c7d8601870729851293c10abd18b3707f7ba5e" - integrity sha512-PH0WVLEgp+ORyNKbGGwUcrueW89K3Iuk/DDCz8mFyG2IG09l/jOF0vzckEyGyz6PO5CMcz4TI1al/qnp3FrahQ== +"@lerna/import@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.21.0.tgz#87b08f2a2bfeeff7357c6fd8490e638d3cd5b32d" + integrity sha512-aISkL4XD0Dqf5asDaOZWu65jgj8fWUhuQseZWuQe3UfHxav69fTS2YLIngUfencaOSZVOcVCom28YCzp61YDxw== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/prompt" "3.18.5" "@lerna/pulse-till-done" "3.13.0" "@lerna/validation-error" "3.13.0" @@ -2020,43 +2020,43 @@ fs-extra "^8.1.0" p-map-series "^1.0.0" -"@lerna/info@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/info/-/info-3.20.0.tgz#3a5212f3029f2bc6255f9533bdf4bcb120ef329a" - integrity sha512-Rsz+KQF9mczbGUbPTrtOed1N0C+cA08Qz0eX/oI+NNjvsryZIju/o7uedG4I3P55MBiAioNrJI88fHH3eTgYug== +"@lerna/info@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/info/-/info-3.21.0.tgz#76696b676fdb0f35d48c83c63c1e32bb5e37814f" + integrity sha512-0XDqGYVBgWxUquFaIptW2bYSIu6jOs1BtkvRTWDDhw4zyEdp6q4eaMvqdSap1CG+7wM5jeLCi6z94wS0AuiuwA== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/output" "3.13.0" envinfo "^7.3.1" -"@lerna/init@3.18.5": - version "3.18.5" - resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.18.5.tgz#86dd0b2b3290755a96975069b5cb007f775df9f5" - integrity sha512-oCwipWrha98EcJAHm8AGd2YFFLNI7AW9AWi0/LbClj1+XY9ah+uifXIgYGfTk63LbgophDd8936ZEpHMxBsbAg== +"@lerna/init@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.21.0.tgz#1e810934dc8bf4e5386c031041881d3b4096aa5c" + integrity sha512-6CM0z+EFUkFfurwdJCR+LQQF6MqHbYDCBPyhu/d086LRf58GtYZYj49J8mKG9ktayp/TOIxL/pKKjgLD8QBPOg== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" fs-extra "^8.1.0" p-map "^2.1.0" write-json-file "^3.2.0" -"@lerna/link@3.18.5": - version "3.18.5" - resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.18.5.tgz#f24347e4f0b71d54575bd37cfa1794bc8ee91b18" - integrity sha512-xTN3vktJpkT7Nqc3QkZRtHO4bT5NvuLMtKNIBDkks0HpGxC9PRyyqwOoCoh1yOGbrWIuDezhfMg3Qow+6I69IQ== +"@lerna/link@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.21.0.tgz#8be68ff0ccee104b174b5bbd606302c2f06e9d9b" + integrity sha512-tGu9GxrX7Ivs+Wl3w1+jrLi1nQ36kNI32dcOssij6bg0oZ2M2MDEFI9UF2gmoypTaN9uO5TSsjCFS7aR79HbdQ== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/package-graph" "3.18.5" "@lerna/symlink-dependencies" "3.17.0" p-map "^2.1.0" slash "^2.0.0" -"@lerna/list@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.20.0.tgz#7e67cc29c5cf661cfd097e8a7c2d3dcce7a81029" - integrity sha512-fXTicPrfioVnRzknyPawmYIVkzDRBaQqk9spejS1S3O1DOidkihK0xxNkr8HCVC0L22w6f92g83qWDp2BYRUbg== +"@lerna/list@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.21.0.tgz#42f76fafa56dea13b691ec8cab13832691d61da2" + integrity sha512-KehRjE83B1VaAbRRkRy6jLX1Cin8ltsrQ7FHf2bhwhRHK0S54YuA6LOoBnY/NtA8bHDX/Z+G5sMY78X30NS9tg== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/listable" "3.18.5" "@lerna/output" "3.13.0" @@ -2202,10 +2202,10 @@ npmlog "^4.1.2" upath "^1.2.0" -"@lerna/project@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.18.0.tgz#56feee01daeb42c03cbdf0ed8a2a10cbce32f670" - integrity sha512-+LDwvdAp0BurOAWmeHE3uuticsq9hNxBI0+FMHiIai8jrygpJGahaQrBYWpwbshbQyVLeQgx3+YJdW2TbEdFWA== +"@lerna/project@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.21.0.tgz#5d784d2d10c561a00f20320bcdb040997c10502d" + integrity sha512-xT1mrpET2BF11CY32uypV2GPtPVm6Hgtha7D81GQP9iAitk9EccrdNjYGt5UBYASl4CIDXBRxwmTTVGfrCx82A== dependencies: "@lerna/package" "3.16.0" "@lerna/validation-error" "3.13.0" @@ -2228,10 +2228,10 @@ inquirer "^6.2.0" npmlog "^4.1.2" -"@lerna/publish@3.20.2": - version "3.20.2" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.20.2.tgz#a45d29813099b3249657ea913d0dc3f8ebc5cc2e" - integrity sha512-N7Y6PdhJ+tYQPdI1tZum8W25cDlTp4D6brvRacKZusweWexxaopbV8RprBaKexkEX/KIbncuADq7qjDBdQHzaA== +"@lerna/publish@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.21.0.tgz#0112393125f000484c3f50caba71a547f91bd7f4" + integrity sha512-JZ+ehZB9UCQ9nqH8Ld/Yqc/If++aK/7XIubkrB9sQ5hf2GeIbmI/BrJpMgLW/e9T5bKrUBZPUvoUN3daVipA5A== dependencies: "@evocateur/libnpmaccess" "^3.1.2" "@evocateur/npm-registry-fetch" "^4.0.0" @@ -2239,7 +2239,7 @@ "@lerna/check-working-tree" "3.16.5" "@lerna/child-process" "3.16.5" "@lerna/collect-updates" "3.20.0" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/describe-ref" "3.16.5" "@lerna/log-packed" "3.16.0" "@lerna/npm-conf" "3.16.0" @@ -2254,7 +2254,7 @@ "@lerna/run-lifecycle" "3.16.2" "@lerna/run-topologically" "3.18.5" "@lerna/validation-error" "3.13.0" - "@lerna/version" "3.20.2" + "@lerna/version" "3.21.0" figgy-pudding "^3.5.1" fs-extra "^8.1.0" npm-package-arg "^6.1.0" @@ -2317,12 +2317,12 @@ figgy-pudding "^3.5.1" p-queue "^4.0.0" -"@lerna/run@3.20.0": - version "3.20.0" - resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.20.0.tgz#a479f7c42bdf9ebabb3a1e5a2bdebb7a8d201151" - integrity sha512-9U3AqeaCeB7KsGS9oyKNp62s9vYoULg/B4cqXTKZkc+OKL6QOEjYHYVSBcMK9lUXrMjCjDIuDSX3PnTCPxQ2Dw== +"@lerna/run@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.21.0.tgz#2a35ec84979e4d6e42474fe148d32e5de1cac891" + integrity sha512-fJF68rT3veh+hkToFsBmUJ9MHc9yGXA7LSDvhziAojzOb0AI/jBDp6cEcDQyJ7dbnplba2Lj02IH61QUf9oW0Q== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/npm-run-script" "3.16.5" "@lerna/output" "3.13.0" @@ -2367,15 +2367,15 @@ dependencies: npmlog "^4.1.2" -"@lerna/version@3.20.2": - version "3.20.2" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.20.2.tgz#3709141c0f537741d9bc10cb24f56897bcb30428" - integrity sha512-ckBJMaBWc+xJen0cMyCE7W67QXLLrc0ELvigPIn8p609qkfNM0L0CF803MKxjVOldJAjw84b8ucNWZLvJagP/Q== +"@lerna/version@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.21.0.tgz#5bcc3d2de9eb8f4db18efb0d88973f9a509eccc3" + integrity sha512-nIT3u43fCNj6uSMN1dRxFnF4GhmIiOEqSTkGSjrMU+8kHKwzOqS/6X6TOzklBmCyEZOpF/fLlGqH3BZHnwLDzQ== dependencies: "@lerna/check-working-tree" "3.16.5" "@lerna/child-process" "3.16.5" "@lerna/collect-updates" "3.20.0" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/conventional-commits" "3.18.5" "@lerna/github-client" "3.16.5" "@lerna/gitlab-client" "3.15.0" @@ -6307,9 +6307,9 @@ eslint-plugin-react-native@3.8.1: eslint-plugin-react-native-globals "^0.1.1" eslint-plugin-react@7.19.0, eslint-plugin-react@^7.14.1: - version "7.19.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666" - integrity sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ== + version "7.20.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.20.0.tgz#f98712f0a5e57dfd3e5542ef0604b8739cd47be3" + integrity sha512-rqe1abd0vxMjmbPngo4NaYxTcR3Y4Hrmc/jg4T+sYz63yqlmJRknpEQfmWY+eDWPuMmix6iUIK+mv0zExjeLgA== dependencies: array-includes "^3.1.1" doctrine "^2.1.0" @@ -6320,7 +6320,6 @@ eslint-plugin-react@7.19.0, eslint-plugin-react@^7.14.1: object.values "^1.1.1" prop-types "^15.7.2" resolve "^1.15.1" - semver "^6.3.0" string.prototype.matchall "^4.0.2" xregexp "^4.3.0" @@ -6999,9 +6998,9 @@ forwarded@~0.1.2: integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= fp-ts@^2.5.0: - version "2.5.4" - resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.5.4.tgz#f184079aaa78403ea66458517be7787e0d593429" - integrity sha512-cZlLeEneRYypc2dOzB9h8+bd9mQhJVyt2g0Dny2gKR7uWNgA4EmLSJyguLYsTU44nJSSG9EjurUalEc0wQqeKw== + version "2.6.0" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.6.0.tgz#55dc9e82612a44a7bfed3484b18c535514d63e51" + integrity sha512-4EKVa3EOP3NoDwXCLgSCT2t+E2wPCWDXCuj3wEcQ1ovkLfdCMxKxCuElpXptIPBmtA+KbjnUl5Ta/1U3jsCmkg== fragment-cache@^0.2.1: version "0.2.1" @@ -9020,26 +9019,26 @@ left-pad@^1.3.0: integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== lerna@^3.16.1: - version "3.20.2" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.20.2.tgz#abf84e73055fe84ee21b46e64baf37b496c24864" - integrity sha512-bjdL7hPLpU3Y8CBnw/1ys3ynQMUjiK6l9iDWnEGwFtDy48Xh5JboR9ZJwmKGCz9A/sarVVIGwf1tlRNKUG9etA== - dependencies: - "@lerna/add" "3.20.0" - "@lerna/bootstrap" "3.20.0" - "@lerna/changed" "3.20.0" - "@lerna/clean" "3.20.0" + version "3.21.0" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.21.0.tgz#c81a0f8df45c6b7c9d3fc9fdcd0f846aca2375c6" + integrity sha512-ux8yOwQEgIXOZVUfq+T8nVzPymL19vlIoPbysOP3YA4hcjKlqQIlsjI/1ugBe6b4MF7W4iV5vS3gH9cGqBBc1A== + dependencies: + "@lerna/add" "3.21.0" + "@lerna/bootstrap" "3.21.0" + "@lerna/changed" "3.21.0" + "@lerna/clean" "3.21.0" "@lerna/cli" "3.18.5" - "@lerna/create" "3.18.5" - "@lerna/diff" "3.18.5" - "@lerna/exec" "3.20.0" - "@lerna/import" "3.18.5" - "@lerna/info" "3.20.0" - "@lerna/init" "3.18.5" - "@lerna/link" "3.18.5" - "@lerna/list" "3.20.0" - "@lerna/publish" "3.20.2" - "@lerna/run" "3.20.0" - "@lerna/version" "3.20.2" + "@lerna/create" "3.21.0" + "@lerna/diff" "3.21.0" + "@lerna/exec" "3.21.0" + "@lerna/import" "3.21.0" + "@lerna/info" "3.21.0" + "@lerna/init" "3.21.0" + "@lerna/link" "3.21.0" + "@lerna/list" "3.21.0" + "@lerna/publish" "3.21.0" + "@lerna/run" "3.21.0" + "@lerna/version" "3.21.0" import-local "^2.0.0" npmlog "^4.1.2" @@ -13420,9 +13419,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^3.5.3, typescript@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" - integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== + version "3.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.2.tgz#64e9c8e9be6ea583c54607677dd4680a1cf35db9" + integrity sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw== typescript@~3.7.2: version "3.7.2"