-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expose ability for apps to provide their own RedBox implementation #4786
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
25426cb
25e6be6
519863b
7b7c57c
362c663
f7a5116
32d3b51
2f6d79e
3159214
710cab9
a33bf75
7ff9c8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "type": "prerelease", | ||
| "comment": "Expose ability for apps to provide their own RedBox implementation", | ||
| "packageName": "react-native-windows", | ||
| "email": "[email protected]", | ||
| "dependentChangeType": "patch", | ||
| "date": "2020-05-04T19:54:41.414Z" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| #include "RedBox.h" | ||
| #include <boost/algorithm/string.hpp> | ||
| #include <functional/functor.h> | ||
| #include <winrt/Windows.ApplicationModel.Core.h> | ||
| #include <winrt/Windows.Data.Json.h> | ||
| #include <winrt/Windows.Foundation.h> | ||
| #include <winrt/Windows.UI.Core.h> | ||
|
|
@@ -18,10 +19,10 @@ namespace Mso::React { | |
|
|
||
| struct RedBox : public std::enable_shared_from_this<RedBox> { | ||
| RedBox( | ||
| Mso::WeakPtr<IReactHost> weakReactHost, | ||
| const Mso::WeakPtr<IReactHost> &weakReactHost, | ||
| Mso::Functor<void(uint32_t)> &&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<RedBox> { | |
| /* | ||
| * This class is implemented such that the methods on IRedBoxHandler are thread safe. | ||
| */ | ||
| struct RedBoxHandler : public std::enable_shared_from_this<RedBoxHandler>, IRedBoxHandler { | ||
| RedBoxHandler( | ||
| Mso::WeakPtr<Mso::React::IReactHost> weakReactHost, | ||
| std::shared_ptr<facebook::react::MessageQueueThread> uiMessageQueue) noexcept | ||
| : m_weakReactHost(std::move(weakReactHost)), m_wkUIMessageQueue(std::move(uiMessageQueue)) {} | ||
| struct DefaultRedBoxHandler : public std::enable_shared_from_this<DefaultRedBoxHandler>, IRedBoxHandler { | ||
| DefaultRedBoxHandler(Mso::WeakPtr<Mso::React::IReactHost> &&weakReactHost) noexcept | ||
| : m_weakReactHost(std::move(weakReactHost)) {} | ||
|
|
||
| ~RedBoxHandler() { | ||
| ~DefaultRedBoxHandler() { | ||
| // Hide any currently showing redboxes | ||
| std::vector<std::shared_ptr<RedBox>> 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(); | ||
| } | ||
| }); | ||
| } | ||
| Mso::DispatchQueue::MainUIQueue().Post([redboxes = std::move(redboxes)]() { | ||
| for (const auto redbox : redboxes) { | ||
| redbox->Dismiss(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| virtual void showNewError(ErrorInfo &&info, ErrorType /*exceptionType*/) override { | ||
|
|
@@ -394,7 +391,7 @@ struct RedBoxHandler : public std::enable_shared_from_this<RedBoxHandler>, IRedB | |
| showTopJSError(); | ||
| } | ||
|
|
||
| virtual bool isDevSupportEnabled() override { | ||
| virtual bool isDevSupportEnabled() const override { | ||
| if (auto reactHost = m_weakReactHost.GetStrongPtr()) { | ||
| return reactHost->Options().DeveloperSettings.IsDevModeEnabled; | ||
| } | ||
|
|
@@ -414,28 +411,26 @@ struct RedBoxHandler : public std::enable_shared_from_this<RedBoxHandler>, IRedB | |
| } | ||
| } | ||
| if (redbox) { | ||
| if (auto uiQueue = m_wkUIMessageQueue.lock()) { | ||
| uiQueue->runOnQueue([redboxCaptured = std::move(redbox), errorInfo = std::move(info)]() { | ||
| redboxCaptured->UpdateError(std::move(errorInfo)); | ||
| }); | ||
| } | ||
| Mso::DispatchQueue::MainUIQueue().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(); | ||
| }); | ||
| } | ||
| Mso::DispatchQueue::MainUIQueue().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 | ||
| 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> redbox; | ||
| { | ||
|
|
@@ -463,25 +458,114 @@ struct RedBoxHandler : public std::enable_shared_from_this<RedBoxHandler>, IRedB | |
| } | ||
| } | ||
|
|
||
| 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; | ||
|
|
||
| Mso::DispatchQueue::MainUIQueue().Post( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious how Mso determines which one is the MainUIQueue - i.e. how is this going to work in xaml islands?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is the plan. Currently we use the MainView CoreDispatcher and it fails for the XAML islands. |
||
| [redboxCaptured = std::move(redbox)]() { redboxCaptured->ShowNewJSError(); }); | ||
| } | ||
|
|
||
| bool m_showingRedBox = false; // Access from UI Thread only | ||
| std::mutex m_lockRedBox; | ||
| std::vector<std::shared_ptr<RedBox>> m_redboxes; // Protected by m_lockRedBox | ||
| const Mso::WeakPtr<IReactHost> m_weakReactHost; | ||
| const std::weak_ptr<facebook::react::MessageQueueThread> m_wkUIMessageQueue; | ||
| }; | ||
|
|
||
| struct RedBoxErrorFrameInfo | ||
| : public winrt::implements<RedBoxErrorFrameInfo, winrt::Microsoft::ReactNative::IRedBoxErrorFrameInfo> { | ||
| 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; | ||
| } | ||
acoates-ms marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| uint32_t Column() const noexcept { | ||
| return m_frame.Column; | ||
| } | ||
acoates-ms marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private: | ||
| Mso::React::ErrorFrameInfo m_frame; | ||
| }; | ||
|
|
||
| struct RedBoxErrorInfo : public winrt::implements<RedBoxErrorInfo, winrt::Microsoft::ReactNative::IRedBoxErrorInfo> { | ||
| 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<winrt::Microsoft::ReactNative::IRedBoxErrorFrameInfo> | ||
| Callstack() noexcept { | ||
| if (!m_callstack) { | ||
| m_callstack = winrt::single_threaded_vector<winrt::Microsoft::ReactNative::IRedBoxErrorFrameInfo>(); | ||
| for (auto frame : m_errorInfo.Callstack) { | ||
| m_callstack.Append(winrt::make<RedBoxErrorFrameInfo>(std::move(frame))); | ||
| } | ||
| } | ||
|
|
||
| return m_callstack.GetView(); | ||
| } | ||
|
|
||
| private: | ||
| winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::ReactNative::IRedBoxErrorFrameInfo> 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<uint32_t>(Mso::React::ErrorType::JSFatal) == | ||
| static_cast<uint32_t>(winrt::Microsoft::ReactNative::RedBoxErrorType::JavaScriptFatal)); | ||
| static_assert( | ||
| static_cast<uint32_t>(Mso::React::ErrorType::JSSoft) == | ||
| static_cast<uint32_t>(winrt::Microsoft::ReactNative::RedBoxErrorType::JavaScriptSoft)); | ||
| static_assert( | ||
| static_cast<uint32_t>(Mso::React::ErrorType::Native) == | ||
| static_cast<uint32_t>(winrt::Microsoft::ReactNative::RedBoxErrorType::Native)); | ||
|
|
||
| virtual void showNewError(Mso::React::ErrorInfo &&info, Mso::React::ErrorType errorType) override { | ||
| m_redBoxHandler.ShowNewError( | ||
| winrt::make<RedBoxErrorInfo>(std::move(info)), | ||
| static_cast<winrt::Microsoft::ReactNative::RedBoxErrorType>(static_cast<uint32_t>(errorType))); | ||
| } | ||
| virtual bool isDevSupportEnabled() const override { | ||
| return m_redBoxHandler.IsDevSupportEnabled(); | ||
| } | ||
| virtual void updateError(Mso::React::ErrorInfo &&info) override { | ||
| m_redBoxHandler.UpdateError(winrt::make<RedBoxErrorInfo>(std::move(info))); | ||
| } | ||
|
|
||
| virtual void dismissRedbox() override { | ||
| m_redBoxHandler.DismissRedBox(); | ||
| } | ||
|
|
||
| private: | ||
| winrt::Microsoft::ReactNative::IRedBoxHandler m_redBoxHandler; | ||
| }; | ||
|
|
||
| std::shared_ptr<IRedBoxHandler> CreateRedBoxHandler( | ||
| Mso::WeakPtr<IReactHost> &&weakReactHost, | ||
| std::shared_ptr<facebook::react::MessageQueueThread> &&uiMessageQueue) noexcept { | ||
| return std::make_shared<RedBoxHandler>(std::move(weakReactHost), std::move(uiMessageQueue)); | ||
| winrt::Microsoft::ReactNative::IRedBoxHandler const &redBoxHandler) noexcept { | ||
| return std::make_shared<RedBoxHandler>(redBoxHandler); | ||
| } | ||
|
|
||
| std::shared_ptr<IRedBoxHandler> CreateDefaultRedBoxHandler(Mso::WeakPtr<IReactHost> &&weakReactHost) noexcept { | ||
| return std::make_shared<DefaultRedBoxHandler>(std::move(weakReactHost)); | ||
| } | ||
|
|
||
| } // namespace Mso::React | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed