Skip to content

Commit

Permalink
UI: Prevent crash when right clicking on an unloaded image
Browse files Browse the repository at this point in the history
  • Loading branch information
Gingeh authored and tcl3 committed Jan 12, 2025
1 parent 57479c2 commit 6fd0342
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 20 deletions.
6 changes: 5 additions & 1 deletion Libraries/LibWeb/Page/EventHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,11 @@ EventResult EventHandler::handle_mouseup(CSSPixelPoint viewport_position, CSSPix
if (is<HTML::HTMLImageElement>(*node)) {
auto& image_element = verify_cast<HTML::HTMLImageElement>(*node);
auto image_url = image_element.document().encoding_parse_url(image_element.src());
m_navigable->page().client().page_did_request_image_context_menu(viewport_position, image_url, "", modifiers, image_element.immutable_bitmap()->bitmap());
Optional<Gfx::Bitmap const*> bitmap;
if (image_element.immutable_bitmap())
bitmap = image_element.immutable_bitmap()->bitmap();

m_navigable->page().client().page_did_request_image_context_menu(viewport_position, image_url, "", modifiers, bitmap);
} else if (is<HTML::HTMLMediaElement>(*node)) {
auto& media_element = verify_cast<HTML::HTMLMediaElement>(*node);

Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWeb/Page/Page.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ class PageClient : public JS::Cell {
virtual void page_did_request_cursor_change(Gfx::StandardCursor) { }
virtual void page_did_request_context_menu(CSSPixelPoint) { }
virtual void page_did_request_link_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers) { }
virtual void page_did_request_image_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers, Gfx::Bitmap const*) { }
virtual void page_did_request_image_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers, Optional<Gfx::Bitmap const*>) { }
virtual void page_did_request_media_context_menu(CSSPixelPoint, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers, Page::MediaContextMenu) { }
virtual void page_did_click_link(URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers) { }
virtual void page_did_middle_click_link(URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers) { }
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWebView/ViewImplementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class ViewImplementation {
Function<void()> on_close;
Function<void(Gfx::IntPoint screen_position)> on_context_menu_request;
Function<void(URL::URL const&, Gfx::IntPoint screen_position)> on_link_context_menu_request;
Function<void(URL::URL const&, Gfx::IntPoint screen_position, Gfx::ShareableBitmap const&)> on_image_context_menu_request;
Function<void(URL::URL const&, Gfx::IntPoint screen_position, Optional<Gfx::ShareableBitmap> const&)> on_image_context_menu_request;
Function<void(Gfx::IntPoint screen_position, Web::Page::MediaContextMenu const&)> on_media_context_menu_request;
Function<void(URL::URL const&)> on_link_hover;
Function<void()> on_link_unhover;
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWebView/WebContentClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ void WebContentClient::did_request_link_context_menu(u64 page_id, Gfx::IntPoint
}
}

void WebContentClient::did_request_image_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL const& url, ByteString const&, unsigned, Gfx::ShareableBitmap const& bitmap)
void WebContentClient::did_request_image_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL const& url, ByteString const&, unsigned, Optional<Gfx::ShareableBitmap> const& bitmap)
{
if (auto view = view_for_page_id(page_id); view.has_value()) {
if (view->on_image_context_menu_request)
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWebView/WebContentClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class WebContentClient final
virtual void did_start_loading(u64 page_id, URL::URL const&, bool) override;
virtual void did_request_context_menu(u64 page_id, Gfx::IntPoint) override;
virtual void did_request_link_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned) override;
virtual void did_request_image_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned, Gfx::ShareableBitmap const&) override;
virtual void did_request_image_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned, Optional<Gfx::ShareableBitmap> const&) override;
virtual void did_request_media_context_menu(u64 page_id, Gfx::IntPoint, ByteString const&, unsigned, Web::Page::MediaContextMenu const&) override;
virtual void did_get_source(u64 page_id, URL::URL const&, URL::URL const&, String const&) override;
virtual void did_inspect_dom_tree(u64 page_id, ByteString const&) override;
Expand Down
7 changes: 5 additions & 2 deletions Services/WebContent/PageClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,12 @@ void PageClient::page_did_request_link_context_menu(Web::CSSPixelPoint content_p
client().async_did_request_link_context_menu(m_id, page().css_to_device_point(content_position).to_type<int>(), url, target, modifiers);
}

void PageClient::page_did_request_image_context_menu(Web::CSSPixelPoint content_position, URL::URL const& url, ByteString const& target, unsigned modifiers, Gfx::Bitmap const* bitmap_pointer)
void PageClient::page_did_request_image_context_menu(Web::CSSPixelPoint content_position, URL::URL const& url, ByteString const& target, unsigned modifiers, Optional<Gfx::Bitmap const*> bitmap_pointer)
{
auto bitmap = bitmap_pointer ? bitmap_pointer->to_shareable_bitmap() : Gfx::ShareableBitmap();
Optional<Gfx::ShareableBitmap> bitmap;
if (bitmap_pointer.has_value() && bitmap_pointer.value())
bitmap = bitmap_pointer.value()->to_shareable_bitmap();

client().async_did_request_image_context_menu(m_id, page().css_to_device_point(content_position).to_type<int>(), url, target, modifiers, bitmap);
}

Expand Down
2 changes: 1 addition & 1 deletion Services/WebContent/PageClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class PageClient final : public Web::PageClient {
virtual void page_did_middle_click_link(URL::URL const&, ByteString const& target, unsigned modifiers) override;
virtual void page_did_request_context_menu(Web::CSSPixelPoint) override;
virtual void page_did_request_link_context_menu(Web::CSSPixelPoint, URL::URL const&, ByteString const& target, unsigned modifiers) override;
virtual void page_did_request_image_context_menu(Web::CSSPixelPoint, URL::URL const&, ByteString const& target, unsigned modifiers, Gfx::Bitmap const*) override;
virtual void page_did_request_image_context_menu(Web::CSSPixelPoint, URL::URL const&, ByteString const& target, unsigned modifiers, Optional<Gfx::Bitmap const*>) override;
virtual void page_did_request_media_context_menu(Web::CSSPixelPoint, ByteString const& target, unsigned modifiers, Web::Page::MediaContextMenu) override;
virtual void page_did_start_loading(URL::URL const&, bool) override;
virtual void page_did_create_new_document(Web::DOM::Document&) override;
Expand Down
2 changes: 1 addition & 1 deletion Services/WebContent/WebContentClient.ipc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ endpoint WebContentClient
did_middle_click_link(u64 page_id, URL::URL url, ByteString target, unsigned modifiers) =|
did_request_context_menu(u64 page_id, Gfx::IntPoint content_position) =|
did_request_link_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL url, ByteString target, unsigned modifiers) =|
did_request_image_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL url, ByteString target, unsigned modifiers, Gfx::ShareableBitmap bitmap) =|
did_request_image_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL url, ByteString target, unsigned modifiers, Optional<Gfx::ShareableBitmap> bitmap) =|
did_request_media_context_menu(u64 page_id, Gfx::IntPoint content_position, ByteString target, unsigned modifiers, Web::Page::MediaContextMenu menu) =|
did_request_alert(u64 page_id, String message) =|
did_request_confirm(u64 page_id, String message) =|
Expand Down
19 changes: 15 additions & 4 deletions UI/AppKit/Interface/LadybirdWebView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
static constexpr NSInteger CONTEXT_MENU_LOOP_TAG = 4;
static constexpr NSInteger CONTEXT_MENU_SEARCH_SELECTED_TEXT_TAG = 5;
static constexpr NSInteger CONTEXT_MENU_COPY_LINK_TAG = 6;
static constexpr NSInteger CONTEXT_MENU_COPY_IMAGE_TAG = 7;

// Calls to [NSCursor hide] and [NSCursor unhide] must be balanced. We use this struct to ensure
// we only call [NSCursor hide] once and to ensure that we do call [NSCursor unhide].
Expand All @@ -54,7 +55,7 @@ @interface LadybirdWebView () <NSDraggingDestination>
OwnPtr<Ladybird::WebViewBridge> m_web_view_bridge;

URL::URL m_context_menu_url;
Gfx::ShareableBitmap m_context_menu_bitmap;
Optional<Gfx::ShareableBitmap> m_context_menu_bitmap;
Optional<String> m_context_menu_search_text;

Optional<HideCursor> m_hidden_cursor;
Expand Down Expand Up @@ -669,6 +670,9 @@ - (void)setWebViewCallbacks
TemporaryChange change_url { m_context_menu_url, url };
TemporaryChange change_bitmap { m_context_menu_bitmap, bitmap };

