[epiphany/pgriffis/browser-action-abstraction: 5/5] WebExtensions: Redesign Browser Actions to have a flexible presentation




commit 9496817d20251e3c00dfba37279de04a717c7b21
Author: Patrick Griffis <pgriffis igalia com>
Date:   Mon Oct 10 20:01:00 2022 -0500

    WebExtensions: Redesign Browser Actions to have a flexible presentation
    
    Previously the WebExtensionManager would directly poke around inserting
    widgets into the HeaderBar. This falls apart with adaptive views so
    switching to mobile would lose access to extensions.
    
    This changes the design where WebExtensionsManager exposes a list of
    EphyBrowserAction objects and the UI tracks this list and decides
    how to show them in any mode or in multiple windows.
    
    As well as this change the UI was changed so now all browser actions
    are shown in a popover and the popover doubles as the browser actions
    popup UI when clicked. This matches Firefox's UI (but is even smoother!).
    
    The ability to show all actions as a dedicated button in the headerbar
    was lost for now. I think this is ok for now but ideally we implement
    a solution similar to other browsers where the user can select which
    ones to be dedicated buttons and which ones to go into the dropdown.
    
    Part-of: <https://gitlab.gnome.org/GNOME/epiphany/-/merge_requests/1199>

 meson.build                                   |   6 +-
 src/ephy-action-bar-end.c                     | 182 ++++++++++++++++++++++--
 src/ephy-action-bar-end.h                     |   5 +-
 src/ephy-header-bar.c                         |   7 -
 src/ephy-header-bar.h                         |   3 -
 src/resources/epiphany.gresource.xml          |   1 +
 src/resources/gtk/action-bar-end.ui           |  92 ++++++++++++-
 src/resources/gtk/browser-action-row.ui       |  32 +++++
 src/resources/themes/_shared-base.scss        |   4 +
 src/webextension/api/commands.c               |   3 +-
 src/webextension/api/menus.c                  |   4 +-
 src/webextension/ephy-browser-action-row.c    | 147 ++++++++++++++++++++
 src/webextension/ephy-browser-action-row.h    |  37 +++++
 src/webextension/ephy-browser-action.c        | 150 ++++++++++++++++++++
 src/webextension/ephy-browser-action.h        |  39 ++++++
 src/webextension/ephy-web-extension-manager.c | 191 +++++++++++---------------
 src/webextension/ephy-web-extension-manager.h |  14 +-
 src/webextension/meson.build                  |   2 +
 18 files changed, 767 insertions(+), 152 deletions(-)
---
diff --git a/meson.build b/meson.build
index 0270ed4fd..30fb154c4 100644
--- a/meson.build
+++ b/meson.build
@@ -73,13 +73,13 @@ gsb_api_key = get_option('gsb_api_key')
 conf.set_quoted('GSB_API_KEY', gsb_api_key)
 conf.set10('ENABLE_GSB', gsb_api_key != '')
 
-glib_requirement = '>= 2.67.4'
+glib_requirement = '>= 2.70.0'
 gtk_requirement = '>= 3.24.0'
 nettle_requirement = '>= 3.4'
 webkitgtk_requirement = '>= 2.37.90'
 
-conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_68')
-conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_68')
+conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_70')
+conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_70')
 
 cairo_dep = dependency('cairo', version: '>= 1.2')
 gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
diff --git a/src/ephy-action-bar-end.c b/src/ephy-action-bar-end.c
index c6e0cd154..c93c3d78a 100644
--- a/src/ephy-action-bar-end.c
+++ b/src/ephy-action-bar-end.c
@@ -21,10 +21,13 @@
 
 #include "ephy-action-bar-end.h"
 #include "ephy-add-bookmark-popover.h"
+#include "ephy-browser-action.h"
+#include "ephy-browser-action-row.h"
 #include "ephy-desktop-utils.h"
 #include "ephy-downloads-popover.h"
 #include "ephy-location-entry.h"
 #include "ephy-shell.h"
+#include "ephy-web-extension-manager.h"
 #include "ephy-window.h"
 
 #define NEEDS_ATTENTION_ANIMATION_TIMEOUT 2000 /*ms */
@@ -42,7 +45,13 @@ struct _EphyActionBarEnd {
   GtkWidget *downloads_popover;
   GtkWidget *downloads_icon;
   GtkWidget *downloads_progress;
-  GtkWidget *browser_action_box;
+  GtkWidget *browser_actions_button;
+  GtkWidget *browser_actions_popover;
+  GtkWidget *browser_actions_scrolled_window;
+  GtkWidget *browser_actions_listbox;
+  GtkWidget *browser_actions_stack;
+  GtkWidget *browser_actions_popup_view_box;
+  GtkWidget *browser_actions_popup_view_label;
 
   guint downloads_button_attention_timeout_id;
 };
@@ -50,6 +59,8 @@ struct _EphyActionBarEnd {
 G_DEFINE_TYPE (EphyActionBarEnd, ephy_action_bar_end, GTK_TYPE_BOX)
 
 static void begin_complete_theatrics (EphyActionBarEnd *self);
+static void set_browser_actions (EphyActionBarEnd *action_bar_end,
+                                 GListStore       *browser_actions);
 
 static void
 remove_downloads_button_attention_style (EphyActionBarEnd *self)
@@ -223,6 +234,93 @@ show_downloads_cb (EphyDownloadsManager *manager,
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (action_bar_end->downloads_button), TRUE);
 }
 
