UWP: Crash report UI

This commit is contained in:
Paul Rouget 2020-08-03 17:23:30 +02:00
parent 0c00022ae0
commit 52f01a8a14
9 changed files with 196 additions and 36 deletions

View file

@ -19,6 +19,7 @@ using namespace winrt::Windows::ApplicationModel::Resources;
using namespace winrt::Windows::UI::Notifications; using namespace winrt::Windows::UI::Notifications;
using namespace winrt::Windows::Data::Json; using namespace winrt::Windows::Data::Json;
using namespace winrt::Windows::Data::Xml::Dom; using namespace winrt::Windows::Data::Xml::Dom;
using namespace winrt::Windows::Storage;
using namespace winrt::servo; using namespace winrt::servo;
namespace winrt::ServoApp::implementation { namespace winrt::ServoApp::implementation {
@ -37,15 +38,19 @@ void BrowserPage::BindServoEvents() {
backButton().IsEnabled(back); backButton().IsEnabled(back);
forwardButton().IsEnabled(forward); forwardButton().IsEnabled(forward);
}); });
servoView().OnServoPanic([=](const auto &, hstring /*message*/) {
mPanicking = true;
CheckCrashReport();
});
servoView().OnLoadStarted([=] { servoView().OnLoadStarted([=] {
urlbarLoadingIndicator().IsActive(true); urlbarLoadingIndicator().IsActive(true);
transientLoadingIndicator().IsIndeterminate(true); transientLoadingIndicator().IsIndeterminate(true);
reloadButton().IsEnabled(false); reloadButton().IsEnabled(false);
reloadButton().Visibility(Visibility::Collapsed); reloadButton().Visibility(Visibility::Collapsed);
stopButton().IsEnabled(true); stopButton().IsEnabled(true);
stopButton().Visibility(Visibility::Visible); stopButton().Visibility(Visibility::Visible);
devtoolsButton().IsEnabled(true); devtoolsButton().IsEnabled(true);
CheckCrashReport();
}); });
servoView().OnLoadEnded([=] { servoView().OnLoadEnded([=] {
urlbarLoadingIndicator().IsActive(false); urlbarLoadingIndicator().IsActive(false);
@ -297,24 +302,59 @@ void BrowserPage::OnDevtoolsMessage(DevtoolsMessageLevel level, hstring source,
}); });
} }
void BrowserPage::CheckCrashReport() {
Concurrency::create_task([=] {
auto pref = servo::Servo::GetPref(L"shell.crash_reporter.enabled");
bool reporter_enabled = unbox_value<bool>(std::get<1>(pref));
auto storageFolder = ApplicationData::Current().LocalFolder();
bool file_exist =
storageFolder.TryGetItemAsync(L"crash-report.txt").get() != nullptr;
if (reporter_enabled && file_exist) {
auto crash_file = storageFolder.GetFileAsync(L"crash-report.txt").get();
auto content = FileIO::ReadTextAsync(crash_file).get();
Dispatcher().RunAsync(CoreDispatcherPriority::High, [=] {
auto resourceLoader = ResourceLoader::GetForCurrentView();
auto message = resourceLoader.GetString(mPanicking ? L"crash/Happening"
: L"crash/Happened");
crashTabMessage().Text(message);
crashReport().Text(content);
crashTab().Visibility(Visibility::Visible);
crashTab().IsSelected(true);
ShowToolbox();
});
} else {
Dispatcher().RunAsync(CoreDispatcherPriority::High, [=] {
crashTab().Visibility(Visibility::Collapsed);
devtoolsTabConsole().IsSelected(true);
});
}
});
}
void BrowserPage::OnDismissCrashReport(IInspectable const &,
RoutedEventArgs const &) {
Concurrency::create_task([=] {
auto storageFolder = ApplicationData::Current().LocalFolder();
auto crash_file = storageFolder.GetFileAsync(L"crash-report.txt").get();
crash_file.DeleteAsync().get();
});
HideToolbox();
}
void BrowserPage::OnSubmitCrashReport(IInspectable const &,
RoutedEventArgs const &) {
// FIXME
}
void BrowserPage::OnDevtoolsDetached() {} void BrowserPage::OnDevtoolsDetached() {}
void BrowserPage::OnDevtoolsButtonClicked(IInspectable const &, void BrowserPage::ShowToolbox() {
RoutedEventArgs const &) {
if (toolbox().Visibility() == Visibility::Visible) { if (toolbox().Visibility() == Visibility::Visible) {
prefList().Children().Clear();
toolbox().Visibility(Visibility::Collapsed);
ClearConsole();
if (mDevtoolsClient != nullptr) {
mDevtoolsClient->Stop();
}
return; return;
} }
toolbox().Visibility(Visibility::Visible); toolbox().Visibility(Visibility::Visible);
CheckCrashReport();
BuildPrefList(); BuildPrefList();
auto resourceLoader = ResourceLoader::GetForCurrentView(); auto resourceLoader = ResourceLoader::GetForCurrentView();
if (mDevtoolsStatus == DevtoolsStatus::Running) { if (mDevtoolsStatus == DevtoolsStatus::Running) {
hstring port = to_hstring(mDevtoolsPort); hstring port = to_hstring(mDevtoolsPort);
@ -337,6 +377,24 @@ void BrowserPage::OnDevtoolsButtonClicked(IInspectable const &,
} }
} }
void BrowserPage::HideToolbox() {
prefList().Children().Clear();
toolbox().Visibility(Visibility::Collapsed);
ClearConsole();
if (mDevtoolsClient != nullptr) {
mDevtoolsClient->Stop();
}
}
void BrowserPage::OnDevtoolsButtonClicked(IInspectable const &,
RoutedEventArgs const &) {
if (toolbox().Visibility() == Visibility::Visible) {
HideToolbox();
} else {
ShowToolbox();
}
}
void BrowserPage::OnJSInputEdited(IInspectable const &, void BrowserPage::OnJSInputEdited(IInspectable const &,
Input::KeyRoutedEventArgs const &e) { Input::KeyRoutedEventArgs const &e) {
if (e.Key() == Windows::System::VirtualKey::Enter) { if (e.Key() == Windows::System::VirtualKey::Enter) {

View file

@ -41,6 +41,8 @@ public:
void Shutdown(); void Shutdown();
void LoadFXRURI(Uri uri); void LoadFXRURI(Uri uri);
void SetArgs(hstring); void SetArgs(hstring);
void OnDismissCrashReport(IInspectable const &, RoutedEventArgs const &);
void OnSubmitCrashReport(IInspectable const &, RoutedEventArgs const &);
void OnMediaControlsPlayClicked(IInspectable const &, void OnMediaControlsPlayClicked(IInspectable const &,
RoutedEventArgs const &); RoutedEventArgs const &);
void OnMediaControlsPauseClicked(IInspectable const &, void OnMediaControlsPauseClicked(IInspectable const &,
@ -55,11 +57,15 @@ public:
private: private:
void SetTransientMode(bool); void SetTransientMode(bool);
void UpdatePref(ServoApp::Pref, Controls::Control); void UpdatePref(ServoApp::Pref, Controls::Control);
void CheckCrashReport();
void BindServoEvents(); void BindServoEvents();
void BuildPrefList(); void BuildPrefList();
void ShowToolbox();
void HideToolbox();
DevtoolsStatus mDevtoolsStatus = DevtoolsStatus::Stopped; DevtoolsStatus mDevtoolsStatus = DevtoolsStatus::Stopped;
unsigned int mDevtoolsPort = 0; unsigned int mDevtoolsPort = 0;
hstring mDevtoolsToken; hstring mDevtoolsToken;
bool mPanicking = false;
std::unique_ptr<servo::DevtoolsClient> mDevtoolsClient; std::unique_ptr<servo::DevtoolsClient> mDevtoolsClient;
Collections::IObservableVector<IInspectable> mLogs; Collections::IObservableVector<IInspectable> mLogs;
}; };

View file

@ -150,7 +150,7 @@
</Button> </Button>
</Grid> </Grid>
</muxc:TabView.TabStripFooter> </muxc:TabView.TabStripFooter>
<muxc:TabViewItem x:Uid="devtoolsTabConsole" IsClosable="False"> <muxc:TabViewItem x:Uid="devtoolsTabConsole" x:Name="devtoolsTabConsole" IsClosable="False">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@ -199,6 +199,20 @@
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
</muxc:TabViewItem> </muxc:TabViewItem>
<muxc:TabViewItem x:Uid="crashTab" x:Name="crashTab" IsClosable="False" Visibility="Collapsed">
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Padding="4" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" x:Name="crashTabMessage"></TextBlock>
<Button IsTabStop="true" x:Uid="SubmitCrashReportButton" Margin="4" Click="OnSubmitCrashReport"/>
<Button IsTabStop="true" x:Uid="DismissCrashReportButton" Margin="4" Click="OnDismissCrashReport"/>
</StackPanel>
<TextBox Grid.Row="1" TextWrapping="Wrap" IsTabStop="true" x:Name="crashReport" AcceptsReturn="True" IsSpellCheckEnabled="False" Margin="3"/>
</Grid>
</muxc:TabViewItem>
</muxc:TabView> </muxc:TabView>
<ProgressBar x:Name="transientLoadingIndicator" Visibility="Collapsed" Grid.Row="3"/> <ProgressBar x:Name="transientLoadingIndicator" Visibility="Collapsed" Grid.Row="3"/>
<CommandBar Grid.Row="4" x:Name="mediaControls" Visibility="Collapsed"> <CommandBar Grid.Row="4" x:Name="mediaControls" Visibility="Collapsed">

View file

@ -21,8 +21,10 @@ void on_title_changed(const char *title) {
sServo->Delegate().OnServoTitleChanged(char2hstring(title)); sServo->Delegate().OnServoTitleChanged(char2hstring(title));
} }
void on_url_changed(const char *url) { void on_url_changed(const char *curl) {
sServo->Delegate().OnServoURLChanged(char2hstring(url)); auto url = char2hstring(curl);
sServo->CurrentUrl(url);
sServo->Delegate().OnServoURLChanged(url);
} }
void wakeup() { sServo->Delegate().WakeUp(); } void wakeup() { sServo->Delegate().WakeUp(); }
@ -35,12 +37,46 @@ void on_animating_changed(bool aAnimating) {
sServo->Delegate().OnServoAnimatingChanged(aAnimating); sServo->Delegate().OnServoAnimatingChanged(aAnimating);
} }
void on_panic(const char *backtrace) { void on_panic(const char *cbacktrace) {
if (sLogHandle != INVALID_HANDLE_VALUE) { if (sLogHandle != INVALID_HANDLE_VALUE) {
CloseHandle(sLogHandle); CloseHandle(sLogHandle);
sLogHandle = INVALID_HANDLE_VALUE; sLogHandle = INVALID_HANDLE_VALUE;
} }
throw hresult_error(E_FAIL, char2hstring(backtrace));
auto backtrace = char2hstring(cbacktrace);
try {
// Making all sync operations sync, as we are crashing.
auto storageFolder = ApplicationData::Current().LocalFolder();
auto stdout_txt = storageFolder.GetFileAsync(L"stdout.txt").get();
auto crash_txt =
storageFolder
.CreateFileAsync(L"crash-report.txt",
CreationCollisionOption::ReplaceExisting)
.get();
auto out = FileIO::ReadTextAsync(stdout_txt).get();
FileIO::WriteTextAsync(crash_txt, L"--- CUSTOM MESSAGE ---\r\n").get();
FileIO::AppendTextAsync(crash_txt,
L"Feel free to add details here before reporting")
.get();
FileIO::AppendTextAsync(
crash_txt, L"\r\n--- CURRENT URL (remove if sensitive) ---\r\n")
.get();
FileIO::AppendTextAsync(crash_txt, sServo->CurrentUrl()).get();
FileIO::AppendTextAsync(crash_txt, L"\r\n--- BACKTRACE ---\r\n").get();
FileIO::AppendTextAsync(crash_txt, backtrace).get();
FileIO::AppendTextAsync(crash_txt, L"\r\n--- STDOUT ---\r\n").get();
FileIO::AppendTextAsync(crash_txt, out).get();
FileIO::AppendTextAsync(crash_txt, L"\r\n").get();
} catch (...) {
log(L"Failed to log panic to crash report");
}
// If this is happening in the GL thread, the app can continue running.
// So let's show the crash report:
sServo->Delegate().OnServoPanic(backtrace);
throw hresult_error(E_FAIL, backtrace);
} }
void on_ime_show(const char *text, int32_t x, int32_t y, int32_t width, void on_ime_show(const char *text, int32_t x, int32_t y, int32_t width,
@ -371,9 +407,7 @@ std::vector<Servo::PrefTuple> Servo::GetPrefs() {
return {}; return {};
} }
auto prefs = capi::get_prefs(); auto prefs = capi::get_prefs();
std::vector< std::vector<PrefTuple> vec;
std::tuple<hstring, winrt::Windows::Foundation::IInspectable, bool>>
vec;
for (auto i = 0; i < prefs.len; i++) { for (auto i = 0; i < prefs.len; i++) {
auto pref = WrapPref(prefs.list[i]); auto pref = WrapPref(prefs.list[i]);
vec.push_back(pref); vec.push_back(pref);

View file

@ -31,6 +31,8 @@ public:
float, ServoDelegate &, bool); float, ServoDelegate &, bool);
~Servo(); ~Servo();
ServoDelegate &Delegate() { return mDelegate; } ServoDelegate &Delegate() { return mDelegate; }
hstring CurrentUrl() { return mUrl; }
void CurrentUrl(hstring url) { mUrl = url; }
typedef std::tuple<hstring, winrt::Windows::Foundation::IInspectable, bool> typedef std::tuple<hstring, winrt::Windows::Foundation::IInspectable, bool>
PrefTuple; PrefTuple;
@ -96,6 +98,7 @@ public:
private: private:
ServoDelegate &mDelegate; ServoDelegate &mDelegate;
hstring mUrl;
GLsizei mWindowWidth; GLsizei mWindowWidth;
GLsizei mWindowHeight; GLsizei mWindowHeight;
static void SaveUserPref(PrefTuple); static void SaveUserPref(PrefTuple);
@ -115,6 +118,7 @@ public:
virtual void OnServoURLChanged(hstring) = 0; virtual void OnServoURLChanged(hstring) = 0;
virtual bool OnServoAllowNavigation(hstring) = 0; virtual bool OnServoAllowNavigation(hstring) = 0;
virtual void OnServoAnimatingChanged(bool) = 0; virtual void OnServoAnimatingChanged(bool) = 0;
virtual void OnServoPanic(hstring) = 0;
virtual void OnServoIMEShow(hstring text, int32_t x, int32_t y, int32_t width, virtual void OnServoIMEShow(hstring text, int32_t x, int32_t y, int32_t width,
int32_t height) = 0; int32_t height) = 0;
virtual void OnServoIMEHide() = 0; virtual void OnServoIMEHide() = 0;

View file

@ -418,6 +418,7 @@ void ServoControl::Loop() {
while (true) { while (true) {
EnterCriticalSection(&mGLLock); EnterCriticalSection(&mGLLock);
try {
while (mTasks.size() == 0 && !mAnimating && mLooping) { while (mTasks.size() == 0 && !mAnimating && mLooping) {
SleepConditionVariableCS(&mGLCondVar, &mGLLock, INFINITE); SleepConditionVariableCS(&mGLCondVar, &mGLLock, INFINITE);
} }
@ -431,21 +432,34 @@ void ServoControl::Loop() {
mTasks.clear(); mTasks.clear();
LeaveCriticalSection(&mGLLock); LeaveCriticalSection(&mGLLock);
mServo->PerformUpdates(); mServo->PerformUpdates();
} catch (hresult_error const &e) {
log(L"GL Thread exception: %s", e.message().c_str());
throw e;
} catch (...) {
log(L"GL Thread exception");
throw winrt::hresult_error(E_FAIL, L"GL Thread exception");
}
} }
mServo->DeInit(); mServo->DeInit();
} }
void ServoControl::StartRenderLoop() { void ServoControl::StartRenderLoop() {
if (mLooping) { if (mLooping) {
#if defined _DEBUG
throw winrt::hresult_error(E_FAIL, L"GL thread is already looping"); throw winrt::hresult_error(E_FAIL, L"GL thread is already looping");
#else
return;
#endif
} }
mLooping = true; mLooping = true;
log(L"BrowserPage::StartRenderLoop(). UI thread: %i", GetCurrentThreadId()); log(L"BrowserPage::StartRenderLoop(). UI thread: %i", GetCurrentThreadId());
auto task = Concurrency::create_task([=] { Loop(); }); auto task = Concurrency::create_task([=] {
try {
Loop();
} catch (...) {
// Do our best to recover. Exception has been logged at that point.
mLooping = false;
mLoopTask.reset();
mServo.reset();
LeaveCriticalSection(&mGLLock);
}
});
mLoopTask = std::make_unique<Concurrency::task<void>>(task); mLoopTask = std::make_unique<Concurrency::task<void>>(task);
} }
@ -502,6 +516,10 @@ bool ServoControl::OnServoAllowNavigation(hstring uri) {
return !mTransient; return !mTransient;
} }
void ServoControl::OnServoPanic(hstring backtrace) {
RunOnUIThread([=] { mOnServoPanic(*this, backtrace); });
}
void ServoControl::OnServoAnimatingChanged(bool animating) { void ServoControl::OnServoAnimatingChanged(bool animating) {
EnterCriticalSection(&mGLLock); EnterCriticalSection(&mGLLock);
mAnimating = animating; mAnimating = animating;

View file

@ -100,6 +100,14 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
mOnTitleChangedEvent.remove(token); mOnTitleChangedEvent.remove(token);
} }
winrt::event_token
OnServoPanic(Windows::Foundation::EventHandler<hstring> const &handler) {
return mOnServoPanic.add(handler);
};
void OnServoPanic(winrt::event_token const &token) noexcept {
mOnServoPanic.remove(token);
}
winrt::event_token OnHistoryChanged(HistoryChangedDelegate const &handler) { winrt::event_token OnHistoryChanged(HistoryChangedDelegate const &handler) {
return mOnHistoryChangedEvent.add(handler); return mOnHistoryChangedEvent.add(handler);
}; };
@ -173,6 +181,7 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
virtual void OnServoURLChanged(winrt::hstring); virtual void OnServoURLChanged(winrt::hstring);
virtual bool OnServoAllowNavigation(winrt::hstring); virtual bool OnServoAllowNavigation(winrt::hstring);
virtual void OnServoAnimatingChanged(bool); virtual void OnServoAnimatingChanged(bool);
virtual void OnServoPanic(hstring);
virtual void OnServoIMEHide(); virtual void OnServoIMEHide();
virtual void OnServoIMEShow(hstring text, int32_t, int32_t, int32_t, int32_t); virtual void OnServoIMEShow(hstring text, int32_t, int32_t, int32_t, int32_t);
virtual void OnServoMediaSessionMetadata(winrt::hstring, winrt::hstring, virtual void OnServoMediaSessionMetadata(winrt::hstring, winrt::hstring,
@ -193,6 +202,7 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
private: private:
winrt::event<Windows::Foundation::EventHandler<hstring>> mOnURLChangedEvent; winrt::event<Windows::Foundation::EventHandler<hstring>> mOnURLChangedEvent;
winrt::event<Windows::Foundation::EventHandler<hstring>> mOnTitleChangedEvent; winrt::event<Windows::Foundation::EventHandler<hstring>> mOnTitleChangedEvent;
winrt::event<Windows::Foundation::EventHandler<hstring>> mOnServoPanic;
winrt::event<HistoryChangedDelegate> mOnHistoryChangedEvent; winrt::event<HistoryChangedDelegate> mOnHistoryChangedEvent;
winrt::event<DevtoolsStatusChangedDelegate> mOnDevtoolsStatusChangedEvent; winrt::event<DevtoolsStatusChangedDelegate> mOnDevtoolsStatusChangedEvent;
winrt::event<EventDelegate> mOnLoadStartedEvent; winrt::event<EventDelegate> mOnLoadStartedEvent;

View file

@ -39,6 +39,7 @@ namespace ServoApp {
event DevtoolsStatusChangedDelegate OnDevtoolsStatusChanged; event DevtoolsStatusChangedDelegate OnDevtoolsStatusChanged;
event HistoryChangedDelegate OnHistoryChanged; event HistoryChangedDelegate OnHistoryChanged;
event Windows.Foundation.EventHandler<String> OnTitleChanged; event Windows.Foundation.EventHandler<String> OnTitleChanged;
event Windows.Foundation.EventHandler<String> OnServoPanic;
event Windows.Foundation.EventHandler<String> OnURLChanged; event Windows.Foundation.EventHandler<String> OnURLChanged;
event MediaSessionMetadataDelegate OnMediaSessionMetadata; event MediaSessionMetadataDelegate OnMediaSessionMetadata;
event Windows.Foundation.EventHandler<int> OnMediaSessionPlaybackStateChange; event Windows.Foundation.EventHandler<int> OnMediaSessionPlaybackStateChange;

View file

@ -114,6 +114,21 @@
<data name="devtoolsTabPrefs.[using:Microsoft.UI.Xaml.Controls]TabViewItem.Header" xml:space="preserve"> <data name="devtoolsTabPrefs.[using:Microsoft.UI.Xaml.Controls]TabViewItem.Header" xml:space="preserve">
<value>Preferences</value> <value>Preferences</value>
</data> </data>
<data name="crashTab.[using:Microsoft.UI.Xaml.Controls]TabViewItem.Header" xml:space="preserve">
<value>Crash Report</value>
</data>
<data name="crash.Happening" xml:space="preserve">
<value>Internal crash detected. Application might be unstable:</value>
</data>
<data name="crash.Happened" xml:space="preserve">
<value>Internal crash was detected during last run:</value>
</data>
<data name="SubmitCrashReportButton.Content" xml:space="preserve">
<value>Send crash report</value>
</data>
<data name="DismissCrashReportButton.Content" xml:space="preserve">
<value>Dismiss</value>
</data>
<data name="preferenceSearchbox.PlaceholderText" xml:space="preserve"> <data name="preferenceSearchbox.PlaceholderText" xml:space="preserve">
<value>Search Preferences</value> <value>Search Preferences</value>
</data> </data>