diff --git a/Ladybird/AppKit/UI/Inspector.mm b/Ladybird/AppKit/UI/Inspector.mm index 22ccaed02d..5f8efe1479 100644 --- a/Ladybird/AppKit/UI/Inspector.mm +++ b/Ladybird/AppKit/UI/Inspector.mm @@ -166,6 +166,11 @@ static constexpr NSInteger CONTEXT_MENU_COPY_ATTRIBUTE_VALUE_TAG = 3; m_inspector_client->context_menu_edit_dom_node(); } +- (void)copyDOMNode:(id)sender +{ + m_inspector_client->context_menu_copy_dom_node(); +} + - (void)deleteDOMNode:(id)sender { m_inspector_client->context_menu_remove_dom_node(); @@ -196,6 +201,9 @@ static constexpr NSInteger CONTEXT_MENU_COPY_ATTRIBUTE_VALUE_TAG = 3; [_dom_node_text_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Edit text" action:@selector(editDOMNode:) keyEquivalent:@""]]; + [_dom_node_text_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy text" + action:@selector(copyDOMNode:) + keyEquivalent:@""]]; [_dom_node_text_context_menu addItem:[NSMenuItem separatorItem]]; @@ -226,6 +234,12 @@ static constexpr NSInteger CONTEXT_MENU_COPY_ATTRIBUTE_VALUE_TAG = 3; [_dom_node_tag_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Delete node" action:@selector(deleteDOMNode:) keyEquivalent:@""]]; + + [_dom_node_tag_context_menu addItem:[NSMenuItem separatorItem]]; + + [_dom_node_tag_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy HTML" + action:@selector(copyDOMNode:) + keyEquivalent:@""]]; } return _dom_node_tag_context_menu; @@ -262,6 +276,12 @@ static constexpr NSInteger CONTEXT_MENU_COPY_ATTRIBUTE_VALUE_TAG = 3; [_dom_node_attribute_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Delete node" action:@selector(deleteDOMNode:) keyEquivalent:@""]]; + + [_dom_node_attribute_context_menu addItem:[NSMenuItem separatorItem]]; + + [_dom_node_attribute_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy HTML" + action:@selector(copyDOMNode:) + keyEquivalent:@""]]; } return _dom_node_attribute_context_menu; diff --git a/Ladybird/Qt/InspectorWidget.cpp b/Ladybird/Qt/InspectorWidget.cpp index 6077889999..eeb48fcae0 100644 --- a/Ladybird/Qt/InspectorWidget.cpp +++ b/Ladybird/Qt/InspectorWidget.cpp @@ -30,6 +30,9 @@ InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view) m_edit_node_action = new QAction("&Edit node", this); connect(m_edit_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_edit_dom_node(); }); + m_copy_node_action = new QAction("&Copy HTML", this); + connect(m_copy_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_copy_dom_node(); }); + m_delete_node_action = new QAction("&Delete node", this); connect(m_delete_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_remove_dom_node(); }); @@ -44,6 +47,7 @@ InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view) m_dom_node_text_context_menu = new QMenu("DOM text context menu", this); m_dom_node_text_context_menu->addAction(m_edit_node_action); + m_dom_node_text_context_menu->addAction(m_copy_node_action); m_dom_node_text_context_menu->addSeparator(); m_dom_node_text_context_menu->addAction(m_delete_node_action); @@ -52,6 +56,8 @@ InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view) m_dom_node_tag_context_menu->addSeparator(); m_dom_node_tag_context_menu->addAction(m_add_attribute_action); m_dom_node_tag_context_menu->addAction(m_delete_node_action); + m_dom_node_tag_context_menu->addSeparator(); + m_dom_node_tag_context_menu->addAction(m_copy_node_action); m_dom_node_attribute_context_menu = new QMenu("DOM attribute context menu", this); m_dom_node_attribute_context_menu->addAction(m_edit_node_action); @@ -60,15 +66,19 @@ InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view) m_dom_node_attribute_context_menu->addSeparator(); m_dom_node_attribute_context_menu->addAction(m_add_attribute_action); m_dom_node_attribute_context_menu->addAction(m_delete_node_action); + m_dom_node_attribute_context_menu->addSeparator(); + m_dom_node_attribute_context_menu->addAction(m_copy_node_action); m_inspector_client->on_requested_dom_node_text_context_menu = [this](auto position) { m_edit_node_action->setText("&Edit text"); + m_copy_node_action->setText("&Copy text"); m_dom_node_text_context_menu->exec(to_widget_position(position)); }; m_inspector_client->on_requested_dom_node_tag_context_menu = [this](auto position, auto const& tag) { m_edit_node_action->setText(qstring_from_ak_string(MUST(String::formatted("&Edit \"{}\"", tag)))); + m_copy_node_action->setText("&Copy HTML"); m_dom_node_tag_context_menu->exec(to_widget_position(position)); }; @@ -76,6 +86,7 @@ InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view) m_inspector_client->on_requested_dom_node_attribute_context_menu = [this](auto position, auto const&, WebView::Attribute const& attribute) { static constexpr size_t MAX_ATTRIBUTE_VALUE_LENGTH = 32; + m_copy_node_action->setText("&Copy HTML"); m_edit_node_action->setText(qstring_from_ak_string(MUST(String::formatted("&Edit attribute \"{}\"", attribute.name)))); m_remove_attribute_action->setText(qstring_from_ak_string(MUST(String::formatted("&Remove attribute \"{}\"", attribute.name)))); m_copy_attribute_value_action->setText(qstring_from_ak_string(MUST(String::formatted("Copy attribute &value \"{:.{}}{}\"", diff --git a/Ladybird/Qt/InspectorWidget.h b/Ladybird/Qt/InspectorWidget.h index b3f476e966..b4b3a4ee91 100644 --- a/Ladybird/Qt/InspectorWidget.h +++ b/Ladybird/Qt/InspectorWidget.h @@ -44,6 +44,7 @@ private: QMenu* m_dom_node_attribute_context_menu { nullptr }; QAction* m_edit_node_action { nullptr }; + QAction* m_copy_node_action { nullptr }; QAction* m_delete_node_action { nullptr }; QAction* m_add_attribute_action { nullptr }; QAction* m_remove_attribute_action { nullptr }; diff --git a/Userland/Applications/Browser/InspectorWidget.cpp b/Userland/Applications/Browser/InspectorWidget.cpp index e03d2ab74c..5b90ac52c6 100644 --- a/Userland/Applications/Browser/InspectorWidget.cpp +++ b/Userland/Applications/Browser/InspectorWidget.cpp @@ -30,6 +30,7 @@ InspectorWidget::InspectorWidget(WebView::OutOfProcessWebView& content_view) m_inspector_client = make(content_view, *m_inspector_view); m_edit_node_action = GUI::Action::create("&Edit node"sv, [this](auto&) { m_inspector_client->context_menu_edit_dom_node(); }); + m_copy_node_action = GUI::Action::create("&Copy HTML"sv, [this](auto&) { m_inspector_client->context_menu_copy_dom_node(); }); m_delete_node_action = GUI::Action::create("&Delete node"sv, [this](auto&) { m_inspector_client->context_menu_remove_dom_node(); }); m_add_attribute_action = GUI::Action::create("&Add attribute"sv, [this](auto&) { m_inspector_client->context_menu_add_dom_node_attribute(); }); m_remove_attribute_action = GUI::Action::create("&Remove attribute"sv, [this](auto&) { m_inspector_client->context_menu_remove_dom_node_attribute(); }); @@ -37,6 +38,7 @@ InspectorWidget::InspectorWidget(WebView::OutOfProcessWebView& content_view) m_dom_node_text_context_menu = GUI::Menu::construct(); m_dom_node_text_context_menu->add_action(*m_edit_node_action); + m_dom_node_text_context_menu->add_action(*m_copy_node_action); m_dom_node_text_context_menu->add_separator(); m_dom_node_text_context_menu->add_action(*m_delete_node_action); @@ -45,6 +47,8 @@ InspectorWidget::InspectorWidget(WebView::OutOfProcessWebView& content_view) m_dom_node_tag_context_menu->add_separator(); m_dom_node_tag_context_menu->add_action(*m_add_attribute_action); m_dom_node_tag_context_menu->add_action(*m_delete_node_action); + m_dom_node_tag_context_menu->add_separator(); + m_dom_node_tag_context_menu->add_action(*m_copy_node_action); m_dom_node_attribute_context_menu = GUI::Menu::construct(); m_dom_node_attribute_context_menu->add_action(*m_edit_node_action); @@ -53,15 +57,19 @@ InspectorWidget::InspectorWidget(WebView::OutOfProcessWebView& content_view) m_dom_node_attribute_context_menu->add_separator(); m_dom_node_attribute_context_menu->add_action(*m_add_attribute_action); m_dom_node_attribute_context_menu->add_action(*m_delete_node_action); + m_dom_node_attribute_context_menu->add_separator(); + m_dom_node_attribute_context_menu->add_action(*m_copy_node_action); m_inspector_client->on_requested_dom_node_text_context_menu = [this](auto position) { m_edit_node_action->set_text("&Edit text"); + m_copy_node_action->set_text("&Copy text"); m_dom_node_text_context_menu->popup(to_widget_position(position)); }; m_inspector_client->on_requested_dom_node_tag_context_menu = [this](auto position, auto const& tag) { m_edit_node_action->set_text(DeprecatedString::formatted("&Edit \"{}\"", tag)); + m_copy_node_action->set_text("&Copy HTML"); m_dom_node_tag_context_menu->popup(to_widget_position(position)); }; @@ -69,6 +77,7 @@ InspectorWidget::InspectorWidget(WebView::OutOfProcessWebView& content_view) m_inspector_client->on_requested_dom_node_attribute_context_menu = [this](auto position, auto const&, auto const& attribute) { static constexpr size_t MAX_ATTRIBUTE_VALUE_LENGTH = 32; + m_copy_node_action->set_text("&Copy HTML"); m_edit_node_action->set_text(DeprecatedString::formatted("&Edit attribute \"{}\"", attribute.name)); m_remove_attribute_action->set_text(DeprecatedString::formatted("&Remove attribute \"{}\"", attribute.name)); m_copy_attribute_value_action->set_text(DeprecatedString::formatted("Copy attribute &value \"{:.{}}{}\"", diff --git a/Userland/Applications/Browser/InspectorWidget.h b/Userland/Applications/Browser/InspectorWidget.h index 99e6f9d1da..9419f576e5 100644 --- a/Userland/Applications/Browser/InspectorWidget.h +++ b/Userland/Applications/Browser/InspectorWidget.h @@ -40,6 +40,7 @@ private: RefPtr m_dom_node_attribute_context_menu; RefPtr m_edit_node_action; + RefPtr m_copy_node_action; RefPtr m_delete_node_action; RefPtr m_add_attribute_action; RefPtr m_remove_attribute_action; diff --git a/Userland/Libraries/LibWebView/InspectorClient.cpp b/Userland/Libraries/LibWebView/InspectorClient.cpp index 0da75d8672..e6aff3ccf7 100644 --- a/Userland/Libraries/LibWebView/InspectorClient.cpp +++ b/Userland/Libraries/LibWebView/InspectorClient.cpp @@ -233,6 +233,18 @@ void InspectorClient::context_menu_edit_dom_node() m_context_menu_data.clear(); } +void InspectorClient::context_menu_copy_dom_node() +{ + VERIFY(m_context_menu_data.has_value()); + + if (auto html = m_content_web_view.get_dom_node_html(m_context_menu_data->dom_node_id); html.has_value()) { + if (m_content_web_view.on_insert_clipboard_entry) + m_content_web_view.on_insert_clipboard_entry(*html, "unspecified"_string, "text/plain"_string); + } + + m_context_menu_data.clear(); +} + void InspectorClient::context_menu_remove_dom_node() { VERIFY(m_context_menu_data.has_value()); diff --git a/Userland/Libraries/LibWebView/InspectorClient.h b/Userland/Libraries/LibWebView/InspectorClient.h index 81f78f276d..e99b050da9 100644 --- a/Userland/Libraries/LibWebView/InspectorClient.h +++ b/Userland/Libraries/LibWebView/InspectorClient.h @@ -27,6 +27,7 @@ public: void clear_selection(); void context_menu_edit_dom_node(); + void context_menu_copy_dom_node(); void context_menu_remove_dom_node(); void context_menu_add_dom_node_attribute(); void context_menu_remove_dom_node_attribute();