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::Data::Json;
using namespace winrt::Windows::Data::Xml::Dom;
using namespace winrt::Windows::Storage;
using namespace winrt::servo;
namespace winrt::ServoApp::implementation {
@ -37,15 +38,19 @@ void BrowserPage::BindServoEvents() {
backButton().IsEnabled(back);
forwardButton().IsEnabled(forward);
});
servoView().OnServoPanic([=](const auto &, hstring /*message*/) {
mPanicking = true;
CheckCrashReport();
});
servoView().OnLoadStarted([=] {
urlbarLoadingIndicator().IsActive(true);
transientLoadingIndicator().IsIndeterminate(true);
reloadButton().IsEnabled(false);
reloadButton().Visibility(Visibility::Collapsed);
stopButton().IsEnabled(true);
stopButton().Visibility(Visibility::Visible);
devtoolsButton().IsEnabled(true);
CheckCrashReport();
});
servoView().OnLoadEnded([=] {
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::OnDevtoolsButtonClicked(IInspectable const &,
RoutedEventArgs const &) {
void BrowserPage::ShowToolbox() {
if (toolbox().Visibility() == Visibility::Visible) {
prefList().Children().Clear();
toolbox().Visibility(Visibility::Collapsed);
ClearConsole();
if (mDevtoolsClient != nullptr) {
mDevtoolsClient->Stop();
}
return;
}
toolbox().Visibility(Visibility::Visible);
CheckCrashReport();
BuildPrefList();
auto resourceLoader = ResourceLoader::GetForCurrentView();
if (mDevtoolsStatus == DevtoolsStatus::Running) {
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 &,
Input::KeyRoutedEventArgs const &e) {
if (e.Key() == Windows::System::VirtualKey::Enter) {

View file

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

View file

@ -150,7 +150,7 @@
</Button>
</Grid>
</muxc:TabView.TabStripFooter>
<muxc:TabViewItem x:Uid="devtoolsTabConsole" IsClosable="False">
<muxc:TabViewItem x:Uid="devtoolsTabConsole" x:Name="devtoolsTabConsole" IsClosable="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
@ -199,6 +199,20 @@
</ScrollViewer>
</Grid>
</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>
<ProgressBar x:Name="transientLoadingIndicator" Visibility="Collapsed" Grid.Row="3"/>
<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));
}
void on_url_changed(const char *url) {
sServo->Delegate().OnServoURLChanged(char2hstring(url));
void on_url_changed(const char *curl) {
auto url = char2hstring(curl);
sServo->CurrentUrl(url);
sServo->Delegate().OnServoURLChanged(url);
}
void wakeup() { sServo->Delegate().WakeUp(); }
@ -35,12 +37,46 @@ void on_animating_changed(bool aAnimating) {
sServo->Delegate().OnServoAnimatingChanged(aAnimating);
}
void on_panic(const char *backtrace) {
void on_panic(const char *cbacktrace) {
if (sLogHandle != INVALID_HANDLE_VALUE) {
CloseHandle(sLogHandle);
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,
@ -371,9 +407,7 @@ std::vector<Servo::PrefTuple> Servo::GetPrefs() {
return {};
}
auto prefs = capi::get_prefs();
std::vector<
std::tuple<hstring, winrt::Windows::Foundation::IInspectable, bool>>
vec;
std::vector<PrefTuple> vec;
for (auto i = 0; i < prefs.len; i++) {
auto pref = WrapPref(prefs.list[i]);
vec.push_back(pref);

View file

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

View file

@ -418,34 +418,48 @@ void ServoControl::Loop() {
while (true) {
EnterCriticalSection(&mGLLock);
while (mTasks.size() == 0 && !mAnimating && mLooping) {
SleepConditionVariableCS(&mGLCondVar, &mGLLock, INFINITE);
}
if (!mLooping) {
try {
while (mTasks.size() == 0 && !mAnimating && mLooping) {
SleepConditionVariableCS(&mGLCondVar, &mGLLock, INFINITE);
}
if (!mLooping) {
LeaveCriticalSection(&mGLLock);
break;
}
for (auto &&task : mTasks) {
task();
}
mTasks.clear();
LeaveCriticalSection(&mGLLock);
break;
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");
}
for (auto &&task : mTasks) {
task();
}
mTasks.clear();
LeaveCriticalSection(&mGLLock);
mServo->PerformUpdates();
}
mServo->DeInit();
}
void ServoControl::StartRenderLoop() {
if (mLooping) {
#if defined _DEBUG
throw winrt::hresult_error(E_FAIL, L"GL thread is already looping");
#else
return;
#endif
}
mLooping = true;
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);
}
@ -502,6 +516,10 @@ bool ServoControl::OnServoAllowNavigation(hstring uri) {
return !mTransient;
}
void ServoControl::OnServoPanic(hstring backtrace) {
RunOnUIThread([=] { mOnServoPanic(*this, backtrace); });
}
void ServoControl::OnServoAnimatingChanged(bool animating) {
EnterCriticalSection(&mGLLock);
mAnimating = animating;

View file

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

View file

@ -39,6 +39,7 @@ namespace ServoApp {
event DevtoolsStatusChangedDelegate OnDevtoolsStatusChanged;
event HistoryChangedDelegate OnHistoryChanged;
event Windows.Foundation.EventHandler<String> OnTitleChanged;
event Windows.Foundation.EventHandler<String> OnServoPanic;
event Windows.Foundation.EventHandler<String> OnURLChanged;
event MediaSessionMetadataDelegate OnMediaSessionMetadata;
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">
<value>Preferences</value>
</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">
<value>Search Preferences</value>
</data>