2020-06-17 17:31:42 +02:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2020-08-17 16:20:47 +02:00
# include "OutOfProcessWebView.h"
2020-06-17 17:31:42 +02:00
# include "WebContentClient.h"
2021-01-30 21:57:16 +01:00
# include <AK/String.h>
# include <AK/URLParser.h>
2021-03-30 12:10:06 -04:00
# include <LibGUI/Application.h>
2021-04-04 00:12:37 +02:00
# include <LibGUI/Desktop.h>
2021-02-20 12:05:18 +01:00
# include <LibGUI/InputBox.h>
2020-09-12 11:56:13 +02:00
# include <LibGUI/MessageBox.h>
2020-06-17 17:31:42 +02:00
# include <LibGUI/Painter.h>
2020-07-04 23:19:32 +02:00
# include <LibGUI/ScrollBar.h>
2020-07-06 23:32:12 +02:00
# include <LibGUI/Window.h>
2020-12-29 00:44:58 +01:00
# include <LibGfx/Palette.h>
2020-06-17 17:31:42 +02:00
# include <LibGfx/SystemTheme.h>
2020-10-08 21:11:32 +01:00
REGISTER_WIDGET ( Web , OutOfProcessWebView )
2020-08-24 15:33:18 +04:30
namespace Web {
2020-08-17 16:20:47 +02:00
OutOfProcessWebView : : OutOfProcessWebView ( )
2020-06-17 17:31:42 +02:00
{
2020-07-06 22:02:38 +02:00
set_should_hide_unnecessary_scrollbars ( true ) ;
2020-10-30 10:58:27 +01:00
set_focus_policy ( GUI : : FocusPolicy : : StrongFocus ) ;
2021-01-30 18:20:40 +01:00
create_client ( ) ;
2020-06-17 17:31:42 +02:00
}
2020-08-17 16:20:47 +02:00
OutOfProcessWebView : : ~ OutOfProcessWebView ( )
2020-06-17 17:31:42 +02:00
{
}
2021-02-22 11:07:40 +01:00
void OutOfProcessWebView : : handle_web_content_process_crash ( )
{
create_client ( ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( m_client_state . client ) ;
2021-02-22 11:07:40 +01:00
// Don't keep a stale backup bitmap around.
m_backup_bitmap = nullptr ;
handle_resize ( ) ;
StringBuilder builder ;
builder . append ( " <html><head><title>Crashed: " ) ;
builder . append ( escape_html_entities ( m_url . to_string ( ) ) ) ;
builder . append ( " </title></head><body> " ) ;
builder . append ( " <h1>Web page crashed " ) ;
if ( ! m_url . host ( ) . is_empty ( ) ) {
builder . appendff ( " on {} " , escape_html_entities ( m_url . host ( ) ) ) ;
}
builder . append ( " </h1> " ) ;
builder . appendff ( " The web page <a href= \" {} \" >{}</a> has crashed.<br><br>You can reload the page to try again. " , escape_html_entities ( m_url . to_string_encoded ( ) ) , escape_html_entities ( m_url . to_string ( ) ) ) ;
builder . append ( " </body></html> " ) ;
load_html ( builder . to_string ( ) , m_url ) ;
}
2021-01-30 18:20:40 +01:00
void OutOfProcessWebView : : create_client ( )
{
m_client_state = { } ;
m_client_state . client = WebContentClient : : construct ( * this ) ;
m_client_state . client - > on_web_content_process_crash = [ this ] {
2021-02-22 11:07:40 +01:00
deferred_invoke ( [ this ] {
handle_web_content_process_crash ( ) ;
} ) ;
2021-01-30 18:20:40 +01:00
} ;
client ( ) . post_message ( Messages : : WebContentServer : : UpdateSystemTheme ( Gfx : : current_system_theme_buffer ( ) ) ) ;
2021-04-04 00:12:37 +02:00
client ( ) . post_message ( Messages : : WebContentServer : : UpdateScreenRect ( GUI : : Desktop : : the ( ) . rect ( ) ) ) ;
2021-01-30 18:20:40 +01:00
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : load ( const URL & url )
2020-06-17 17:31:42 +02:00
{
2020-07-06 21:46:37 +02:00
m_url = url ;
2020-06-17 17:31:42 +02:00
client ( ) . post_message ( Messages : : WebContentServer : : LoadURL ( url ) ) ;
}
2020-10-08 21:11:01 +01:00
void OutOfProcessWebView : : load_html ( const StringView & html , const URL & url )
{
m_url = url ;
client ( ) . post_message ( Messages : : WebContentServer : : LoadHTML ( html , url ) ) ;
}
2020-10-08 21:58:00 +01:00
void OutOfProcessWebView : : load_empty_document ( )
{
m_url = { } ;
client ( ) . post_message ( Messages : : WebContentServer : : LoadHTML ( " " , { } ) ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : paint_event ( GUI : : PaintEvent & event )
2020-06-17 17:31:42 +02:00
{
2020-07-05 12:45:56 +02:00
GUI : : ScrollableWidget : : paint_event ( event ) ;
2020-07-04 23:27:36 +02:00
2020-11-08 15:07:57 +00:00
// If the available size is empty, we don't have a front or back bitmap to draw.
if ( available_size ( ) . is_empty ( ) )
return ;
2020-06-17 17:31:42 +02:00
GUI : : Painter painter ( * this ) ;
painter . add_clip_rect ( event . rect ( ) ) ;
2020-12-29 00:44:58 +01:00
2021-02-09 22:07:36 +01:00
if ( auto * bitmap = m_client_state . has_usable_bitmap ? m_client_state . front_bitmap . ptr ( ) : m_backup_bitmap . ptr ( ) ) {
painter . add_clip_rect ( frame_inner_rect ( ) ) ;
painter . translate ( frame_thickness ( ) , frame_thickness ( ) ) ;
painter . blit ( { 0 , 0 } , * bitmap , bitmap - > rect ( ) ) ;
2020-12-29 00:44:58 +01:00
return ;
}
2021-02-09 22:07:36 +01:00
painter . fill_rect ( frame_inner_rect ( ) , palette ( ) . base ( ) ) ;
2020-06-17 17:31:42 +02:00
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : resize_event ( GUI : : ResizeEvent & event )
2020-06-17 17:31:42 +02:00
{
2020-07-05 12:45:56 +02:00
GUI : : ScrollableWidget : : resize_event ( event ) ;
2021-01-30 18:20:40 +01:00
handle_resize ( ) ;
}
2020-07-05 12:45:56 +02:00
2021-01-30 18:20:40 +01:00
void OutOfProcessWebView : : handle_resize ( )
{
2020-11-08 15:07:57 +00:00
client ( ) . post_message ( Messages : : WebContentServer : : SetViewportRect ( Gfx : : IntRect ( { horizontal_scrollbar ( ) . value ( ) , vertical_scrollbar ( ) . value ( ) } , available_size ( ) ) ) ) ;
2021-02-09 22:07:36 +01:00
if ( m_client_state . has_usable_bitmap ) {
// NOTE: We keep the outgoing front bitmap as a backup so we have something to paint until we get a new one.
m_backup_bitmap = m_client_state . front_bitmap ;
}
2021-01-30 18:20:40 +01:00
if ( m_client_state . front_bitmap ) {
m_client_state . front_bitmap = nullptr ;
client ( ) . post_message ( Messages : : WebContentServer : : RemoveBackingStore ( m_client_state . front_bitmap_id ) ) ;
2021-01-16 23:15:32 +01:00
}
2021-01-30 18:20:40 +01:00
if ( m_client_state . back_bitmap ) {
m_client_state . back_bitmap = nullptr ;
client ( ) . post_message ( Messages : : WebContentServer : : RemoveBackingStore ( m_client_state . back_bitmap_id ) ) ;
2021-01-16 23:15:32 +01:00
}
2021-01-30 18:20:40 +01:00
m_client_state . front_bitmap_id = - 1 ;
m_client_state . back_bitmap_id = - 1 ;
m_client_state . has_usable_bitmap = false ;
2020-11-08 14:53:36 +01:00
2020-11-08 15:07:57 +00:00
if ( available_size ( ) . is_empty ( ) )
return ;
2021-03-16 11:48:42 +01:00
if ( auto new_bitmap = Gfx : : Bitmap : : create_shareable ( Gfx : : BitmapFormat : : BGRx8888 , available_size ( ) ) ) {
2021-01-30 18:20:40 +01:00
m_client_state . front_bitmap = move ( new_bitmap ) ;
m_client_state . front_bitmap_id = m_client_state . next_bitmap_id + + ;
client ( ) . post_message ( Messages : : WebContentServer : : AddBackingStore ( m_client_state . front_bitmap_id , m_client_state . front_bitmap - > to_shareable_bitmap ( ) ) ) ;
2020-11-08 14:53:36 +01:00
}
2021-03-16 11:48:42 +01:00
if ( auto new_bitmap = Gfx : : Bitmap : : create_shareable ( Gfx : : BitmapFormat : : BGRx8888 , available_size ( ) ) ) {
2021-01-30 18:20:40 +01:00
m_client_state . back_bitmap = move ( new_bitmap ) ;
m_client_state . back_bitmap_id = m_client_state . next_bitmap_id + + ;
client ( ) . post_message ( Messages : : WebContentServer : : AddBackingStore ( m_client_state . back_bitmap_id , m_client_state . back_bitmap - > to_shareable_bitmap ( ) ) ) ;
2020-11-08 14:53:36 +01:00
}
2020-07-05 16:31:50 +02:00
2020-07-04 23:19:32 +02:00
request_repaint ( ) ;
2020-06-17 17:31:42 +02:00
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : keydown_event ( GUI : : KeyEvent & event )
2020-08-03 19:58:59 +02:00
{
client ( ) . post_message ( Messages : : WebContentServer : : KeyDown ( event . key ( ) , event . modifiers ( ) , event . code_point ( ) ) ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : mousedown_event ( GUI : : MouseEvent & event )
2020-06-17 18:05:08 +02:00
{
2020-07-04 23:23:36 +02:00
client ( ) . post_message ( Messages : : WebContentServer : : MouseDown ( to_content_position ( event . position ( ) ) , event . button ( ) , event . buttons ( ) , event . modifiers ( ) ) ) ;
2020-06-17 18:05:08 +02:00
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : mouseup_event ( GUI : : MouseEvent & event )
2020-06-17 18:05:08 +02:00
{
2020-07-04 23:23:36 +02:00
client ( ) . post_message ( Messages : : WebContentServer : : MouseUp ( to_content_position ( event . position ( ) ) , event . button ( ) , event . buttons ( ) , event . modifiers ( ) ) ) ;
2020-06-17 18:05:08 +02:00
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : mousemove_event ( GUI : : MouseEvent & event )
2020-06-17 18:05:08 +02:00
{
2020-07-04 23:23:36 +02:00
client ( ) . post_message ( Messages : : WebContentServer : : MouseMove ( to_content_position ( event . position ( ) ) , event . button ( ) , event . buttons ( ) , event . modifiers ( ) ) ) ;
2020-06-17 18:05:08 +02:00
}
2021-02-22 19:45:41 +01:00
void OutOfProcessWebView : : mousewheel_event ( GUI : : MouseEvent & event )
{
client ( ) . post_message ( Messages : : WebContentServer : : MouseWheel ( to_content_position ( event . position ( ) ) , event . button ( ) , event . buttons ( ) , event . modifiers ( ) , event . wheel_delta ( ) ) ) ;
}
2020-10-08 22:13:54 +01:00
void OutOfProcessWebView : : theme_change_event ( GUI : : ThemeChangeEvent & event )
{
GUI : : ScrollableWidget : : theme_change_event ( event ) ;
2021-01-16 17:20:53 +01:00
client ( ) . post_message ( Messages : : WebContentServer : : UpdateSystemTheme ( Gfx : : current_system_theme_buffer ( ) ) ) ;
2020-10-08 22:13:54 +01:00
request_repaint ( ) ;
}
2021-04-04 00:12:37 +02:00
void OutOfProcessWebView : : screen_rect_change_event ( GUI : : ScreenRectChangeEvent & event )
{
client ( ) . post_message ( Messages : : WebContentServer : : UpdateScreenRect ( event . rect ( ) ) ) ;
}
2021-01-16 23:15:32 +01:00
void OutOfProcessWebView : : notify_server_did_paint ( Badge < WebContentClient > , i32 bitmap_id )
2020-06-17 17:31:42 +02:00
{
2021-01-30 18:20:40 +01:00
if ( m_client_state . back_bitmap_id = = bitmap_id ) {
m_client_state . has_usable_bitmap = true ;
swap ( m_client_state . back_bitmap , m_client_state . front_bitmap ) ;
swap ( m_client_state . back_bitmap_id , m_client_state . front_bitmap_id ) ;
2021-02-09 22:07:36 +01:00
// We don't need the backup bitmap anymore, so drop it.
m_backup_bitmap = nullptr ;
2020-06-17 17:31:42 +02:00
update ( ) ;
2020-07-05 16:31:50 +02:00
}
2020-06-17 17:31:42 +02:00
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_invalidate_content_rect ( Badge < WebContentClient > , [[maybe_unused]] const Gfx : : IntRect & content_rect )
2020-06-17 18:00:18 +02:00
{
2020-07-04 20:57:57 +02:00
request_repaint ( ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_change_selection ( Badge < WebContentClient > )
2020-07-04 20:57:57 +02:00
{
request_repaint ( ) ;
}
2021-02-27 21:12:12 +00:00
void OutOfProcessWebView : : notify_server_did_request_cursor_change ( Badge < WebContentClient > , Gfx : : StandardCursor cursor )
{
set_override_cursor ( cursor ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_layout ( Badge < WebContentClient > , const Gfx : : IntSize & content_size )
2020-07-04 23:19:32 +02:00
{
set_content_size ( content_size ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_change_title ( Badge < WebContentClient > , const String & title )
2020-07-04 23:40:17 +02:00
{
if ( on_title_change )
on_title_change ( title ) ;
}
2021-03-02 08:39:07 +11:00
void OutOfProcessWebView : : notify_server_did_request_scroll ( Badge < WebContentClient > , int wheel_delta )
{
vertical_scrollbar ( ) . set_value ( vertical_scrollbar ( ) . value ( ) + wheel_delta * 20 ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_request_scroll_into_view ( Badge < WebContentClient > , const Gfx : : IntRect & rect )
2020-07-05 15:43:43 +02:00
{
scroll_into_view ( rect , true , true ) ;
}
2021-03-30 12:10:06 -04:00
void OutOfProcessWebView : : notify_server_did_enter_tooltip_area ( Badge < WebContentClient > , const Gfx : : IntPoint & , const String & title )
{
GUI : : Application : : the ( ) - > show_tooltip ( title , nullptr ) ;
}
void OutOfProcessWebView : : notify_server_did_leave_tooltip_area ( Badge < WebContentClient > )
{
GUI : : Application : : the ( ) - > hide_tooltip ( ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_hover_link ( Badge < WebContentClient > , const URL & url )
2020-07-05 16:59:20 +02:00
{
if ( on_link_hover )
on_link_hover ( url ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_unhover_link ( Badge < WebContentClient > )
2020-07-05 16:59:20 +02:00
{
2020-09-11 14:28:05 +02:00
set_override_cursor ( Gfx : : StandardCursor : : None ) ;
2020-07-05 16:59:20 +02:00
if ( on_link_hover )
on_link_hover ( { } ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_click_link ( Badge < WebContentClient > , const URL & url , const String & target , unsigned int modifiers )
2020-07-06 20:01:46 +02:00
{
if ( on_link_click )
on_link_click ( url , target , modifiers ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_middle_click_link ( Badge < WebContentClient > , const URL & url , const String & target , unsigned int modifiers )
2020-07-06 20:01:46 +02:00
{
if ( on_link_middle_click )
on_link_middle_click ( url , target , modifiers ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_start_loading ( Badge < WebContentClient > , const URL & url )
2020-07-06 21:58:16 +02:00
{
if ( on_load_start )
on_load_start ( url ) ;
}
2020-12-08 21:44:42 +01:00
void OutOfProcessWebView : : notify_server_did_finish_loading ( Badge < WebContentClient > , const URL & url )
{
if ( on_load_finish )
on_load_finish ( url ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_request_context_menu ( Badge < WebContentClient > , const Gfx : : IntPoint & content_position )
2020-07-07 12:24:29 +02:00
{
if ( on_context_menu_request )
on_context_menu_request ( screen_relative_rect ( ) . location ( ) . translated ( to_widget_position ( content_position ) ) ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : notify_server_did_request_link_context_menu ( Badge < WebContentClient > , const Gfx : : IntPoint & content_position , const URL & url , const String & , unsigned )
2020-07-07 12:24:29 +02:00
{
if ( on_link_context_menu_request )
on_link_context_menu_request ( url , screen_relative_rect ( ) . location ( ) . translated ( to_widget_position ( content_position ) ) ) ;
}
2020-09-12 11:56:13 +02:00
void OutOfProcessWebView : : notify_server_did_request_alert ( Badge < WebContentClient > , const String & message )
{
GUI : : MessageBox : : show ( window ( ) , message , " Alert " , GUI : : MessageBox : : Type : : Information ) ;
}
2021-02-10 08:48:28 +01:00
bool OutOfProcessWebView : : notify_server_did_request_confirm ( Badge < WebContentClient > , const String & message )
{
auto confirm_result = GUI : : MessageBox : : show ( window ( ) , message , " Confirm " , GUI : : MessageBox : : Type : : Warning , GUI : : MessageBox : : InputType : : OKCancel ) ;
return confirm_result = = GUI : : Dialog : : ExecResult : : ExecOK ;
}
2021-02-20 12:05:18 +01:00
String OutOfProcessWebView : : notify_server_did_request_prompt ( Badge < WebContentClient > , const String & message , const String & default_ )
{
String response { default_ } ;
if ( GUI : : InputBox : : show ( window ( ) , response , message , " Prompt " ) = = GUI : : InputBox : : ExecOK )
return response ;
return { } ;
}
2021-02-23 06:17:23 -06:00
void OutOfProcessWebView : : notify_server_did_get_source ( const URL & url , const String & source )
{
if ( on_get_source )
on_get_source ( url , source ) ;
}
2021-02-27 21:47:14 -06:00
void OutOfProcessWebView : : notify_server_did_js_console_output ( const String & method , const String & line )
{
if ( on_js_console_output )
on_js_console_output ( method , line ) ;
}
2021-03-26 10:41:25 -04:00
void OutOfProcessWebView : : notify_server_did_change_favicon ( const Gfx : : Bitmap & favicon )
{
if ( on_favicon_change )
on_favicon_change ( favicon ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : did_scroll ( )
2020-07-04 23:19:32 +02:00
{
2020-07-05 12:45:56 +02:00
client ( ) . post_message ( Messages : : WebContentServer : : SetViewportRect ( visible_content_rect ( ) ) ) ;
2020-07-04 23:19:32 +02:00
request_repaint ( ) ;
}
2020-08-17 16:20:47 +02:00
void OutOfProcessWebView : : request_repaint ( )
2020-07-04 20:57:57 +02:00
{
2020-10-24 12:28:38 +02:00
// If this widget was instantiated but not yet added to a window,
// it won't have a back bitmap yet, so we can just skip repaint requests.
2021-01-30 18:20:40 +01:00
if ( ! m_client_state . back_bitmap )
2020-10-24 12:28:38 +02:00
return ;
2021-01-30 18:20:40 +01:00
client ( ) . post_message ( Messages : : WebContentServer : : Paint ( m_client_state . back_bitmap - > rect ( ) . translated ( horizontal_scrollbar ( ) . value ( ) , vertical_scrollbar ( ) . value ( ) ) , m_client_state . back_bitmap_id ) ) ;
2020-06-17 18:00:18 +02:00
}
2020-08-17 16:20:47 +02:00
WebContentClient & OutOfProcessWebView : : client ( )
2020-06-17 17:31:42 +02:00
{
2021-02-23 20:42:32 +01:00
VERIFY ( m_client_state . client ) ;
2021-01-30 18:20:40 +01:00
return * m_client_state . client ;
2020-06-17 17:31:42 +02:00
}
2020-08-24 15:33:18 +04:30
2021-01-31 09:06:25 +01:00
void OutOfProcessWebView : : debug_request ( const String & request , const String & argument )
{
client ( ) . post_message ( Messages : : WebContentServer : : DebugRequest ( request , argument ) ) ;
}
2021-02-23 06:17:23 -06:00
void OutOfProcessWebView : : get_source ( )
{
client ( ) . post_message ( Messages : : WebContentServer : : GetSource ( ) ) ;
}
2021-02-27 21:47:14 -06:00
void OutOfProcessWebView : : js_console_initialize ( )
{
client ( ) . post_message ( Messages : : WebContentServer : : JSConsoleInitialize ( ) ) ;
}
void OutOfProcessWebView : : js_console_input ( const String & js_source )
{
client ( ) . post_message ( Messages : : WebContentServer : : JSConsoleInput ( js_source ) ) ;
}
2020-08-24 15:33:18 +04:30
}