diff options
author | Christian Dywan <christian@twotoasts.de> | 2018-10-31 11:23:18 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-31 11:23:18 +0100 |
commit | ff9ef19b39e746acc6931482f63eb4175ff2be25 (patch) | |
tree | ba5346ec3a206fe8c8c8cd33467bba4a8c0718b3 | |
parent | b706998fb38a91e6f1327f46ab62d5d2e36c9b6b (diff) | |
download | midori-git-ff9ef19b39e746acc6931482f63eb4175ff2be25.tar.gz |
Implement built-in session extension (#65)
- The database schema is compatible with the original tabby.
- Every browser is a "session".
- Each tab is part of a session and can be closed.
- The last closed session (window) is restored as a fallback.
- `Midori.Database.insert/ update/ delete` become virtual.
- `Midori.Browser.tabs` is made public.
- `Midori.Browser.add` ensures there's at least one visible tab.
- The new `ClearPrivateDataActivatable` allows adding buttons and clearing data used by an extension, in this case previously open tabs.
Follow-ups:
- See #70 for closed storing tabs in the session
- See #71 for historing tab history
- See #142 for re-rodering of tabs
Closes: #41
-rw-r--r-- | core/app.vala | 14 | ||||
-rw-r--r-- | core/browser.vala | 26 | ||||
-rw-r--r-- | core/clear-private-data.vala | 32 | ||||
-rw-r--r-- | core/database.vala | 11 | ||||
-rw-r--r-- | core/settings.vala | 23 | ||||
-rw-r--r-- | data/tabby/Create.sql | 33 | ||||
-rw-r--r-- | data/tabby/Update1.sql | 4 | ||||
-rw-r--r-- | extensions/session.plugin.in | 5 | ||||
-rw-r--r-- | extensions/session.vala | 406 | ||||
-rw-r--r-- | gresource.xml | 2 | ||||
-rw-r--r-- | po/POTFILES.in | 2 |
11 files changed, 540 insertions, 18 deletions
diff --git a/core/app.vala b/core/app.vala index 561b4ad8..404675a9 100644 --- a/core/app.vala +++ b/core/app.vala @@ -313,14 +313,21 @@ namespace Midori { void win_new_activated (Action action, Variant? parameter) { var browser = new Browser (this); + if (!browser.default_tab ()) { + browser.add (new Tab (null, browser.web_context)); + } string? uri = parameter.get_string () != "" ? parameter.get_string () : null; - browser.add (new Tab (null, browser.web_context, uri)); + if (uri != null) { + browser.add (new Tab (null, browser.web_context, uri)); + } browser.show (); } void win_incognito_new_activated () { var browser = new Browser.incognito (this); - browser.add (new Tab (null, browser.web_context)); + if (!browser.default_tab ()) { + browser.add (new Tab (null, browser.web_context)); + } browser.show (); } @@ -390,8 +397,7 @@ namespace Midori { } if (app != "") { - var browser = new Browser (this); - browser.is_locked = true; + var browser = new Browser (this, true); var tab = new Tab (null, browser.web_context, app); tab.pinned = true; browser.add (tab); diff --git a/core/browser.vala b/core/browser.vala index 3fc9b4a8..f365fa40 100644 --- a/core/browser.vala +++ b/core/browser.vala @@ -24,7 +24,7 @@ namespace Midori { public Tab? tab { get; protected set; } public ListStore trash { get; protected set; } public bool is_fullscreen { get; protected set; default = false; } - public bool is_locked { get; set; default = false; } + public bool is_locked { get; construct set; default = false; } const ActionEntry[] actions = { { "tab-new", tab_new_activated }, @@ -69,7 +69,7 @@ namespace Midori { [GtkChild] Navigationbar navigationbar; [GtkChild] - Gtk.Stack tabs; + public Gtk.Stack tabs; [GtkChild] public Gtk.Overlay overlay; [GtkChild] @@ -285,8 +285,16 @@ namespace Midori { navigationbar.pack_end (button); } - public Browser (App app) { + /* + * Requests a default tab to be added to an otherwise empty window. + * + * Connect, adding one or more windows, and return true to override. + */ + public signal bool default_tab (); + + public Browser (App app, bool is_locked=false) { Object (application: app, + is_locked: is_locked, web_context: WebKit.WebContext.get_default ()); } @@ -370,14 +378,20 @@ namespace Midori { void homepage_activated () { var settings = CoreSettings.get_default (); string homepage = settings.homepage; + string uri; if ("://" in homepage) { - tab.load_uri (homepage); + uri = homepage; } else if ("." in homepage) { // Prepend http:// if hompepage has no scheme - tab.load_uri ("http://" + homepage); + uri = "http://" + homepage; } else { // Fallback to search if URI is about:search or anything else - tab.load_uri (settings.uri_for_search ()); + uri = settings.uri_for_search (); + } + if (tab == null) { + add (new Tab (null, web_context, uri)); + } else { + tab.load_uri (uri); } } diff --git a/core/clear-private-data.vala b/core/clear-private-data.vala index b7d36ef0..96f222d0 100644 --- a/core/clear-private-data.vala +++ b/core/clear-private-data.vala @@ -10,6 +10,12 @@ */ namespace Midori { + public interface ClearPrivateDataActivatable : Object { + public abstract Gtk.Box box { owned get; set; } + public abstract void activate (); + public async abstract void clear (TimeSpan timespan); + } + [GtkTemplate (ui = "/ui/clear-private-data.ui")] class ClearPrivateData : Gtk.Dialog { [GtkChild] @@ -22,6 +28,7 @@ namespace Midori { Gtk.CheckButton cache; Cancellable? show_cancellable = null; + Peas.ExtensionSet extensions; public ClearPrivateData (Gtk.Window parent) { Object (transient_for: parent); @@ -51,6 +58,11 @@ namespace Midori { } catch (DatabaseError error) { debug ("Failed to check history: %s", error.message); } + + extensions = Plugins.get_default ().plug<ClearPrivateDataActivatable> ("box", history.parent); + extensions.extension_added.connect ((info, extension) => { ((ClearPrivateDataActivatable)extension).activate (); }); + extensions.foreach ((extensions, info, extension) => { extensions.extension_added (info, extension); }); + base.show (); } @@ -79,6 +91,10 @@ namespace Midori { public override void response (int response_id) { show_cancellable.cancel (); + response_async.begin (response_id); + } + + async void response_async (int response_id) { if (response_id == Gtk.ResponseType.OK) { // The ID is the number of days as a string; 0 means everything var timespan = timerange.active_id.to_int () * TimeSpan.DAY; @@ -91,15 +107,25 @@ namespace Midori { } if (types != 0) { var manager = WebKit.WebContext.get_default ().website_data_manager; - manager.clear.begin (types, timespan, null); + try { + yield manager.clear (types, timespan, null); + } catch (Error error) { + critical ("Failed to clear website data: %s", error.message); + } } if (history.active) { try { - HistoryDatabase.get_default ().clear.begin (timespan); + yield HistoryDatabase.get_default ().clear (timespan); } catch (DatabaseError error) { - debug ("Failed to clear history: %s", error.message); + critical ("Failed to clear history: %s", error.message); } } + + var active_extensions = new List<Peas.Extension> (); + extensions.foreach ((extensions, info, extension) => { active_extensions.append (extension); }); + foreach (var extension in active_extensions) { + yield ((ClearPrivateDataActivatable)extension).clear (timespan); + } } close (); } diff --git a/core/database.vala b/core/database.vala index 4c215486..64abeee4 100644 --- a/core/database.vala +++ b/core/database.vala @@ -324,7 +324,8 @@ namespace Midori { } public bool exec_script (string filename) throws DatabaseError { - string schema_path = "/data/%s/%s.sql".printf (table, filename); + string basename = Path.get_basename (path).split (".")[0]; + string schema_path = "/data/%s/%s.sql".printf (basename, filename); try { var schema = resources_lookup_data (schema_path, ResourceLookupFlags.NONE); transaction (()=> { return exec ((string)schema.get_data ()); }); @@ -376,7 +377,7 @@ namespace Midori { /* * Delete an item from the database. */ - public async bool delete (DatabaseItem item) throws DatabaseError { + public async virtual bool delete (DatabaseItem item) throws DatabaseError { string sqlcmd = """ DELETE FROM %s WHERE rowid = :id """.printf (table); @@ -544,9 +545,9 @@ namespace Midori { // Note: TimeSpan is defined in microseconds int64 maximum_age = new DateTime.now_local ().to_unix () - timespan / 1000000; - unowned string sqlcmd = """ - DELETE FROM %s WHERE date <= :maximum_age; - """; + string sqlcmd = """ + DELETE FROM %s WHERE date <= :maximum_age + """.printf (table); var statement = prepare (sqlcmd, ":maximum_age", typeof (int64), maximum_age); return statement.exec (); diff --git a/core/settings.vala b/core/settings.vala index 1cb8896a..0d3f6ef0 100644 --- a/core/settings.vala +++ b/core/settings.vala @@ -10,6 +10,14 @@ */ namespace Midori { + [CCode (cprefix = "MIDORI_STARTUP_")] + public enum StartupType { + SPEED_DIAL, + HOMEPAGE, + LAST_OPEN_PAGES, + DELAYED_PAGES + } + public class CoreSettings : Settings { static CoreSettings? _default = null; @@ -34,6 +42,21 @@ namespace Midori { set_boolean ("extensions", "lib%s.so".printf (plugin), enabled); } + // Note: Speed Dial is saved as Blank Page for compatibility reasons + public StartupType load_on_startup { get { + var startup = get_string ("settings", "load-on-startup", "MIDORI_STARTUP_LAST_OPEN_PAGES"); + if (startup.has_suffix ("BLANK_PAGE")) { + return StartupType.SPEED_DIAL; + } else if (startup.has_suffix ("HOMEPAGE")) { + return StartupType.HOMEPAGE; + } else { + return StartupType.LAST_OPEN_PAGES; + } + } set { + var startup = value == StartupType.SPEED_DIAL ? "MIDORI_STARTUP_BLANK_PAGE" : value.to_string (); + set_string ("settings", "load-on-startup", startup, "MIDORI_STARTUP_LAST_OPEN_PAGES"); + } } + public bool enable_spell_checking { get { return get_boolean ("settings", "enable-spell-checking", true); } set { diff --git a/data/tabby/Create.sql b/data/tabby/Create.sql new file mode 100644 index 00000000..0b5690bd --- /dev/null +++ b/data/tabby/Create.sql @@ -0,0 +1,33 @@ +CREATE TABLE IF NOT EXISTS sessions +( + id INTEGER PRIMARY KEY, + parent_id INTEGER DEFAULT 0, + crdate INTEGER DEFAULT 0, + tstamp INTEGER DEFAULT 0, + closed INTEGER DEFAULT 0, + title TEXT DEFAULT NULL, + FOREIGN KEY(parent_id) REFERENCES sessions(id) +); + +CREATE TABLE IF NOT EXISTS tabs +( + id INTEGER PRIMARY KEY, + session_id INTEGER NOT NULL, + uri TEXT DEFAULT NULL, + icon TEXT DEFAULT NULL, + title TEXT DEFAULT NULL, + crdate INTEGER DEFAULT 0, + tstamp INTEGER DEFAULT 0, + closed INTEGER DEFAULT 0, + FOREIGN KEY(session_id) REFERENCES sessions(id) +); + +CREATE TABLE IF NOT EXISTS tab_history +( + id INTEGER PRIMARY KEY, + tab_id INTEGER, + url TEXT, + icon TEXT, + title TEXT, + FOREIGN KEY(tab_id) REFERENCES tabs(id) +); diff --git a/data/tabby/Update1.sql b/data/tabby/Update1.sql new file mode 100644 index 00000000..26b72103 --- /dev/null +++ b/data/tabby/Update1.sql @@ -0,0 +1,4 @@ +ALTER TABLE tabs ADD sorting REAL DEFAULT 0; + +CREATE INDEX sorting on tabs (sorting ASC); +CREATE INDEX tstamp on tabs (tstamp ASC); diff --git a/extensions/session.plugin.in b/extensions/session.plugin.in new file mode 100644 index 00000000..e2181ba6 --- /dev/null +++ b/extensions/session.plugin.in @@ -0,0 +1,5 @@ +[Plugin] +Module=session +IAge=3 +Builtin=true +Name=Session diff --git a/extensions/session.vala b/extensions/session.vala new file mode 100644 index 00000000..92bcee90 --- /dev/null +++ b/extensions/session.vala @@ -0,0 +1,406 @@ +/* + Copyright (C) 2013-2018 Christian Dywan <christian@twotoats.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See the file COPYING for the full license text. +*/ + +namespace Tabby { + class SessionDatabase : Midori.Database { + static SessionDatabase? _default = null; + // Note: Using string instead of int64 because it's a hashable type + HashTable<string, Midori.Browser> browsers; + + public static SessionDatabase get_default () throws Midori.DatabaseError { + if (_default == null) { + _default = new SessionDatabase (); + } + return _default; + } + + SessionDatabase () throws Midori.DatabaseError { + Object (path: "tabby.db", table: "tabs"); + init (); + browsers = new HashTable<string, Midori.Browser> (str_hash, str_equal); + } + + async List<int64?> get_sessions () throws Midori.DatabaseError { + string sqlcmd = """ + SELECT id, closed FROM sessions WHERE closed = 0 + UNION + SELECT * FROM (SELECT id, closed FROM sessions WHERE closed = 1 ORDER BY tstamp DESC LIMIT 1) + ORDER BY closed; + """; + var sessions = new List<int64?> (); + var statement = prepare (sqlcmd); + while (statement.step ()) { + int64 id = statement.get_int64 ("id"); + int64 closed = statement.get_int64 ("closed"); + if (closed == 0 || sessions.length () == 0) { + sessions.append (id); + } + } + return sessions; + } + + async List<Midori.DatabaseItem>? get_items (int64 session_id, string? filter=null, int64 max_items=15, Cancellable? cancellable=null) throws Midori.DatabaseError { + string where = filter != null ? "AND (uri LIKE :filter OR title LIKE :filter)" : ""; + string sqlcmd = """ + SELECT id, uri, title, tstamp FROM %s + WHERE session_id = :session_id %s + ORDER BY tstamp DESC LIMIT :limit + """.printf (table, where); + var statement = prepare (sqlcmd, + ":session_id", typeof (int64), session_id, + ":limit", typeof (int64), max_items); + if (filter != null) { + string real_filter = "%" + filter.replace (" ", "%") + "%"; + statement.bind (":filter", typeof (string), real_filter); + } + + var items = new List<Midori.DatabaseItem> (); + while (statement.step ()) { + string uri = statement.get_string ("uri"); + string title = statement.get_string ("title"); + int64 date = statement.get_int64 ("tstamp"); + var item = new Midori.DatabaseItem (uri, title, date); + item.database = this; + item.id = statement.get_int64 ("id"); + item.set_data<int64> ("session_id", session_id); + items.append (item); + + uint src = Idle.add (get_items.callback); + yield; + Source.remove (src); + + if (cancellable != null && cancellable.is_cancelled ()) + return null; + } + + if (cancellable != null && cancellable.is_cancelled ()) + return null; + return items; + } + + public async override List<Midori.DatabaseItem>? query (string? filter=null, int64 max_items=15, Cancellable? cancellable=null) throws Midori.DatabaseError { + var items = new List<Midori.DatabaseItem> (); + foreach (int64 session_id in yield get_sessions ()) { + foreach (var item in yield get_items (session_id, filter, max_items, cancellable)) { + items.append (item); + } + } + + if (cancellable != null && cancellable.is_cancelled ()) + return null; + return items; + } + + public async override bool insert (Midori.DatabaseItem item) throws Midori.DatabaseError { + item.database = this; + + string sqlcmd = """ + INSERT INTO %s (crdate, tstamp, session_id, uri, title) + VALUES (:crdate, :tstamp, :session_id, :uri, :title) + """.printf (table); + + var statement = prepare (sqlcmd, + ":crdate", typeof (int64), item.date, + ":tstamp", typeof (int64), item.date, + ":session_id", typeof (int64), item.get_data<int64> ("session_id"), + ":uri", typeof (string), item.uri, + ":title", typeof (string), item.title); + if (statement.exec ()) { + item.id = statement.row_id (); + return true; + } + return false; + } + + public async override bool update (Midori.DatabaseItem item) throws Midori.DatabaseError { + string sqlcmd = """ + UPDATE %s SET uri = :uri, title = :title, tstamp = :tstamp WHERE id = :id + """.printf (table); + try { + var statement = prepare (sqlcmd, + ":id", typeof (int64), item.id, + ":uri", typeof (string), item.uri, + ":title", typeof (string), item.title, + ":tstamp", typeof (int64), new DateTime.now_local ().to_unix ()); + if (statement.exec ()) { + return true; + } + } catch (Midori.DatabaseError error) { + critical ("Failed to update %s: %s", table, error.message); + } + return false; + } + + public async override bool delete (Midori.DatabaseItem item) throws Midori.DatabaseError { + string sqlcmd = """ + DELETE FROM %s WHERE id = :id + """.printf (table); + var statement = prepare (sqlcmd, + ":id", typeof (int64), item.id); + if (statement.exec ()) { + return true; + } + return false; + } + + int64 insert_session () { + string sqlcmd = """ + INSERT INTO sessions (tstamp) VALUES (:tstamp) + """; + try { + var statement = prepare (sqlcmd, + ":tstamp", typeof (int64), new DateTime.now_local ().to_unix ()); + statement.exec (); + debug ("Added session: %s", statement.row_id ().to_string ()); + return statement.row_id (); + } catch (Midori.DatabaseError error) { + critical ("Failed to add session: %s", error.message); + } + return -1; + } + + void update_session (int64 id, bool closed) { + string sqlcmd = """ + UPDATE sessions SET closed=:closed, tstamp=:tstamp WHERE id = :id + """; + try { + var statement = prepare (sqlcmd, + ":id", typeof (int64), id, + ":tstamp", typeof (int64), new DateTime.now_local ().to_unix (), + ":closed", typeof (int64), closed ? 1 : 0); + statement.exec (); + } catch (Midori.DatabaseError error) { + critical ("Failed to update session: %s", error.message); + } + } + + public async override bool clear (TimeSpan timespan) throws Midori.DatabaseError { + // Note: TimeSpan is defined in microseconds + int64 maximum_age = new DateTime.now_local ().to_unix () - timespan / 1000000; + + string sqlcmd = """ + DELETE FROM %s WHERE tstamp >= :maximum_age; + DELETE FROM sessions WHERE tstamp >= :maximum_age; + """.printf (table); + var statement = prepare (sqlcmd, + ":maximum_age", typeof (int64), maximum_age); + return statement.exec (); + } + + public async bool restore_windows (Midori.Browser default_browser) throws Midori.DatabaseError { + bool restored = false; + + // Restore existing session(s) that weren't closed, or the last closed one + foreach (var item in yield query (null, int64.MAX - 1)) { + Midori.Browser browser; + int64 id = item.get_data<int64> ("session_id"); + if (!restored) { + browser = default_browser; + restored = true; + connect_browser (browser, id); + foreach (var widget in browser.tabs.get_children ()) { + yield tab_added (widget as Midori.Tab, id); + } + } else { + var app = (Midori.App)default_browser.get_application (); + browser = browser_for_session (app, id); + } + var tab = new Midori.Tab (browser.tab, browser.web_context, + item.uri, item.title); + connect_tab (tab, item); + browser.add (tab); + } + return restored; + } + + Midori.Browser browser_for_session (Midori.App app, int64 id) { + var browser = browsers.lookup (id.to_string ()); + if (browser == null) { + debug ("Restoring session %s", id.to_string ()); + browser = new Midori.Browser (app); + browser.show (); + connect_browser (browser, id); + } + return browser; + } + + public void connect_browser (Midori.Browser browser, int64 id=-1) { + if (id < 0) { + id = insert_session (); + } else { + update_session (id, false); + } + + browsers.insert (id.to_string (), browser); + browser.set_data<bool> ("tabby_connected", true); + foreach (var widget in browser.tabs.get_children ()) { + tab_added.begin (widget as Midori.Tab, id); + } + browser.tabs.add.connect ((widget) => { tab_added.begin (widget as Midori.Tab, id); }); + browser.delete_event.connect ((event) => { + debug ("Closing session %s", id.to_string ()); + update_session (id, true); + return false; + }); + } + + void connect_tab (Midori.Tab tab, Midori.DatabaseItem item) { + debug ("Connecting %s to session %s", item.uri, item.get_data<int64> ("session_id").to_string ()); + tab.set_data<Midori.DatabaseItem?> ("tabby-item", item); + tab.notify["uri"].connect ((pspec) => { item.uri = tab.uri; update.begin (item); }); + tab.notify["title"].connect ((pspec) => { item.title = tab.title; }); + tab.close.connect (() => { tab_removed (tab); }); + } + + bool tab_is_connected (Midori.Tab tab) { + return tab.get_data<Midori.DatabaseItem?> ("tabby-item") != null; + } + + async void tab_added (Midori.Tab tab, int64 id) { + if (tab_is_connected (tab)) { + return; + } + var item = new Midori.DatabaseItem (tab.display_uri, tab.display_title, + new DateTime.now_local ().to_unix ()); + item.set_data<int64> ("session_id", id); + try { + yield insert (item); + connect_tab (tab, item); + } catch (Midori.DatabaseError error) { + critical ("Failed add tab to session database: %s", error.message); + } + } + + void tab_removed (Midori.Tab tab) { + var item = tab.get_data<Midori.DatabaseItem?> ("tabby-item"); + debug ("Trashing tab %s:%s", item.get_data<int64> ("session_id").to_string (), tab.display_uri); + item.delete.begin (); + } + } + + public class Session : Peas.ExtensionBase, Midori.BrowserActivatable { + public Midori.Browser browser { owned get; set; } + + static bool session_restored = false; + + public void activate () { + // Don't track locked (app) or private windows + if (browser.is_locked || browser.web_context.is_ephemeral ()) { + return; + } + // Skip windows already in the session + if (browser.get_data<bool> ("tabby_connected")) { + return; + } + + browser.default_tab.connect (restore_or_connect); + try { + var session = SessionDatabase.get_default (); + if (session_restored) { + session.connect_browser (browser); + browser.activate_action ("tab-new", null); + } else { + session_restored = true; + restore_session.begin (session); + } + } catch (Midori.DatabaseError error) { + critical ("Failed to restore session: %s", error.message); + } + } + + bool restore_or_connect () { + try { + var session = SessionDatabase.get_default (); + var settings = Midori.CoreSettings.get_default (); + if (settings.load_on_startup == Midori.StartupType.SPEED_DIAL) { + session.connect_browser (browser); + } else if (settings.load_on_startup == Midori.StartupType.HOMEPAGE) { + session.connect_browser (browser); + browser.activate_action ("homepage", null); + return true; + } else { + return true; + } + } catch (Midori.DatabaseError error) { + critical ("Failed to restore session: %s", error.message); + } + return false; + } + + async void restore_session (SessionDatabase session) { + try { + bool restored = yield session.restore_windows (browser); + if (!restored) { + browser.add (new Midori.Tab (null, browser.web_context)); + session.connect_browser (browser); + } + } catch (Midori.DatabaseError error) { + critical ("Failed to restore session: %s", error.message); + } + } + } + + public class Preferences : Object, Midori.PreferencesActivatable { + public Midori.Preferences preferences { owned get; set; } + + public void activate () { + var settings = Midori.CoreSettings.get_default (); + var box = new Midori.LabelWidget (_("Startup")); + var combo = new Gtk.ComboBoxText (); + combo.append ("0", _("Show Speed Dial")); + combo.append ("1", _("Show Homepage")); + combo.append ("2", _("Show last open tabs")); + settings.bind_property ("load-on-startup", combo, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); + var button = new Midori.LabelWidget (_("When Midori starts:"), combo); + box.add (button); + box.show_all (); + preferences.add (_("Browsing"), box); + deactivate.connect (() => { + box.destroy (); + }); + } + } + + public class ClearSession : Peas.ExtensionBase, Midori.ClearPrivateDataActivatable { + public Gtk.Box box { owned get; set; } + + Gtk.CheckButton button; + + public void activate () { + button = new Gtk.CheckButton.with_mnemonic (_("Last open _tabs")); + button.show (); + box.add (button); + } + + public async void clear (TimeSpan timespan) { + if (!button.active) { + return; + } + + try { + yield SessionDatabase.get_default ().clear (timespan); + } catch (Midori.DatabaseError error) { + critical ("Failed to clear session: %s", error.message); + } + } + } +} + +[ModuleInit] +public void peas_register_types(TypeModule module) { + ((Peas.ObjectModule)module).register_extension_type ( + typeof (Midori.BrowserActivatable), typeof (Tabby.Session)); + ((Peas.ObjectModule)module).register_extension_type ( + typeof (Midori.PreferencesActivatable), typeof (Tabby.Preferences)); + ((Peas.ObjectModule)module).register_extension_type ( + typeof (Midori.ClearPrivateDataActivatable), typeof (Tabby.ClearSession)); + +} diff --git a/gresource.xml b/gresource.xml index ef7361cb..92418a7c 100644 --- a/gresource.xml +++ b/gresource.xml @@ -19,6 +19,8 @@ <file compressed="true">data/bookmarks/Create.sql</file> <file compressed="true">data/history/Create.sql</file> <file compressed="true">data/history/Day.sql</file> + <file compressed="true">data/tabby/Create.sql</file> + <file compressed="true">data/tabby/Update1.sql</file> <file compressed="true" preprocess="xml-stripblanks">ui/about.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/bookmarks-button.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/browser.ui</file> diff --git a/po/POTFILES.in b/po/POTFILES.in index 1f2ba990..d31844ed 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -41,6 +41,8 @@ extensions/statusbar-features.in extensions/statusbar-features.vala extensions/colorful-tabs.plugin.in extensions/colorful-tabs.vala +extensions/session.plugin.in +extensions/session.vala ui/bookmarks-button.ui ui/browser.ui ui/clear-private-data.ui |