+static void
+remove_popup_webview (EphyActionBarEnd *action_bar_end)
+{
+  GList *children = gtk_container_get_children (GTK_CONTAINER 
(action_bar_end->browser_actions_popup_view_box));
+  GtkWidget *last_child = GTK_WIDGET (g_list_last (children)->data);
+
+  if (WEBKIT_IS_WEB_VIEW (last_child))
+    gtk_widget_destroy (last_child);
+
+  g_list_free (children);
+}
+
+static void
+show_browser_action_popup (EphyActionBarEnd  *action_bar_end,
+                           EphyBrowserAction *action)
+{
+  EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
+  EphyWebExtension *web_extension = ephy_browser_action_get_web_extension (action);
+  GtkWidget *popup_view = ephy_web_extension_manager_create_browser_popup (manager, web_extension);
+
+  gtk_container_add (GTK_CONTAINER (action_bar_end->browser_actions_popup_view_box), popup_view);
+
+  gtk_label_set_text (GTK_LABEL (action_bar_end->browser_actions_popup_view_label),
+                      ephy_browser_action_get_title (action));
+
+  gtk_stack_set_visible_child (GTK_STACK (action_bar_end->browser_actions_stack),
+                               action_bar_end->browser_actions_popup_view_box);
+}
+
+static void
+browser_actions_popup_view_back_clicked_cb (GtkButton        *button,
+                                            EphyActionBarEnd *action_bar_end)
+{
+  gtk_stack_set_visible_child (GTK_STACK (action_bar_end->browser_actions_stack),
+                               action_bar_end->browser_actions_scrolled_window);
+  remove_popup_webview (action_bar_end);
+}
+
+static void
+show_browser_action_cb (EphyWebExtensionManager *manager,
+                        EphyBrowserAction       *action,
+                        EphyActionBarEnd        *action_bar_end)
+{
+  GtkWindow *parent_window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (action_bar_end)));
+  GtkWindow *active_window = gtk_application_get_active_window (GTK_APPLICATION (g_application_get_default 
()));
+
+  /* There may be multiple action bars that exist. We only want to show the popup in the visible bar the 
active window. */
+  if (parent_window != active_window)
+    return;
+  if (!gtk_widget_is_visible (action_bar_end->browser_actions_button))
+    return;
+
+  remove_popup_webview (action_bar_end);
+  gtk_popover_popup (GTK_POPOVER (action_bar_end->browser_actions_popover));
+  show_browser_action_popup (action_bar_end, action);
+}
+
+static void
+browser_actions_row_activated_cb (GtkListBox           *listbox,
+                                  EphyBrowserActionRow *row,
+                                  EphyActionBarEnd     *action_bar_end)
+{
+  EphyBrowserAction *action = ephy_browser_action_row_get_browser_action (row);
+
+  /* If it was handled we are done, otherwise we have to show a popup. */
+  if (ephy_browser_action_activate (action)) {
+    gtk_popover_popdown (GTK_POPOVER (action_bar_end->browser_actions_popover));
+    return;
+  }
+
+  show_browser_action_popup (action_bar_end, action);
+}
+
+static void
+browser_action_popover_visible_changed_cb (GtkWidget        *popover,
+                                           GParamSpec       *pspec,
+                                           EphyActionBarEnd *action_bar_end)
+{
+  if (!gtk_widget_get_visible (popover)) {
+    GtkStack *stack = GTK_STACK (action_bar_end->browser_actions_stack);
+
+    /* Reset to default state and destroy any open webview. */
+    gtk_stack_set_visible_child (stack, action_bar_end->browser_actions_scrolled_window);
+    remove_popup_webview (action_bar_end);
+  }
+}
+
 static void
 ephy_action_bar_end_class_init (EphyActionBarEndClass *klass)
 {
@@ -254,7 +352,30 @@ ephy_action_bar_end_class_init (EphyActionBarEndClass *klass)
                                         downloads_progress);
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarEnd,
-                                        browser_action_box);
+                                        browser_actions_button);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphyActionBarEnd,
+                                        browser_actions_popover);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphyActionBarEnd,
+                                        browser_actions_scrolled_window);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphyActionBarEnd,
+                                        browser_actions_listbox);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphyActionBarEnd,
+                                        browser_actions_stack);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphyActionBarEnd,
+                                        browser_actions_popup_view_box);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphyActionBarEnd,
+                                        browser_actions_popup_view_label);
+
+  gtk_widget_class_bind_template_callback (widget_class,
+                                           browser_actions_popup_view_back_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class,
+                                           browser_actions_row_activated_cb);
 }
 
 static void
@@ -263,6 +384,7 @@ ephy_action_bar_end_init (EphyActionBarEnd *action_bar_end)
   GObject *object = G_OBJECT (action_bar_end);
   EphyDownloadsManager *downloads_manager;
   GtkWidget *popover;
+  EphyWebExtensionManager *extension_manager;
 
   gtk_widget_init_template (GTK_WIDGET (action_bar_end));
 
@@ -307,6 +429,17 @@ ephy_action_bar_end_init (EphyActionBarEnd *action_bar_end)
   popover = ephy_add_bookmark_popover_new ();
 
   gtk_menu_button_set_popover (GTK_MENU_BUTTON (action_bar_end->bookmark_button), popover);
+
+  extension_manager = ephy_web_extension_manager_get_default ();
+  g_signal_connect_object (extension_manager, "show-browser-action",
+                           G_CALLBACK (show_browser_action_cb),
+                           object, 0);
+
+  set_browser_actions (action_bar_end, ephy_web_extension_manager_get_browser_actions (extension_manager));
+
+  g_signal_connect (action_bar_end->browser_actions_popover, "notify::visible",
+                    G_CALLBACK (browser_action_popover_visible_changed_cb),
+                    action_bar_end);
 }
 
 EphyActionBarEnd *
@@ -349,13 +482,6 @@ ephy_action_bar_end_get_downloads_revealer (EphyActionBarEnd *action_bar_end)
   return action_bar_end->downloads_revealer;
 }
 
-void
-ephy_action_bar_end_add_browser_action (EphyActionBarEnd *action_bar_end,
-                                        GtkWidget        *action)
-{
-  gtk_container_add (GTK_CONTAINER (action_bar_end->browser_action_box), action);
-}
-
 void
 ephy_action_bar_end_set_show_bookmark_button (EphyActionBarEnd *action_bar_end,
                                               gboolean          show)
@@ -390,3 +516,41 @@ ephy_action_bar_end_set_bookmark_icon_state (EphyActionBarEnd      *action_bar_e
       g_assert_not_reached ();
   }
 }
+
+GtkWidget *
+create_browser_action_item_widget (EphyBrowserAction *action,
+                                   gpointer           user_data)
+{
+  return ephy_browser_action_row_new (action);
+}
+
+static void
+browser_actions_items_changed_cb (GListModel       *list,
+                                  guint             position,
+                                  guint             removed,
+                                  guint             added,
+                                  EphyActionBarEnd *action_bar_end)
+{
+  gtk_widget_set_visible (action_bar_end->browser_actions_button, g_list_model_get_n_items (list) != 0);
+
+  /* This handles an edge-case where if an extension is disabled while its popover is open the webview 
should be destroyed.
+   * However in normal usage this shouldn't happen and with the GTK4 port the extension dialog is also modal.
+   * So we just always manually close it instead of trying to track which extension popup is open. */
+  if (removed)
+    gtk_popover_popdown (GTK_POPOVER (action_bar_end->browser_actions_popover));
+}
+
+static void
+set_browser_actions (EphyActionBarEnd *action_bar_end,
+                     GListStore       *browser_actions)
+{
+  gtk_list_box_bind_model (GTK_LIST_BOX (action_bar_end->browser_actions_listbox),
+                           G_LIST_MODEL (browser_actions),
+                           (GtkListBoxCreateWidgetFunc)create_browser_action_item_widget,
+                           NULL, NULL);
+
+  g_signal_connect_object (browser_actions, "items-changed", G_CALLBACK (browser_actions_items_changed_cb),
+                           action_bar_end, 0);
+
+  browser_actions_items_changed_cb (G_LIST_MODEL (browser_actions), 0, 0, 0, action_bar_end);
+}
diff --git a/src/ephy-action-bar-end.h b/src/ephy-action-bar-end.h
index ffecf9596..3872aed46 100644
--- a/src/ephy-action-bar-end.h
+++ b/src/ephy-action-bar-end.h
@@ -38,13 +38,12 @@ void              ephy_action_bar_end_show_downloads            (EphyActionBarEn
 void              ephy_action_bar_end_show_bookmarks            (EphyActionBarEnd *action_bar_end);
 GtkWidget        *ephy_action_bar_end_get_downloads_revealer    (EphyActionBarEnd *action_bar_end);
 
-void              ephy_action_bar_end_add_browser_action        (EphyActionBarEnd *action_bar_end,
-                                                                 GtkWidget        *action);
+void              ephy_action_bar_end_set_browser_actions       (EphyActionBarEnd *action_bar_end,
+                                                                 GListStore       *browser_actions);
 
 void              ephy_action_bar_end_set_show_bookmark_button (EphyActionBarEnd *action_bar_end,
                                                                 gboolean          show);
 
 void              ephy_action_bar_end_set_bookmark_icon_state (EphyActionBarEnd       *action_bar_end,
                                                                EphyBookmarkIconState  state);
-
 G_END_DECLS
diff --git a/src/ephy-header-bar.c b/src/ephy-header-bar.c
index d6132ba8b..8a08d3fa1 100644
--- a/src/ephy-header-bar.c
+++ b/src/ephy-header-bar.c
@@ -468,10 +468,3 @@ ephy_header_bar_set_zoom_level (EphyHeaderBar *header_bar,
 
   gtk_label_set_label (GTK_LABEL (header_bar->zoom_level_label), zoom_level);
 }
-
-void
-ephy_header_bar_add_browser_action (EphyHeaderBar *header_bar,
-                                    GtkWidget     *action)
-{
-  ephy_action_bar_end_add_browser_action (header_bar->action_bar_end, action);
-}
diff --git a/src/ephy-header-bar.h b/src/ephy-header-bar.h
index f066de16d..56fde0d3d 100644
--- a/src/ephy-header-bar.h
+++ b/src/ephy-header-bar.h
@@ -49,7 +49,4 @@ void                ephy_header_bar_start_change_combined_stop_reload_state (Eph
 void                ephy_header_bar_set_zoom_level                 (EphyHeaderBar *header_bar,
                                                                     gdouble        zoom);
 
-void                ephy_header_bar_add_browser_action             (EphyHeaderBar *header_bar,
-                                                                    GtkWidget     *action);
-
 G_END_DECLS
diff --git a/src/resources/epiphany.gresource.xml b/src/resources/epiphany.gresource.xml
index d962bccd1..d9491d6cf 100644
--- a/src/resources/epiphany.gresource.xml
+++ b/src/resources/epiphany.gresource.xml
@@ -8,6 +8,7 @@
     <file preprocess="xml-stripblanks" compressed="true">gtk/bookmark-properties.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/bookmark-row.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/bookmarks-popover.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/browser-action-row.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/clear-data-view.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/data-view.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/encoding-dialog.ui</file>
diff --git a/src/resources/gtk/action-bar-end.ui b/src/resources/gtk/action-bar-end.ui
index fc86d7b69..a2401ef7e 100644
--- a/src/resources/gtk/action-bar-end.ui
+++ b/src/resources/gtk/action-bar-end.ui
@@ -2,12 +2,6 @@
 <interface>
   <template class="EphyActionBarEnd" parent="GtkBox">
     <property name="spacing">6</property>
-    <child>
-      <object class="GtkBox" id="browser_action_box">
-        <property name="visible">True</property>
-        <property name="spacing">6</property>
-      </object>
-    </child>
     <child>
       <object class="GtkRevealer" id="downloads_revealer">
         <property name="visible">True</property>
@@ -49,6 +43,21 @@
         </child>
       </object>
     </child>
+    <child>
+      <object class="GtkMenuButton" id="browser_actions_button">
+        <!-- Translators: tooltip for the webextension actions button -->
+        <property name="tooltip_text" translatable="yes">View extension actions</property>
+        <property name="visible">False</property>
+        <property name="receives_default">True</property>
+        <property name="popover">browser_actions_popover</property>
+        <child>
+          <object class="GtkImage">
+            <property name="visible">True</property>
+            <property name="icon-name">application-x-addon-symbolic</property>
+          </object>
+        </child>
+      </object>
+    </child>
     <child>
       <object class="GtkMenuButton" id="bookmark_button">
         <property name="visible">True</property>
@@ -88,4 +97,75 @@
   <object class="EphyBookmarksPopover" id="bookmarks_popover">
     <property name="visible">True</property>
   </object>
+  <object class="GtkPopover" id="browser_actions_popover">
+    <property name="width-request">250</property>
+    <child>
+      <object class="GtkStack" id="browser_actions_stack">
+        <property name="visible">True</property>
+        <property name="transition-type">slide-left-right</property>
+        <property name="interpolate-size">True</property>
+        <property name="margin-start">6</property>
+        <property name="margin-end">6</property>
+        <property name="margin-top">6</property>
+        <property name="margin-bottom">6</property>
+        <child>
+          <object class="GtkScrolledWindow" id="browser_actions_scrolled_window">
+            <property name="visible">True</property>
+            <property name="hscrollbar-policy">never</property>
+            <property name="max-content-height">360</property>
+            <property name="propagate-natural-height">True</property>
+            <child>
+              <object class="GtkListBox" id="browser_actions_listbox">
+                <property name="visible">True</property>
+                <property name="selection-mode">none</property>
+                <signal name="row-activated" handler="browser_actions_row_activated_cb"/>
+                <style>
+                  <class name="browser-actions-listbox"/>\
+                  <class name="background"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox" id="browser_actions_popup_view_box">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="orientation">horizontal</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkButton">
+                    <property name="visible">True</property>
+                    <signal name="clicked" handler="browser_actions_popup_view_back_clicked_cb"/>
+                    <style>
+                      <class name="flat"/>
+                    </style>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon-name">go-previous-symbolic</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="browser_actions_popup_view_label">
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <style>
+                      <class name="heading"/>
+                    </style>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/src/resources/gtk/browser-action-row.ui b/src/resources/gtk/browser-action-row.ui
new file mode 100644
index 000000000..0ba4b0d01
--- /dev/null
+++ b/src/resources/gtk/browser-action-row.ui
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.18"/>
+  <template class="EphyBrowserActionRow" parent="GtkListBoxRow">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="spacing">6</property>
+        <property name="margin-start">6</property>
+        <property name="margin-end">6</property>
+        <property name="margin-top">6</property>
+        <property name="margin-bottom">6</property>
+        <child>
+          <object class="GtkImage" id="browser_action_image">
+            <property name="visible">True</property>
+            <property name="pixel-size">16</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="title_label">
+            <property name="visible">True</property>
+            <property name="hexpand">True</property>
+            <property name="ellipsize">end</property>
+            <property name="max_width_chars">40</property>
+            <property name="xalign">0</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/resources/themes/_shared-base.scss b/src/resources/themes/_shared-base.scss
index d1628d4e1..7c0a6bc71 100644
--- a/src/resources/themes/_shared-base.scss
+++ b/src/resources/themes/_shared-base.scss
@@ -253,3 +253,7 @@ fullscreenbox > flap {
     background: linear-gradient(to bottom, gtkalpha(black, .1), gtkalpha(black, .0));
   }
 }
+
+.browser-actions-listbox {
+  padding: 6px;
+}
\ No newline at end of file
diff --git a/src/webextension/api/commands.c b/src/webextension/api/commands.c
index 31e3c4a2e..8f4c3218b 100644
--- a/src/webextension/api/commands.c
+++ b/src/webextension/api/commands.c
@@ -33,12 +33,11 @@ on_command_activated (GAction  *action,
   EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
   EphyShell *shell = ephy_shell_get_default ();
   EphyWebView *view = EPHY_WEB_VIEW (ephy_shell_get_active_web_view (shell));
-  EphyWindow *window = EPHY_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (shell)));
   GtkWidget *button;
   const char *command_name = g_object_get_data (G_OBJECT (action), "command-name-json");
 
   if (strcmp (command_name, "\"_execute_browser_action\"") == 0) {
-    ephy_web_extension_manager_activate_browser_action (manager, self, window);
+    ephy_web_extension_manager_show_browser_action (manager, self);
     return;
   } else if (strcmp (command_name, "\"_execute_page_action\"") == 0) {
     button = ephy_web_extension_manager_get_page_action (manager, self, view);
diff --git a/src/webextension/api/menus.c b/src/webextension/api/menus.c
index 674f39a75..1cc6f0cfb 100644
--- a/src/webextension/api/menus.c
+++ b/src/webextension/api/menus.c
@@ -558,10 +558,8 @@ menu_activate_browser_action (gpointer user_data)
 {
   EphyWebExtension *web_extension = user_data;
   EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
-  EphyShell *shell = ephy_shell_get_default ();
-  EphyWindow *window = EPHY_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (shell)));
 
-  ephy_web_extension_manager_activate_browser_action (manager, web_extension, window);
+  ephy_web_extension_manager_show_browser_action (manager, web_extension);
   return G_SOURCE_REMOVE;
 }
 
diff --git a/src/webextension/ephy-browser-action-row.c b/src/webextension/ephy-browser-action-row.c
new file mode 100644
index 000000000..164645902
--- /dev/null
+++ b/src/webextension/ephy-browser-action-row.c
@@ -0,0 +1,147 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2016 Iulian-Gabriel Radu <iulian radu67 gmail com>
+ *  Copyright 2022 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-browser-action-row.h"
+
+struct _EphyBrowserActionRow {
+  GtkListBoxRow parent_instance;
+
+  EphyBrowserAction *browser_action;
+
+  GtkWidget *browser_action_image;
+  GtkWidget *title_label;
+};
+
+G_DEFINE_FINAL_TYPE (EphyBrowserActionRow, ephy_browser_action_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+  PROP_0,
+  PROP_BROWSER_ACTION,
+  LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+static void
+ephy_browser_action_row_set_property (GObject      *object,
+                                      guint         prop_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
+{
+  EphyBrowserActionRow *self = EPHY_BROWSER_ACTION_ROW (object);
+
+  switch (prop_id) {
+    case PROP_BROWSER_ACTION:
+      self->browser_action = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_browser_action_row_get_property (GObject    *object,
+                                      guint       prop_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  EphyBrowserActionRow *self = EPHY_BROWSER_ACTION_ROW (object);
+
+  switch (prop_id) {
+    case PROP_BROWSER_ACTION:
+      g_value_set_object (value, ephy_browser_action_row_get_browser_action (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_browser_action_row_constructed (GObject *object)
+{
+  EphyBrowserActionRow *self = EPHY_BROWSER_ACTION_ROW (object);
+
+  gtk_label_set_label (GTK_LABEL (self->title_label),
+                       ephy_browser_action_get_title (self->browser_action));
+  gtk_image_set_from_pixbuf (GTK_IMAGE (self->browser_action_image),
+                             ephy_browser_action_get_pixbuf (self->browser_action, 16));
+
+  G_OBJECT_CLASS (ephy_browser_action_row_parent_class)->constructed (object);
+}
+
+static void
+ephy_browser_action_row_dispose (GObject *object)
+{
+  EphyBrowserActionRow *self = EPHY_BROWSER_ACTION_ROW (object);
+
+  g_clear_object (&self->browser_action);
+
+  G_OBJECT_CLASS (ephy_browser_action_row_parent_class)->dispose (object);
+}
+
+static void
+ephy_browser_action_row_class_init (EphyBrowserActionRowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->set_property = ephy_browser_action_row_set_property;
+  object_class->get_property = ephy_browser_action_row_get_property;
+  object_class->dispose = ephy_browser_action_row_dispose;
+  object_class->constructed = ephy_browser_action_row_constructed;
+
+  obj_properties[PROP_BROWSER_ACTION] =
+    g_param_spec_object ("browser-action",
+                         "",
+                         "",
+                         EPHY_TYPE_BROWSER_ACTION,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/epiphany/gtk/browser-action-row.ui");
+  gtk_widget_class_bind_template_child (widget_class, EphyBrowserActionRow, browser_action_image);
+  gtk_widget_class_bind_template_child (widget_class, EphyBrowserActionRow, title_label);
+}
+
+static void
+ephy_browser_action_row_init (EphyBrowserActionRow *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+ephy_browser_action_row_new (EphyBrowserAction *browser_action)
+{
+  return g_object_new (EPHY_TYPE_BROWSER_ACTION_ROW,
+                       "browser-action", browser_action,
+                       NULL);
+}
+
+EphyBrowserAction *
+ephy_browser_action_row_get_browser_action (EphyBrowserActionRow *self)
+{
+  g_assert (EPHY_IS_BROWSER_ACTION_ROW (self));
+
+  return self->browser_action;
+}
diff --git a/src/webextension/ephy-browser-action-row.h b/src/webextension/ephy-browser-action-row.h
new file mode 100644
index 000000000..b309940be
--- /dev/null
+++ b/src/webextension/ephy-browser-action-row.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright 2022 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-browser-action.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_BROWSER_ACTION_ROW (ephy_browser_action_row_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyBrowserActionRow, ephy_browser_action_row, EPHY, BROWSER_ACTION_ROW, GtkListBoxRow)
+
+GtkWidget           *ephy_browser_action_row_new               (EphyBrowserAction *browser_action);
+
+EphyBrowserAction   *ephy_browser_action_row_get_browser_action (EphyBrowserActionRow *self);
+
+G_END_DECLS
diff --git a/src/webextension/ephy-browser-action.c b/src/webextension/ephy-browser-action.c
new file mode 100644
index 000000000..e55b3e670
--- /dev/null
+++ b/src/webextension/ephy-browser-action.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2022 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-browser-action.h"
+#include "ephy-web-extension-manager.h"
+
+struct _EphyBrowserAction {
+  GObject parent_instance;
+  EphyWebExtension *web_extension;
+};
+
+G_DEFINE_FINAL_TYPE (EphyBrowserAction, ephy_browser_action, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_WEB_EXTENSION,
+  N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+EphyBrowserAction *
+ephy_browser_action_new (EphyWebExtension *web_extension)
+{
+  return g_object_new (EPHY_TYPE_BROWSER_ACTION,
+                       "web-extension", web_extension,
+                       NULL);
+}
+
+const char *
+ephy_browser_action_get_title (EphyBrowserAction *self)
+{
+  const char *short_name = ephy_web_extension_get_short_name (self->web_extension);
+
+  return short_name && *short_name ? short_name : ephy_web_extension_get_name (self->web_extension);
+}
+
+GdkPixbuf *
+ephy_browser_action_get_pixbuf (EphyBrowserAction *self,
+                                gint64             size)
+{
+  return ephy_web_extension_get_icon (self->web_extension, size);
+}
+
+EphyWebExtension *
+ephy_browser_action_get_web_extension (EphyBrowserAction *self)
+{
+  return self->web_extension;
+}
+
+gboolean
+ephy_browser_action_activate (EphyBrowserAction *self)
+{
+  EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
+
+  /* If it has no popup clicking just emits this event and is handled already. */
+  if (ephy_web_extension_get_browser_popup (self->web_extension) == NULL) {
+    ephy_web_extension_manager_emit_in_background_view (manager, self->web_extension, 
"browserAction.onClicked", "");
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static void
+ephy_browser_action_finalize (GObject *object)
+{
+  EphyBrowserAction *self = (EphyBrowserAction *)object;
+
+  g_clear_object (&self->web_extension);
+
+  G_OBJECT_CLASS (ephy_browser_action_parent_class)->finalize (object);
+}
+
+static void
+ephy_browser_action_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  EphyBrowserAction *self = EPHY_BROWSER_ACTION (object);
+
+  switch (prop_id) {
+    case PROP_WEB_EXTENSION:
+      g_value_set_object (value, self->web_extension);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_browser_action_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  EphyBrowserAction *self = EPHY_BROWSER_ACTION (object);
+
+  switch (prop_id) {
+    case PROP_WEB_EXTENSION:
+      g_set_object (&self->web_extension, g_value_dup_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_browser_action_class_init (EphyBrowserActionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ephy_browser_action_finalize;
+  object_class->get_property = ephy_browser_action_get_property;
+  object_class->set_property = ephy_browser_action_set_property;
+
+  properties[PROP_WEB_EXTENSION] =
+    g_param_spec_object ("web-extension",
+                         "",
+                         "",
+                         EPHY_TYPE_WEB_EXTENSION,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ephy_browser_action_init (EphyBrowserAction *self)
+{
+}
diff --git a/src/webextension/ephy-browser-action.h b/src/webextension/ephy-browser-action.h
new file mode 100644
index 000000000..96d2fbd27
--- /dev/null
+++ b/src/webextension/ephy-browser-action.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2022 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+#include "ephy-web-extension.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_BROWSER_ACTION (ephy_browser_action_get_type())
+
+G_DECLARE_FINAL_TYPE (EphyBrowserAction, ephy_browser_action, EPHY, BROWSER_ACTION, GObject)
+
+EphyBrowserAction *ephy_browser_action_new        (EphyWebExtension *web_extension);
+const char        *ephy_browser_action_get_title  (EphyBrowserAction *self);
+GdkPixbuf         *ephy_browser_action_get_pixbuf (EphyBrowserAction *self, gint64 size);
+EphyWebExtension  *ephy_browser_action_get_web_extension (EphyBrowserAction *self);
+gboolean           ephy_browser_action_activate   (EphyBrowserAction *self);
+
+G_END_DECLS
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 2cb6b9340..2adaab326 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include "ephy-browser-action.h"
 #include "ephy-debug.h"
 #include "ephy-embed-shell.h"
 #include "ephy-embed-prefs.h"
@@ -58,7 +59,9 @@ struct _EphyWebExtensionManager {
   GCancellable *cancellable;
   GPtrArray *web_extensions;
   GHashTable *page_action_map;
+
   GHashTable *browser_action_map;
+  GListStore *browser_actions;
 
   GHashTable *user_agent_overrides;
 
@@ -87,6 +90,7 @@ EphyWebExtensionApiHandler api_handlers[] = {
 
 enum {
   CHANGED,
+  SHOW_BROWSER_ACTION,
   LAST_SIGNAL
 };
 
@@ -206,12 +210,6 @@ ephy_web_extension_manager_scan_directory_async (EphyWebExtensionManager *self,
                                    self);
 }
 
-static void
-destroy_widget_list (GSList *widget_list)
-{
-  g_slist_free_full (widget_list, (GDestroyNotify)gtk_widget_destroy);
-}
-
 static void
 ephy_webextension_scheme_cb (WebKitURISchemeRequest *request,
                              gpointer                user_data)
@@ -269,7 +267,8 @@ ephy_web_extension_manager_constructed (GObject *object)
   self->background_web_views = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
(GDestroyNotify)gtk_widget_destroy);
   self->popup_web_views = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
(GDestroyNotify)g_ptr_array_free);
   self->page_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_hash_table_destroy);
-  self->browser_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)destroy_widget_list);
+  self->browser_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+  self->browser_actions = g_list_store_new (EPHY_TYPE_BROWSER_ACTION);
   self->pending_messages = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
(GDestroyNotify)g_hash_table_destroy);
   self->web_extensions = g_ptr_array_new_full (0, g_object_unref);
   self->user_agent_overrides = create_user_agent_overrides ();
@@ -284,8 +283,12 @@ ephy_web_extension_manager_dispose (GObject *object)
 
   ephy_web_extension_api_downloads_dispose (self);
 
+  g_list_store_remove_all (self->browser_actions);
+
   g_clear_pointer (&self->background_web_views, g_hash_table_destroy);
   g_clear_pointer (&self->popup_web_views, g_hash_table_destroy);
+  g_clear_object (&self->browser_actions);
+  g_clear_pointer (&self->browser_action_map, g_hash_table_destroy);
   g_clear_pointer (&self->page_action_map, g_hash_table_destroy);
   g_clear_pointer (&self->pending_messages, g_hash_table_destroy);
   g_clear_pointer (&self->web_extensions, g_ptr_array_unref);
@@ -306,6 +309,14 @@ ephy_web_extension_manager_class_init (EphyWebExtensionManagerClass *klass)
                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                   0, NULL, NULL, NULL,
                   G_TYPE_NONE, 0);
+
+  signals[SHOW_BROWSER_ACTION] =
+    g_signal_new ("show-browser-action",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_FIRST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1, EPHY_TYPE_BROWSER_ACTION);
 }
 
 static void
@@ -1066,17 +1077,20 @@ ephy_web_extension_manager_register_popup_view (EphyWebExtensionManager *manager
   g_signal_connect (web_view, "destroy", G_CALLBACK (on_popup_view_destroyed), web_extension);
 }
 
-static GtkWidget *
-create_browser_popup (EphyWebExtension *web_extension)
+GtkWidget *
+ephy_web_extension_manager_create_browser_popup (EphyWebExtensionManager *self,
+                                                 EphyWebExtension        *web_extension)
 {
-  EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
   GtkWidget *web_view;
   g_autofree char *popup_uri = NULL;
   const char *popup;
 
   web_view = ephy_web_extensions_manager_create_web_extensions_webview (web_extension);
+  gtk_widget_set_hexpand (web_view, TRUE);
+  gtk_widget_set_vexpand (web_view, TRUE);
   gtk_widget_hide (web_view); /* Shown in on_popup_load_changed. */
-  ephy_web_extension_manager_register_popup_view (manager, web_extension, web_view);
+
+  ephy_web_extension_manager_register_popup_view (self, web_extension, web_view);
 
   popup = ephy_web_extension_get_browser_popup (web_extension);
   popup_uri = g_strdup_printf ("ephy-webextension://%s/%s", ephy_web_extension_get_guid (web_extension), 
popup);
@@ -1086,75 +1100,6 @@ create_browser_popup (EphyWebExtension *web_extension)
   return web_view;
 }
 
-static gboolean
-on_browser_action_clicked (GtkWidget *event_box,
-                           gpointer   user_data)
-{
-  EphyWebExtension *web_extension = EPHY_WEB_EXTENSION (user_data);
-  EphyWebExtensionManager *self = ephy_web_extension_manager_get_default ();
-  g_autofree char *script = NULL;
-  WebKitWebView *web_view = ephy_web_extension_manager_get_background_web_view (self, web_extension);
-
-  script = g_strdup_printf ("window.browser.browserAction.onClicked._emit();");
-
-  webkit_web_view_run_javascript (web_view,
-                                  script,
-                                  NULL,
-                                  NULL,
-                                  NULL);
-
-  return GDK_EVENT_STOP;
-}
-
-static void
-on_browser_action_visible_changed (GtkWidget  *popover,
-                                   GParamSpec *pspec,
-                                   gpointer    user_data)
-{
-  EphyWebExtension *web_extension = EPHY_WEB_EXTENSION (user_data);
-  GtkWidget *child;
-
-  if (gtk_widget_get_visible (popover)) {
-    child = create_browser_popup (web_extension);
-    gtk_container_add (GTK_CONTAINER (popover), child);
-  } else {
-    child = gtk_bin_get_child (GTK_BIN (popover));
-    gtk_container_remove (GTK_CONTAINER (popover), child);
-  }
-}
-
-GtkWidget *
-create_browser_action (EphyWebExtension *web_extension,
-                       EphyWindow       *window)
-{
-  GtkWidget *button;
-  GtkWidget *image;
-  GtkWidget *popover;
-  GdkPixbuf *pixbuf;
-
-  pixbuf = ephy_web_extension_browser_action_get_icon (web_extension, 16);
-  if (pixbuf)
-    image = gtk_image_new_from_pixbuf (pixbuf);
-  else
-    image = gtk_image_new_from_icon_name ("application-x-addon-symbolic", GTK_ICON_SIZE_BUTTON);
-
-  if (ephy_web_extension_get_browser_popup (web_extension)) {
-    button = gtk_menu_button_new ();
-    popover = gtk_popover_new (NULL);
-    g_signal_connect (popover, "notify::visible", G_CALLBACK (on_browser_action_visible_changed), 
web_extension);
-    gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover);
-    gtk_button_set_image (GTK_BUTTON (button), image);
-  } else {
-    button = gtk_button_new ();
-    g_signal_connect_object (button, "clicked", G_CALLBACK (on_browser_action_clicked), web_extension, 0);
-    gtk_button_set_image (GTK_BUTTON (button), image);
-  }
-
-  gtk_widget_set_visible (button, TRUE);
-
-  return button;
-}
-
 void
 ephy_web_extension_manager_add_web_extension_to_window (EphyWebExtensionManager *self,
                                                         EphyWebExtension        *web_extension,
@@ -1174,16 +1119,6 @@ ephy_web_extension_manager_add_web_extension_to_window (EphyWebExtensionManager
     ephy_web_extension_manager_add_web_extension_to_webview (self, web_extension, window, web_view);
   }
 
-  if (ephy_web_extension_has_browser_action (web_extension)) {
-    GtkWidget *browser_action_widget = create_browser_action (web_extension, window);
-    GSList *widget_list = g_hash_table_lookup (self->browser_action_map, web_extension);
-
-    ephy_header_bar_add_browser_action (EPHY_HEADER_BAR (ephy_window_get_header_bar (window)), 
browser_action_widget);
-
-    g_hash_table_steal (self->browser_action_map, web_extension); /* Avoid freeing list. */
-    g_hash_table_insert (self->browser_action_map, web_extension, g_slist_append (widget_list, 
browser_action_widget));
-  }
-
   ephy_web_extension_manager_update_location_entry (self, window);
   g_signal_connect_object (view, "page-attached", G_CALLBACK (page_attached_cb), web_extension, 0);
 }
@@ -1356,6 +1291,23 @@ application_window_removed_cb (EphyShell        *shell,
   ephy_web_extension_manager_emit_in_extension_views (manager, web_extension, "windows.onRemoved", 
window_json);
 }
 
+static void
+remove_browser_action (EphyWebExtensionManager *self,
+                       EphyWebExtension        *web_extension)
+{
+  EphyBrowserAction *action;
+  guint position;
+
+  action = g_hash_table_lookup (self->browser_action_map, web_extension);
+  if (!action)
+    return;
+
+  g_assert (g_list_store_find (self->browser_actions, action, &position));
+  g_list_store_remove (self->browser_actions, position);
+
+  g_hash_table_remove (self->browser_action_map, web_extension);
+}
+
 void
 ephy_web_extension_manager_set_active (EphyWebExtensionManager *self,
                                        EphyWebExtension        *web_extension,
@@ -1401,42 +1353,33 @@ ephy_web_extension_manager_set_active (EphyWebExtensionManager *self,
     if (ephy_web_extension_has_background_web_view (web_extension))
       run_background_script (self, web_extension);
 
+    if (ephy_web_extension_has_browser_action (web_extension)) {
+      EphyBrowserAction *action = ephy_browser_action_new (web_extension);
+      g_list_store_append (self->browser_actions, action);
+      g_hash_table_insert (self->browser_action_map, web_extension, g_steal_pointer (&action));
+    }
+
     ephy_web_extension_api_commands_init (web_extension);
   } else {
     g_signal_handlers_disconnect_by_data (shell, web_extension);
 
-    g_hash_table_remove (self->browser_action_map, web_extension);
+    remove_browser_action (self, web_extension);
     g_hash_table_remove (self->background_web_views, web_extension);
     g_object_set_data (G_OBJECT (web_extension), "alarms", NULL); /* Set in alarms.c */
     ephy_web_extension_api_commands_dispose (web_extension);
   }
 }
 
-gint
-get_browser_action_for_window (GtkWidget *widget,
-                               GtkWidget *window)
-{
-  return gtk_widget_get_toplevel (widget) != window;
-}
-
-
 void
-ephy_web_extension_manager_activate_browser_action (EphyWebExtensionManager *self,
-                                                    EphyWebExtension        *web_extension,
-                                                    EphyWindow              *window)
+ephy_web_extension_manager_show_browser_action (EphyWebExtensionManager *self,
+                                                EphyWebExtension        *web_extension)
 {
-  GSList *table, *l;
-  GtkWidget *button;
+  EphyBrowserAction *action = EPHY_BROWSER_ACTION (g_hash_table_lookup (self->browser_action_map, 
web_extension));
 
-  table = g_hash_table_lookup (self->browser_action_map, web_extension);
-  if (table) {
-    l = g_slist_find_custom (table, window, (GCompareFunc)get_browser_action_for_window);
-
-    g_assert (l && l->data);
+  if (!action || ephy_browser_action_activate (action))
+    return;
 
-    button = l->data;
-    gtk_widget_mnemonic_activate (button, FALSE);
-  }
+  g_signal_emit (self, signals[SHOW_BROWSER_ACTION], 0, action);
 }
 
 GtkWidget *
@@ -1454,6 +1397,12 @@ ephy_web_extension_manager_get_page_action (EphyWebExtensionManager *self,
   return ret;
 }
 
+GListStore *
+ephy_web_extension_manager_get_browser_actions (EphyWebExtensionManager *self)
+{
+  return self->browser_actions;
+}
+
 void
 ephy_web_extension_manager_handle_notifications_action (EphyWebExtensionManager *self,
                                                         GVariant                *params)
@@ -1653,6 +1602,24 @@ on_extension_emit_ready (GObject      *source,
     g_warning ("Emitting in view errored: %s", error->message);
 }
 
+void
+ephy_web_extension_manager_emit_in_background_view (EphyWebExtensionManager *self,
+                                                    EphyWebExtension        *web_extension,
+                                                    const char              *name,
+                                                    const char              *json)
+{
+  g_autofree char *script = NULL;
+  WebKitWebView *web_view = ephy_web_extension_manager_get_background_web_view (self, web_extension);
+
+  script = g_strdup_printf ("window.browser.%s._emit(%s);", name, json);
+
+  webkit_web_view_run_javascript (web_view,
+                                  script,
+                                  NULL,
+                                  NULL,
+                                  NULL);
+}
+
 static void
 ephy_web_extension_manager_emit_in_extension_views_internal (EphyWebExtensionManager *self,
                                                              EphyWebExtension        *web_extension,
diff --git a/src/webextension/ephy-web-extension-manager.h b/src/webextension/ephy-web-extension-manager.h
index 13f68b22d..89901bd75 100644
--- a/src/webextension/ephy-web-extension-manager.h
+++ b/src/webextension/ephy-web-extension-manager.h
@@ -64,17 +64,19 @@ void                    ephy_web_extension_manager_set_active
                                                                                      EphyWebExtension        
*web_extension,
                                                                                      gboolean                
 active);
 
-void                    ephy_web_extension_manager_activate_browser_action          (EphyWebExtensionManager 
*self,
-                                                                                     EphyWebExtension        
*web_extension,
-                                                                                     EphyWindow              
*window);
+void                    ephy_web_extension_manager_show_browser_action              (EphyWebExtensionManager 
*self,
+                                                                                     EphyWebExtension        
*web_extension);
 
 GtkWidget               *ephy_web_extension_manager_get_page_action                 (EphyWebExtensionManager 
*self,
                                                                                      EphyWebExtension        
*web_extension,
                                                                                      EphyWebView             
*web_view);
 
+GListStore              *ephy_web_extension_manager_get_browser_actions             (EphyWebExtensionManager 
*self);
+
 WebKitWebView           *ephy_web_extension_manager_get_background_web_view         (EphyWebExtensionManager 
*self,
                                                                                      EphyWebExtension        
*web_extension);
-
+GtkWidget               *ephy_web_extension_manager_create_browser_popup           (EphyWebExtensionManager 
*self,
+                                                                                    EphyWebExtension        
*web_extension);
 void                     ephy_web_extension_manager_handle_notifications_action     (EphyWebExtensionManager 
*self,
                                                                                      GVariant                
*params);
 
@@ -101,6 +103,10 @@ void                     ephy_web_extension_manager_emit_in_tab_with_reply
                                                                                      WebKitWebView           
*target_web_view,
                                                                                      const char              
*sender_json,
                                                                                      GTask                   
*reply_task);
+void                     ephy_web_extension_manager_emit_in_background_view         (EphyWebExtensionManager 
*self,
+                                                                                     EphyWebExtension        
*web_extension,
+                                                                                     const char              
*name,
+                                                                                     const char              
*json);
 
 GtkWidget                *ephy_web_extensions_manager_create_web_extensions_webview (EphyWebExtension        
*web_extension);
 
diff --git a/src/webextension/meson.build b/src/webextension/meson.build
index c4ad72c50..32a76c5af 100644
--- a/src/webextension/meson.build
+++ b/src/webextension/meson.build
@@ -10,6 +10,8 @@ ephywebextension_src = [
   'webextension/api/storage.c',
   'webextension/api/tabs.c',
   'webextension/api/windows.c',
+  'webextension/ephy-browser-action.c',
+  'webextension/ephy-browser-action-row.c',
   'webextension/ephy-json-utils.c',
   'webextension/ephy-web-extension-manager.c',
   'webextension/ephy-web-extension.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]