auto* copy_image_menu_item = [self.image_context_menu itemWithTag:CONTEXT_MENU_COPY_IMAGE_TAG];
[copy_image_menu_item setEnabled:bitmap.has_value()];

auto* event = Ladybird::create_context_menu_mouse_event(self, position);
[NSMenu popUpContextMenu:self.image_context_menu withEvent:event forView:self];
};
Expand Down Expand Up @@ -1186,7 +1190,10 @@ - (void)copyLink:(id)sender

- (void)copyImage:(id)sender
{
auto* bitmap = m_context_menu_bitmap.bitmap();
if (!m_context_menu_bitmap.has_value()) {
return;
}
auto* bitmap = m_context_menu_bitmap.value().bitmap();
if (bitmap == nullptr) {
return;
}
Expand Down Expand Up @@ -1310,6 +1317,7 @@ - (NSMenu*)image_context_menu
{
if (!_image_context_menu) {
_image_context_menu = [[NSMenu alloc] initWithTitle:@"Image Context Menu"];
[_image_context_menu setAutoenablesItems:NO];

[_image_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Open Image"
action:@selector(openLink:)
Expand All @@ -1319,9 +1327,12 @@ - (NSMenu*)image_context_menu
keyEquivalent:@""]];
[_image_context_menu addItem:[NSMenuItem separatorItem]];

[_image_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy Image"
auto* copy_image_menu_item = [[NSMenuItem alloc] initWithTitle:@"Copy Image"
action:@selector(copyImage:)
keyEquivalent:@""]];
keyEquivalent:@""];
[copy_image_menu_item setTag:CONTEXT_MENU_COPY_IMAGE_TAG];
[_image_context_menu addItem:copy_image_menu_item];

[_image_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy Image URL"
action:@selector(copyLink:)
keyEquivalent:@""]];
Expand Down
16 changes: 10 additions & 6 deletions UI/Qt/Tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,13 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
open_link_in_new_tab(m_image_context_menu_url);
});

auto* copy_image_action = new QAction("&Copy Image", this);
copy_image_action->setIcon(load_icon_from_uri("resource://icons/16x16/edit-copy.png"sv));
QObject::connect(copy_image_action, &QAction::triggered, this, [this]() {
auto* bitmap = m_image_context_menu_bitmap.bitmap();
m_image_context_menu_copy_image_action = new QAction("&Copy Image", this);
m_image_context_menu_copy_image_action->setIcon(load_icon_from_uri("resource://icons/16x16/edit-copy.png"sv));
QObject::connect(m_image_context_menu_copy_image_action, &QAction::triggered, this, [this]() {
if (!m_image_context_menu_bitmap.has_value())
return;

auto* bitmap = m_image_context_menu_bitmap.value().bitmap();
if (bitmap == nullptr)
return;

Expand All @@ -621,14 +624,15 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
m_image_context_menu->addAction(open_image_action);
m_image_context_menu->addAction(open_image_in_new_tab_action);
m_image_context_menu->addSeparator();
m_image_context_menu->addAction(copy_image_action);
m_image_context_menu->addAction(m_image_context_menu_copy_image_action);
m_image_context_menu->addAction(copy_image_url_action);
m_image_context_menu->addSeparator();
m_image_context_menu->addAction(&m_window->inspect_dom_node_action());

view().on_image_context_menu_request = [this](auto& image_url, Gfx::IntPoint content_position, Gfx::ShareableBitmap const& shareable_bitmap) {
view().on_image_context_menu_request = [this](auto& image_url, Gfx::IntPoint content_position, Optional<Gfx::ShareableBitmap> const& shareable_bitmap) {
m_image_context_menu_url = image_url;
m_image_context_menu_bitmap = shareable_bitmap;
m_image_context_menu_copy_image_action->setEnabled(shareable_bitmap.has_value());

m_image_context_menu->exec(view().map_point_to_global_position(content_position));
};
Expand Down
3 changes: 2 additions & 1 deletion UI/Qt/Tab.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ public slots:
URL::URL m_link_context_menu_url;

QMenu* m_image_context_menu { nullptr };
Gfx::ShareableBitmap m_image_context_menu_bitmap;
QAction* m_image_context_menu_copy_image_action { nullptr };
Optional<Gfx::ShareableBitmap> m_image_context_menu_bitmap;
URL::URL m_image_context_menu_url;

QMenu* m_audio_context_menu { nullptr };
Expand Down

0 comments on commit 6fd0342

Please sign in to comment.