mirror of
https://github.com/servo/servo.git
synced 2025-08-10 16:05:43 +01:00
Embedder Prompt API
This commit is contained in:
parent
5f55cd5d71
commit
51f15a055e
17 changed files with 447 additions and 110 deletions
|
@ -13,10 +13,6 @@ void on_history_changed(bool back, bool forward) {
|
|||
|
||||
void on_shutdown_complete() { sServo->Delegate().OnServoShutdownComplete(); }
|
||||
|
||||
void on_alert(const char *message) {
|
||||
sServo->Delegate().OnServoAlert(char2hstring(message));
|
||||
}
|
||||
|
||||
void on_title_changed(const char *title) {
|
||||
sServo->Delegate().OnServoTitleChanged(char2hstring(title));
|
||||
}
|
||||
|
@ -67,6 +63,30 @@ void on_media_session_playback_state_change(
|
|||
return sServo->Delegate().OnServoMediaSessionPlaybackStateChange(state);
|
||||
}
|
||||
|
||||
void prompt_alert(const char *message, bool trusted) {
|
||||
sServo->Delegate().OnServoPromptAlert(char2hstring(message), trusted);
|
||||
}
|
||||
|
||||
Servo::PromptResult prompt_ok_cancel(const char *message, bool trusted) {
|
||||
return sServo->Delegate().OnServoPromptOkCancel(char2hstring(message),
|
||||
trusted);
|
||||
}
|
||||
|
||||
Servo::PromptResult prompt_yes_no(const char *message, bool trusted) {
|
||||
return sServo->Delegate().OnServoPromptYesNo(char2hstring(message), trusted);
|
||||
}
|
||||
|
||||
const char *prompt_input(const char *message, const char *default,
|
||||
bool trusted) {
|
||||
auto input = sServo->Delegate().OnServoPromptInput(
|
||||
char2hstring(message), char2hstring(default), trusted);
|
||||
if (input.has_value()) {
|
||||
return *hstring2char(*input);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
|
||||
float dpi, ServoDelegate &aDelegate)
|
||||
: mWindowHeight(height), mWindowWidth(width), mDelegate(aDelegate) {
|
||||
|
@ -109,7 +129,6 @@ Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
|
|||
capi::CHostCallbacks c;
|
||||
c.flush = &flush;
|
||||
c.make_current = &make_current;
|
||||
c.on_alert = &on_alert;
|
||||
c.on_load_started = &on_load_started;
|
||||
c.on_load_ended = &on_load_ended;
|
||||
c.on_title_changed = &on_title_changed;
|
||||
|
@ -124,6 +143,10 @@ Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
|
|||
c.on_media_session_metadata = &on_media_session_metadata;
|
||||
c.on_media_session_playback_state_change =
|
||||
&on_media_session_playback_state_change;
|
||||
c.prompt_alert = &prompt_alert;
|
||||
c.prompt_ok_cancel = &prompt_ok_cancel;
|
||||
c.prompt_yes_no = &prompt_yes_no;
|
||||
c.prompt_input = &prompt_input;
|
||||
|
||||
capi::register_panic_handler(&on_panic);
|
||||
|
||||
|
|
|
@ -19,29 +19,7 @@ extern "C" {
|
|||
hstring char2hstring(const char *);
|
||||
std::unique_ptr<char *> hstring2char(hstring);
|
||||
|
||||
class ServoDelegate {
|
||||
public:
|
||||
// Called from any thread
|
||||
virtual void WakeUp() = 0;
|
||||
// Called from GL thread
|
||||
virtual void OnServoLoadStarted() = 0;
|
||||
virtual void OnServoLoadEnded() = 0;
|
||||
virtual void OnServoHistoryChanged(bool, bool) = 0;
|
||||
virtual void OnServoShutdownComplete() = 0;
|
||||
virtual void OnServoTitleChanged(hstring) = 0;
|
||||
virtual void OnServoAlert(hstring) = 0;
|
||||
virtual void OnServoURLChanged(hstring) = 0;
|
||||
virtual bool OnServoAllowNavigation(hstring) = 0;
|
||||
virtual void OnServoAnimatingChanged(bool) = 0;
|
||||
virtual void OnServoIMEStateChanged(bool) = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual void MakeCurrent() = 0;
|
||||
virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0;
|
||||
virtual void OnServoMediaSessionPlaybackStateChange(int) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ServoDelegate(){};
|
||||
};
|
||||
class ServoDelegate;
|
||||
|
||||
class Servo {
|
||||
public:
|
||||
|
@ -50,6 +28,7 @@ public:
|
|||
ServoDelegate &Delegate() { return mDelegate; }
|
||||
|
||||
typedef capi::CMouseButton MouseButton;
|
||||
typedef capi::CPromptResult PromptResult;
|
||||
typedef capi::CMediaSessionActionType MediaSessionActionType;
|
||||
typedef capi::CMediaSessionPlaybackState MediaSessionPlaybackState;
|
||||
|
||||
|
@ -100,6 +79,33 @@ private:
|
|||
GLsizei mWindowHeight;
|
||||
};
|
||||
|
||||
class ServoDelegate {
|
||||
public:
|
||||
// Called from any thread
|
||||
virtual void WakeUp() = 0;
|
||||
// Called from GL thread
|
||||
virtual void OnServoLoadStarted() = 0;
|
||||
virtual void OnServoLoadEnded() = 0;
|
||||
virtual void OnServoHistoryChanged(bool, bool) = 0;
|
||||
virtual void OnServoShutdownComplete() = 0;
|
||||
virtual void OnServoTitleChanged(hstring) = 0;
|
||||
virtual void OnServoURLChanged(hstring) = 0;
|
||||
virtual bool OnServoAllowNavigation(hstring) = 0;
|
||||
virtual void OnServoAnimatingChanged(bool) = 0;
|
||||
virtual void OnServoIMEStateChanged(bool) = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual void MakeCurrent() = 0;
|
||||
virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0;
|
||||
virtual void OnServoMediaSessionPlaybackStateChange(int) = 0;
|
||||
virtual void OnServoPromptAlert(hstring, bool) = 0;
|
||||
virtual Servo::PromptResult OnServoPromptOkCancel(hstring, bool) = 0;
|
||||
virtual Servo::PromptResult OnServoPromptYesNo(hstring, bool) = 0;
|
||||
virtual std::optional<hstring> OnServoPromptInput(hstring, hstring, bool) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ServoDelegate(){};
|
||||
};
|
||||
|
||||
// This is sad. We need a static pointer to Servo because we use function
|
||||
// pointer as callback in Servo, and these functions need a way to get
|
||||
// the Servo instance. See https://github.com/servo/servo/issues/22967
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
using namespace std::placeholders;
|
||||
using namespace winrt::Windows::Graphics::Display;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Popups;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::System;
|
||||
|
@ -69,6 +70,8 @@ void ServoControl::OnLoaded(IInspectable const &, RoutedEventArgs const &) {
|
|||
Panel().SizeChanged(std::bind(&ServoControl::OnSurfaceResized, this, _1, _2));
|
||||
InitializeConditionVariable(&mGLCondVar);
|
||||
InitializeCriticalSection(&mGLLock);
|
||||
InitializeConditionVariable(&mDialogCondVar);
|
||||
InitializeCriticalSection(&mDialogLock);
|
||||
CreateRenderSurface();
|
||||
StartRenderLoop();
|
||||
}
|
||||
|
@ -285,7 +288,7 @@ void ServoControl::TryLoadUri(hstring input) {
|
|||
RunOnGLThread([=] {
|
||||
if (!mServo->LoadUri(input)) {
|
||||
RunOnUIThread([=] {
|
||||
Windows::UI::Popups::MessageDialog msg{L"URI not valid"};
|
||||
MessageDialog msg{L"URI not valid"};
|
||||
msg.ShowAsync();
|
||||
});
|
||||
}
|
||||
|
@ -389,20 +392,15 @@ void ServoControl::OnServoShutdownComplete() {
|
|||
LeaveCriticalSection(&mGLLock);
|
||||
}
|
||||
|
||||
void ServoControl::OnServoAlert(hstring message) {
|
||||
// FIXME: make this sync
|
||||
RunOnUIThread([=] {
|
||||
Windows::UI::Popups::MessageDialog msg{message};
|
||||
msg.ShowAsync();
|
||||
});
|
||||
}
|
||||
|
||||
void ServoControl::OnServoTitleChanged(hstring title) {
|
||||
RunOnUIThread([=] { mOnTitleChangedEvent(*this, title); });
|
||||
}
|
||||
|
||||
void ServoControl::OnServoURLChanged(hstring url) {
|
||||
RunOnUIThread([=] { mOnURLChangedEvent(*this, url); });
|
||||
RunOnUIThread([=] {
|
||||
mCurrentUrl = url;
|
||||
mOnURLChangedEvent(*this, url);
|
||||
});
|
||||
}
|
||||
|
||||
void ServoControl::Flush() {
|
||||
|
@ -448,6 +446,113 @@ void ServoControl::OnServoMediaSessionPlaybackStateChange(int state) {
|
|||
RunOnUIThread([=] { mOnMediaSessionPlaybackStateChangeEvent(*this, state); });
|
||||
}
|
||||
|
||||
std::tuple<Controls::ContentDialogResult, std::optional<hstring>>
|
||||
ServoControl::PromptSync(hstring title, hstring message, hstring primaryButton,
|
||||
std::optional<hstring> secondaryButton,
|
||||
std::optional<hstring> input) {
|
||||
|
||||
bool showing = true;
|
||||
Controls::ContentDialogResult retButton = Controls::ContentDialogResult::None;
|
||||
std::optional<hstring> retString = {};
|
||||
|
||||
EnterCriticalSection(&mDialogLock);
|
||||
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::High, [&] {
|
||||
auto dialog = Controls::ContentDialog();
|
||||
dialog.IsPrimaryButtonEnabled(true);
|
||||
dialog.PrimaryButtonText(primaryButton);
|
||||
|
||||
if (secondaryButton.has_value()) {
|
||||
dialog.IsPrimaryButtonEnabled(true);
|
||||
dialog.SecondaryButtonText(*secondaryButton);
|
||||
} else {
|
||||
dialog.IsPrimaryButtonEnabled(false);
|
||||
}
|
||||
|
||||
auto titleBlock = Controls::TextBlock();
|
||||
titleBlock.Text(title);
|
||||
|
||||
auto messageBlock = Controls::TextBlock();
|
||||
messageBlock.TextWrapping(TextWrapping::Wrap);
|
||||
messageBlock.Text(message);
|
||||
Controls::StackPanel stack = Controls::StackPanel();
|
||||
stack.Children().Append(titleBlock);
|
||||
stack.Children().Append(messageBlock);
|
||||
|
||||
dialog.Content(stack);
|
||||
|
||||
auto textbox = Controls::TextBox();
|
||||
textbox.KeyDown([=](auto sender, auto args) {
|
||||
if (args.Key() == Windows::System::VirtualKey::Enter) {
|
||||
dialog.Hide();
|
||||
}
|
||||
});
|
||||
if (input.has_value()) {
|
||||
textbox.Text(*input);
|
||||
stack.Children().Append(textbox);
|
||||
}
|
||||
|
||||
dialog.Closed([&, textbox](Controls::ContentDialog d, auto closed) {
|
||||
EnterCriticalSection(&mDialogLock);
|
||||
retButton = closed.Result();
|
||||
showing = false;
|
||||
if (retButton == Controls::ContentDialogResult::Primary &&
|
||||
input.has_value()) {
|
||||
retString = hstring(textbox.Text());
|
||||
}
|
||||
LeaveCriticalSection(&mDialogLock);
|
||||
WakeConditionVariable(&mDialogCondVar);
|
||||
});
|
||||
dialog.ShowAsync();
|
||||
});
|
||||
|
||||
while (showing) {
|
||||
SleepConditionVariableCS(&mDialogCondVar, &mDialogLock, INFINITE);
|
||||
}
|
||||
LeaveCriticalSection(&mDialogLock);
|
||||
|
||||
return {retButton, retString};
|
||||
}
|
||||
|
||||
void ServoControl::OnServoPromptAlert(winrt::hstring message, bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
PromptSync(title, message, L"OK", {}, {});
|
||||
}
|
||||
|
||||
servo::Servo::PromptResult
|
||||
ServoControl::OnServoPromptOkCancel(winrt::hstring message, bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
auto [button, string] = PromptSync(title, message, L"OK", L"Cancel", {});
|
||||
if (button == Controls::ContentDialogResult::Primary) {
|
||||
return servo::Servo::PromptResult::Primary;
|
||||
} else if (button == Controls::ContentDialogResult::Secondary) {
|
||||
return servo::Servo::PromptResult::Secondary;
|
||||
} else {
|
||||
return servo::Servo::PromptResult::Dismissed;
|
||||
}
|
||||
}
|
||||
|
||||
servo::Servo::PromptResult
|
||||
ServoControl::OnServoPromptYesNo(winrt::hstring message, bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
auto [button, string] = PromptSync(title, message, L"Yes", L"No", {});
|
||||
if (button == Controls::ContentDialogResult::Primary) {
|
||||
return servo::Servo::PromptResult::Primary;
|
||||
} else if (button == Controls::ContentDialogResult::Secondary) {
|
||||
return servo::Servo::PromptResult::Secondary;
|
||||
} else {
|
||||
return servo::Servo::PromptResult::Dismissed;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<hstring> ServoControl::OnServoPromptInput(winrt::hstring message,
|
||||
winrt::hstring default,
|
||||
bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
auto [button, string] = PromptSync(title, message, L"Ok", L"Cancel", default);
|
||||
return string;
|
||||
}
|
||||
|
||||
template <typename Callable> void ServoControl::RunOnUIThread(Callable cb) {
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::High, cb);
|
||||
}
|
||||
|
|
|
@ -98,7 +98,6 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
|
|||
virtual void OnServoHistoryChanged(bool, bool);
|
||||
virtual void OnServoShutdownComplete();
|
||||
virtual void OnServoTitleChanged(winrt::hstring);
|
||||
virtual void OnServoAlert(winrt::hstring);
|
||||
virtual void OnServoURLChanged(winrt::hstring);
|
||||
virtual void Flush();
|
||||
virtual void MakeCurrent();
|
||||
|
@ -108,6 +107,12 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
|
|||
virtual void OnServoMediaSessionMetadata(winrt::hstring, winrt::hstring,
|
||||
winrt::hstring);
|
||||
virtual void OnServoMediaSessionPlaybackStateChange(int);
|
||||
virtual void OnServoPromptAlert(winrt::hstring, bool);
|
||||
virtual servo::Servo::PromptResult OnServoPromptOkCancel(winrt::hstring,
|
||||
bool);
|
||||
virtual servo::Servo::PromptResult OnServoPromptYesNo(winrt::hstring, bool);
|
||||
virtual std::optional<hstring> OnServoPromptInput(winrt::hstring,
|
||||
winrt::hstring, bool);
|
||||
|
||||
private:
|
||||
winrt::event<Windows::Foundation::EventHandler<hstring>> mOnURLChangedEvent;
|
||||
|
@ -121,8 +126,18 @@ private:
|
|||
winrt::event<Windows::Foundation::EventHandler<int>>
|
||||
mOnMediaSessionPlaybackStateChangeEvent;
|
||||
|
||||
CRITICAL_SECTION mDialogLock;
|
||||
CONDITION_VARIABLE mDialogCondVar;
|
||||
|
||||
std::tuple<Windows::UI::Xaml::Controls::ContentDialogResult,
|
||||
std::optional<hstring>>
|
||||
PromptSync(hstring title, hstring message, hstring primaryButton,
|
||||
std::optional<hstring> secondaryButton,
|
||||
std::optional<hstring> input);
|
||||
|
||||
float mDPI = 1;
|
||||
hstring mInitialURL = DEFAULT_URL;
|
||||
hstring mCurrentUrl = L"";
|
||||
bool mTransient = false;
|
||||
|
||||
Windows::UI::Xaml::Controls::SwapChainPanel ServoControl::Panel();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue