summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMario Sanchez Prada <mario@endlessm.com>2016-04-20 19:03:28 +0100
committerMario Sanchez Prada <mario@endlessm.com>2016-04-21 21:56:53 +0100
commit67582310b542a64583c590c43532e53a5b710d6e (patch)
tree5b9d4f87ca1ab7418afeb06ab6ee41e1fe6e7ba9
parent065e06c6110b191d5a13aa3d8598fc9feac03a46 (diff)
Remove parallel version of WebHelper compatible with WebKit2-3.0
We are no longer shipping the Webkit2-3.0 API on Endless and Virtual School has already been migrated to the newer WebKit2-4.0 API after having fixed all the regressions there and so it makes no sense to keep this any longer. Besides, after the WebKitGTK+ upgrade from 2.4.9 to 2.4.11 debian does no longer ship the WebKit2GTK+-3.0 packages (and there's no point on us bringing them back) so, actually, we can not keep this anyway, so let's remove it. This commit basically reverts 8c837e660853abd03c97a98815f73146eb57c010, but considering all the changes integrated ever since (not a "clean" revert). https://phabricator.endlessm.com/T11388
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am35
-rw-r--r--configure.ac9
-rw-r--r--jasmine.json4
-rw-r--r--test/Makefile.am.inc2
-rw-r--r--test/webhelper/testTranslate2Old.js213
-rw-r--r--test/webhelper/testWebActions2Old.js120
-rw-r--r--webhelper/webhelper2.js532
-rw-r--r--webhelper/webhelper2_old.js11
-rw-r--r--webhelper/webhelper_private/common.js530
10 files changed, 526 insertions, 932 deletions
diff --git a/.gitignore b/.gitignore
index 88c0183..274e0ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,8 +8,6 @@ Endless-0.gir
Endless-0.typelib
WebHelper2Private-1.0.gir
WebHelper2Private-1.0.typelib
-WebHelper2OldPrivate-1.0.gir
-WebHelper2OldPrivate-1.0.typelib
endless/eosresource.c
endless/eosresource-private.h
endless/eosversion.h
diff --git a/Makefile.am b/Makefile.am
index 8638a1f..52f610d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -91,7 +91,6 @@ DISTCLEANFILES += @EOS_SDK_API_NAME@.pc
webhelper_sources = \
webhelper/webhelper.js \
webhelper/webhelper2.js \
- webhelper/webhelper2_old.js \
$(NULL)
gjsmodulesdir = $(datadir)/gjs-1.0
@@ -101,7 +100,6 @@ dist_webhelper_DATA = \
$(webhelper_sources) \
$(NULL)
dist_webhelper_private_DATA = \
- webhelper/webhelper_private/common.js \
webhelper/webhelper_private/config.js \
$(NULL)
@@ -130,28 +128,6 @@ wh2extension_la_CPPFLAGS = @WEBHELPER2_EXTENSION_CFLAGS@
wh2extension_la_LIBADD = @WEBHELPER2_EXTENSION_LIBS@
wh2extension_la_LDFLAGS = -module -avoid-version -no-undefined
-# WebKit2-3.0 version
-
-lib_LTLIBRARIES += libwebhelper2oldprivate.la
-libwebhelper2oldprivate_la_SOURCES = \
- webhelper/lib/wh2private.c \
- webhelper/lib/wh2private.h \
- $(NULL)
-libwebhelper2oldprivate_la_CPPFLAGS = @WEBHELPER2_OLD_PRIVATE_CFLAGS@
-libwebhelper2oldprivate_la_LIBADD = @WEBHELPER2_OLD_PRIVATE_LIBS@
-libwebhelper2oldprivate_la_LDFLAGS = -avoid-version
-
-webhelper2oldextensionsdir = $(libexecdir)/webhelper2old
-webhelper2oldextensions_LTLIBRARIES = wh2oldextension.la
-wh2oldextension_la_SOURCES = \
- webhelper/webextensions/wh2extension.c \
- webhelper/webextensions/wh2jscutil.c \
- webhelper/webextensions/wh2jscutil.h \
- $(NULL)
-wh2oldextension_la_CPPFLAGS = @WEBHELPER2_OLD_EXTENSION_CFLAGS@
-wh2oldextension_la_LIBADD = @WEBHELPER2_OLD_EXTENSION_LIBS@
-wh2oldextension_la_LDFLAGS = -module -avoid-version -no-undefined
-
# # # INTROSPECTION FILES # # #
-include $(INTROSPECTION_MAKEFILE)
@@ -187,16 +163,6 @@ WebHelper2Private_1_0_gir_LIBS = libwebhelper2private.la
WebHelper2Private_1_0_gir_FILES = $(libwebhelper2private_la_SOURCES)
INTROSPECTION_GIRS += WebHelper2Private-1.0.gir
-WebHelper2OldPrivate-1.0.gir: libwebhelper2oldprivate.la
-WebHelper2OldPrivate_1_0_gir_INCLUDES = GObject-2.0 GLib-2.0 WebKit2-3.0
-WebHelper2OldPrivate_1_0_gir_SCANNERFLAGS = \
- --identifier-prefix=Wh2 \
- --symbol-prefix=wh2 \
- $(NULL)
-WebHelper2OldPrivate_1_0_gir_LIBS = libwebhelper2oldprivate.la
-WebHelper2OldPrivate_1_0_gir_FILES = $(libwebhelper2oldprivate_la_SOURCES)
-INTROSPECTION_GIRS += WebHelper2OldPrivate-1.0.gir
-
girdir = $(datadir)/gir-1.0
gir_DATA = $(INTROSPECTION_GIRS)
@@ -412,7 +378,6 @@ AM_JS_LOG_FLAGS += @EOS_JS_COVERAGE_LOG_FLAGS@
AM_CFLAGS += @EOS_C_COVERAGE_CFLAGS@
AM_LDFLAGS = @EOS_C_COVERAGE_LDFLAGS@
libwebhelper2private_la_LDFLAGS += @EOS_C_COVERAGE_LDFLAGS@
-libwebhelper2oldprivate_la_LDFLAGS += @EOS_C_COVERAGE_LDFLAGS@
wh2extension_la_LDFLAGS += @EOS_C_COVERAGE_LDFLAGS@
clean-local:: clean-coverage
diff --git a/configure.ac b/configure.ac
index 8aa55f1..3e17d00 100644
--- a/configure.ac
+++ b/configure.ac
@@ -82,7 +82,6 @@ GIO_REQUIREMENT="gio-2.0"
GTK_REQUIREMENT="gtk+-3.0 >= 3.16"
JSON_GLIB_REQUIREMENT="json-glib-1.0 >= 0.12"
WEBKIT2_REQUIREMENT="webkit2gtk-4.0"
-WEBKIT2_OLD_REQUIREMENT="webkit2gtk-3.0"
# These go into the pkg-config file as Requires: and Requires.private:
# (Generally, use Requires.private: instead of Requires:)
EOS_REQUIRED_MODULES=
@@ -223,18 +222,10 @@ PKG_CHECK_MODULES([WEBHELPER2_EXTENSION], [
PKG_CHECK_MODULES([WEBHELPER2_PRIVATE], [
$GLIB_REQUIREMENT
$WEBKIT2_REQUIREMENT])
-PKG_CHECK_MODULES([WEBHELPER2_OLD_EXTENSION], [
- $GLIB_REQUIREMENT
- $GOBJECT_REQUIREMENT
- $WEBKIT2_OLD_REQUIREMENT])
-PKG_CHECK_MODULES([WEBHELPER2_OLD_PRIVATE], [
- $GLIB_REQUIREMENT
- $WEBKIT2_OLD_REQUIREMENT])
# Check installed GIRs for webhelper JS module
EOS_CHECK_GJS_GIR([GLib], [2.0])
EOS_CHECK_GJS_GIR([WebKit], [3.0])
-EOS_CHECK_GJS_GIR([WebKit2], [3.0])
EOS_CHECK_GJS_GIR([WebKit2], [4.0])
# Code coverage reports support
diff --git a/jasmine.json b/jasmine.json
index 7e2e39f..440dca1 100644
--- a/jasmine.json
+++ b/jasmine.json
@@ -10,10 +10,8 @@
],
"exclude": [
"test/webhelper/testTranslate.js",
- "test/webhelper/testTranslate2Old.js",
"test/webhelper/testUpdateFontSize.js",
- "test/webhelper/testWebActions.js",
- "test/webhelper/testWebActions2Old.js"
+ "test/webhelper/testWebActions.js"
],
"environment": {
"GI_TYPELIB_PATH": ".",
diff --git a/test/Makefile.am.inc b/test/Makefile.am.inc
index 0fad7a2..727a5df 100644
--- a/test/Makefile.am.inc
+++ b/test/Makefile.am.inc
@@ -47,10 +47,8 @@ javascript_tests = \
test/webhelper/testLocal.js \
test/webhelper/testTranslate.js \
test/webhelper/testTranslate2.js \
- test/webhelper/testTranslate2Old.js \
test/webhelper/testWebActions.js \
test/webhelper/testWebActions2.js \
- test/webhelper/testWebActions2Old.js \
test/webhelper/testUpdateFontSize.js \
test/endless/testCustomContainer.js \
test/endless/testTopbarHomeButton.js \
diff --git a/test/webhelper/testTranslate2Old.js b/test/webhelper/testTranslate2Old.js
deleted file mode 100644
index 66d54ce..0000000
--- a/test/webhelper/testTranslate2Old.js
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright 2015 Endless Mobile, Inc.
-
-const Gio = imports.gi.Gio;
-const Gtk = imports.gi.Gtk;
-const Mainloop = imports.mainloop;
-const WebHelper2 = imports.webhelper2_old;
-const WebKit2 = imports.gi.WebKit2;
-
-const WELL_KNOWN_NAME = 'com.endlessm.WebHelper.testTranslate2';
-
-/* CAUTION:
- * All tests trying to use the translation functionality of WebHelper2 must be
- * run in this file, and this file must be run before any other WebHelper2
- * tests in the same process.
- * That is because we can only tell the default web context to load web
- * extensions with user data once per process. WebHelper doesn't support web
- * contexts other than the default one.
- */
-
-Gtk.init(null);
-
-describe('WebHelper2 WebKit2-3.0 translator', function () {
- let webhelper, owner_id, connection;
-
- beforeAll(function (done) {
- owner_id = Gio.DBus.own_name(Gio.BusType.SESSION, WELL_KNOWN_NAME,
- Gio.BusNameOwnerFlags.NONE,
- null, // bus acquired
- (con) => { // name acquired
- connection = con;
- done();
- },
- null); // name lost
- });
-
- afterAll(function () {
- Gio.DBus.unown_name(owner_id);
- });
-
- beforeEach(function () {
- webhelper = new WebHelper2.WebHelper({
- well_known_name: WELL_KNOWN_NAME,
- connection: connection,
- });
- });
-
- afterEach(function () {
- webhelper.unregister();
- });
-
- it('complains about a bad gettext function', function () {
- expect(function () {
- webhelper.set_gettext('I am not a function');
- }).toThrow();
- });
-
- it('gets and sets the gettext function', function () {
- let translation_function = (s) => s;
- webhelper.set_gettext(translation_function);
- expect(webhelper.get_gettext()).toBe(translation_function);
- });
-
- it('has a null gettext function by default', function () {
- expect(webhelper.get_gettext()).toBeNull();
- });
-
- it('can remove the gettext function by setting null', function () {
- webhelper.set_gettext((s) => s);
- expect(webhelper.get_gettext()).not.toBeNull();
- webhelper.set_gettext(null);
- expect(webhelper.get_gettext()).toBeNull();
- });
-
- it('complains about a bad ngettext function', function () {
- expect(function () {
- webhelper.set_ngettext('I am not a function');
- }).toThrow();
- });
-
- it('gets and sets the ngettext function', function () {
- let translation_function = (s, p, n) => n == 1 ? s : p;
- webhelper.set_ngettext(translation_function);
- expect(webhelper.get_ngettext()).toBe(translation_function);
- });
-
- it('has a null ngettext function by default', function () {
- expect(webhelper.get_ngettext()).toBeNull();
- });
-
- it('can remove the ngettext function by setting null', function () {
- webhelper.set_ngettext((s, p, n) => n == 1 ? s : p);
- expect(webhelper.get_ngettext()).not.toBeNull();
- webhelper.set_ngettext(null);
- expect(webhelper.get_ngettext()).toBeNull();
- });
-
- describe('translating a page', function () {
- let webview, gettext_spy;
- const MINIMAL_HTML = '<p name="translatable">Translate Me</p>';
-
- function run_loop(html=MINIMAL_HTML) {
- let error_spy = jasmine.createSpy('error_spy');
- webview.connect('load-failed', error_spy);
- let id = webview.connect('load-changed', (webview, event) => {
- if (event === WebKit2.LoadEvent.FINISHED) {
- webhelper.translate_html(webview, null, (obj, res) => {
- expect(function () {
- webhelper.translate_html_finish(res);
- }).not.toThrow();
- webview.disconnect(id);
- expect(error_spy).not.toHaveBeenCalled();
- Mainloop.quit('webhelper2');
- });
- }
- });
- webview.load_html('<html><body>' + html + '</body></html>', null);
- Mainloop.run('webhelper2');
- }
-
- beforeEach(function () {
- webview = new WebKit2.WebView();
- gettext_spy = jasmine.createSpy('gettext_spy').and.callFake((s) => s);
- webhelper.set_gettext(gettext_spy);
- });
-
- it('translates a string', function () {
- run_loop();
- expect(gettext_spy).toHaveBeenCalledWith('Translate Me');
- });
-
- // The following test is disabled because GJS cannot catch exceptions
- // across FFI interfaces (e.g. in GObject callbacks.)
- xit('complains about a gettext function not being set', function () {
- expect(function () {
- run_loop();
- }).toThrow();
- });
-
- it('can cancel the translation operation', function (done) {
- webhelper.set_gettext((s) => s);
- let error_spy = jasmine.createSpy('error_spy');
- webview.connect('load-failed', error_spy);
- let id = webview.connect('load-changed', (webview, event) => {
- if (event === WebKit2.LoadEvent.FINISHED) {
- let cancellable = new Gio.Cancellable();
- cancellable.cancel();
- webhelper.translate_html(webview, cancellable, (obj, res) => {
- expect(function () {
- webhelper.translate_html_finish(res);
- }).toThrow();
- webview.disconnect(id);
- expect(error_spy).not.toHaveBeenCalled();
- done();
- });
- }
- });
- webview.load_html('<html><body></body></html>', null);
- });
-
- it('normalizes a string before translating it', function () {
- run_loop('<p name="translatable">\n\
- Translate Me\n\
- </p>');
- expect(gettext_spy).toHaveBeenCalledWith('Translate Me');
- });
-
- it('handles quotes correctly', function () {
- run_loop('<p name="translatable">String with "quotes"</p>');
- expect(gettext_spy).toHaveBeenCalledWith('String with "quotes"');
- });
-
- it('handles embedded tags correctly', function () {
- run_loop('<p name="translatable">Embedded<br><b>tags</b></p>');
- expect(gettext_spy).toHaveBeenCalledWith('Embedded<br><b>tags</b>');
- });
- });
-
- describe('used from client-side Javascript', function () {
- let webview;
-
- beforeEach(function () {
- webview = new WebKit2.WebView();
- });
-
- function load_script(view, script) {
- view.load_html('<html><body><script type="text/javascript">' +
- script + '</script></body></html>', null);
- Mainloop.run('webhelper2');
- }
-
- it('translates a string with gettext()', function (done) {
- let gettext_spy = jasmine.createSpy('gettext_spy').and.callFake((s) => {
- Mainloop.quit('webhelper2');
- return s;
- });
- webhelper.set_gettext(gettext_spy);
- load_script(webview, 'gettext("Translate Me");');
- expect(gettext_spy).toHaveBeenCalledWith('Translate Me');
- done();
- });
-
- it('translates a string with ngettext()', function (done) {
- let ngettext_spy = jasmine.createSpy('ngettext_spy').and.callFake((s, p, n) => {
- Mainloop.quit('webhelper2');
- return n == 1 ? s : p;
- });
- webhelper.set_ngettext(ngettext_spy);
- load_script(webview, 'ngettext("File", "Files", 3);');
- expect(ngettext_spy).toHaveBeenCalledWith('File', 'Files', 3);
- done();
- });
- });
-});
diff --git a/test/webhelper/testWebActions2Old.js b/test/webhelper/testWebActions2Old.js
deleted file mode 100644
index a602dc1..0000000
--- a/test/webhelper/testWebActions2Old.js
+++ /dev/null
@@ -1,120 +0,0 @@
-const Gio = imports.gi.Gio;
-const Gtk = imports.gi.Gtk;
-const Mainloop = imports.mainloop;
-const WebHelper2 = imports.webhelper2_old;
-const WebKit2 = imports.gi.WebKit2;
-
-const WELL_KNOWN_NAME = 'com.endlessm.WebHelper.testWebActions2';
-
-Gtk.init(null);
-
-describe('WebKit2-3.0 actions bindings', function () {
- let owner_id, connection, webview, webhelper, web_action_spy;
-
- beforeAll(function (done) {
- owner_id = Gio.DBus.own_name(Gio.BusType.SESSION, WELL_KNOWN_NAME,
- Gio.BusNameOwnerFlags.NONE,
- null, // bus acquired
- (con) => { // name acquired
- connection = con;
- done();
- },
- null); // name lost
- });
-
- afterAll(function () {
- Gio.DBus.unown_name(owner_id);
- });
-
- function run_loop(action_to_test) {
- let string = '<html><head><meta http-equiv="refresh" content="0;url=' +
- action_to_test + '"></head><body></body></html>';
- webview.load_html(string, null);
- Mainloop.run('webhelper2');
- }
-
- beforeEach(function () {
- webhelper = new WebHelper2.WebHelper({
- well_known_name: WELL_KNOWN_NAME,
- connection: connection,
- });
- webview = new WebKit2.WebView();
- web_action_spy = jasmine.createSpy('web_action_spy').and.callFake(() =>
- Mainloop.quit('webhelper2'));
- webhelper.define_web_action('action', web_action_spy);
- });
-
- afterEach(function () {
- webhelper.unregister();
- });
-
- it('calls a web action', function () {
- run_loop('webhelper://action');
- expect(web_action_spy).toHaveBeenCalled();
- });
-
- it('calls a web action with a parameter', function () {
- run_loop('webhelper://action?param=value');
- expect(web_action_spy).toHaveBeenCalledWith(jasmine.objectContaining({
- param: 'value',
- }));
- });
-
- it('calls a web action with many parameters', function () {
- run_loop('webhelper://action?first=thefirst&second=thesecond&third=thethird');
- expect(web_action_spy).toHaveBeenCalledWith(jasmine.objectContaining({
- first: 'thefirst',
- second: 'thesecond',
- third: 'thethird',
- }));
- });
-
- it('uri-decodes parameter names', function () {
- run_loop('webhelper://action?p%C3%A4r%C3%A4m%F0%9F%92%A9=value');
- expect(web_action_spy).toHaveBeenCalledWith(jasmine.objectContaining({
- 'päräm💩': 'value',
- }));
- });
-
- it('uri-decodes parameter values', function () {
- run_loop('webhelper://action?param=v%C3%A1lu%C3%A9%F0%9F%92%A9');
- expect(web_action_spy).toHaveBeenCalledWith(jasmine.objectContaining({
- param: 'válué💩',
- }));
- });
-
- // This is commented out because GJS cannot catch exceptions across FFI
- // interfaces (e.g. in GObject callbacks.)
- xit('raises an exception on a nonexistent action instead of calling it', function () {
- expect(function () {
- run_loop('webhelper://nonexistentAction?param=value');
- }).toThrow();
- });
-
- it('calls a web action with a blank parameter', function () {
- run_loop('webhelper://action?param=');
- expect(web_action_spy).toHaveBeenCalledWith(jasmine.objectContaining({
- param: '',
- }));
- });
-
- it('uri-decodes web action names', function () {
- webhelper.define_web_action('äction💩Quit', web_action_spy);
- run_loop('webhelper://%C3%A4ction%F0%9F%92%A9Quit');
- expect(web_action_spy).toHaveBeenCalled();
- });
-
- it('can define more than one action with define_web_actions()', function () {
- webhelper.define_web_actions({
- action2: web_action_spy,
- });
- run_loop('webhelper://action2');
- expect(web_action_spy).toBeTruthy();
- });
-
- it('complains when defining an action that is not a function', function () {
- expect(function () {
- webhelper.define_web_action('badAction', 'not a function');
- }).toThrow();
- });
-});
diff --git a/webhelper/webhelper2.js b/webhelper/webhelper2.js
index 6b28b10..ea25bc1 100644
--- a/webhelper/webhelper2.js
+++ b/webhelper/webhelper2.js
@@ -1,11 +1,529 @@
// Copyright 2015 Endless Mobile, Inc.
-/* exported WebHelper */
-
imports.gi.versions.WebKit2 = '4.0';
-window.WebHelper2Private = imports.gi.WebHelper2Private;
-window.WebKit2 = imports.gi.WebKit2;
-window.LIBEXEC_SUBDIR = 'webhelper2';
-const Common = imports.webhelper_private.common;
-const WebHelper = Common.WebHelper;
+const Format = imports.format;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Lang = imports.lang;
+const WebHelper2Private = imports.gi.WebHelper2Private;
+const WebKit2 = imports.gi.WebKit2;
+const LIBEXEC_SUBDIR = 'webhelper2';
+
+const Config = imports.webhelper_private.config;
+
+String.prototype.format = Format.format;
+
+const WH2_URI_SCHEME = 'webhelper';
+const WH2_LOCAL_FILE_SCHEME = 'local';
+const DBUS_WEBVIEW_EXPORT_PATH = '/com/endlessm/webview/';
+const WH2_DBUS_EXTENSION_INTERFACE = '\
+ <node> \
+ <interface name="com.endlessm.WebHelper2.Translation"> \
+ <method name="Translate"/> \
+ </interface> \
+ </node>';
+const WH2_DBUS_MAIN_PROGRAM_INTERFACE = '\
+ <node> \
+ <interface name="com.endlessm.WebHelper2.Gettext"> \
+ <method name="Gettext"> \
+ <arg name="message" type="s" direction="in"/> \
+ <arg name="translation" type="s" direction="out"/> \
+ </method> \
+ <method name="NGettext"> \
+ <arg name="message_singular" type="s" direction="in"/> \
+ <arg name="message_plural" type="s" direction="in"/> \
+ <arg name="number" type="t" direction="in"/> \
+ <arg name="translation" type="s" direction="out"/> \
+ </method> \
+ </interface> \
+ </node>';
+
+/**
+ * Namespace: WebHelper2
+ * Convenience library for running web applications
+ *
+ * WebHelper is a convenience library for displaying web applications inside a
+ * GTK container running WebKitGTK.
+ * WebHelper2 is the WebKit2 version.
+ *
+ * Although WebKit provides an easy way of communicating from GTK code to
+ * the in-browser Javascript, through the execute_script() method, it is not so
+ * easy to communicate the other way around.
+ *
+ * WebHelper solves that problem by detecting when the web page navigates to a
+ * custom action URI.
+ * The custom URI corresponds to a function that you define using
+ * <WebHelper.define_web_action()>, and you can pass parameters to the
+ * function.
+ *
+ * Another often-encountered problem is localizing text through the same API as
+ * your main GTK program.
+ * WebHelper solves this problem by allowing you to mark strings in your HTML
+ * page and translating them through a function of your choice when you run
+ * <WebHelper.translate_html()>.
+ * It also exposes a *gettext()* function in the client-side Javascript.
+ *
+ * For cases where you need to load local files for your web applications,
+ * WebHelper also provides the local:// URI scheme.
+ * For this to work, you must also load your main page via the local:// URI
+ * scheme.
+ */
+
+/**
+ * Class: WebHelper
+ * Helper object for a WebKit2 web application
+ *
+ * Constructor parameters:
+ * props - a dictionary of construction properties and values (default {})
+ *
+ * The application class for your web application should create <WebHelper> after
+ * registering itself in the session bus (i.e. not inside *vfunc_dbus_register()*),
+ * with appropriate <well-known-name> and <connection> parameters.
+ * A good place to do this would be in your *vfunc_startup()* implementation, where
+ * you can also do further setup on it, such as defining web actions.
+ *
+ * There is no need to set up specially any web views that you create, unlike
+ * WebKit1 where you must set up <Application.web_actions_handler()>.
+ *
+ * Example:
+ * > const TestApplication = new Lang.Class({
+ * > Name: 'TestApplication',
+ * > Extends: Gtk.Application,
+ * >
+ * > vfunc_dbus_register: function (connection, object_path) {
+ * > this._webhelper = new WebHelper2.WebHelper({
+ * > well_known_name: this.application_id,
+ * > connection: connection,
+ * > });
+ * > return this.parent(connection, object_path);
+ * > },
+ * >
+ * > vfunc_startup: function () {
+ * > this.parent();
+ * >
+ * > this._webhelper.set_gettext(Gettext.dgettext.bind(null,
+ * > GETTEXT_DOMAIN));
+ * >
+ * > let window = new Gtk.Window();
+ * > let webview = new WebKit2.WebView();
+ * > webview.connect('load-changed', (webview, event) => {
+ * > if (event === WebKit2.LoadEvent.FINISHED)
+ * > this._webhelper.translate_html(webview, null, (obj, res) => {
+ * > this._webhelper.translate_html_finish(res);
+ * > window.show_all();
+ * > });
+ * > });
+ * > window.add(webview);
+ * > webview.load_uri('file:///path/to/my/page.html');
+ * > },
+ * >
+ * > vfunc_dbus_unregister: function (connection, object_path) {
+ * > this.parent(connection, object_path);
+ * > this._webhelper.unregister();
+ * > },
+ * >});
+ * >
+ * >let app = new TestApplication({
+ * > application_id: 'com.example.SmokeGrinder',
+ * >});
+ * >app.run(ARGV);
+ */
+const WebHelper = new Lang.Class({
+ Name: 'WebHelper',
+ GTypeName: 'Wh2WebHelper',
+ Extends: GObject.Object,
+ Properties: {
+ /**
+ * Property: well-known-name
+ * Well-known bus name owned by the calling program
+ *
+ * Type:
+ * string
+ *
+ * This property is required at construction time.
+ * It must conform to <the rules for well-known bus names at
+ * http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names>.
+ *
+ * This must be a well-known bus name that your program owns.
+ * The easiest way to ensure that is to use your application's ID, since
+ * every application class registers its ID as a bus name.
+ */
+ 'well-known-name': GObject.ParamSpec.string('well-known-name',
+ 'Well-known name',
+ 'Well-known bus name owned by the calling program',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ ''),
+ /**
+ * Property: connection
+ * DBus connection owned by the calling program
+ *
+ * Type:
+ * *Gio.DBusConnection*
+ *
+ * This property is required at construction time.
+ *
+ * This must be a DBus connection object that your program owns.
+ * The easiest way to ensure that is to use the connection object passed
+ * in to your application's *vfunc_dbus_register()* function.
+ */
+ 'connection': GObject.ParamSpec.object('connection', 'Connection',
+ 'DBus connection owned by the calling program',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Gio.DBusConnection.$gtype),
+ },
+
+ _init: function (props={}) {
+ this._web_actions = {};
+ this._gettext = null;
+ this._ngettext = null;
+ this._ProxyConstructor =
+ Gio.DBusProxy.makeProxyWrapper(WH2_DBUS_EXTENSION_INTERFACE);
+ this.parent(props);
+
+ if (this.well_known_name === '')
+ throw new Error('The "well-known-name" parameter is required.');
+ this._extension_name = this.well_known_name + '.webhelper';
+
+ // Set up Webkit to load our web extension
+ let context = WebKit2.WebContext.get_default();
+ context.connect('initialize-web-extensions', () => {
+ let libexec = Gio.File.new_for_path(Config.LIBEXECDIR);
+ let path = libexec.get_child(LIBEXEC_SUBDIR).get_path();
+
+ let localpath = GLib.getenv('WEBHELPER_UNINSTALLED_EXTENSION_DIR');
+ if (localpath)
+ path = localpath;
+
+ context.set_web_extensions_directory(path);
+ context.set_web_extensions_initialization_user_data(new GLib.Variant('s',
+ this.well_known_name));
+ });
+
+ // Export our own DBus interface
+ this._dbus_impl =
+ Gio.DBusExportedObject.wrapJSObject(WH2_DBUS_MAIN_PROGRAM_INTERFACE,
+ this);
+ this._dbus_impl.export(this.connection, '/com/endlessm/gettext');
+
+ // Set up handling for custom URIs
+ WebHelper2Private.register_uri_scheme(WH2_URI_SCHEME,
+ this._on_endless_uri_request.bind(this));
+ WebHelper2Private.register_uri_scheme(WH2_LOCAL_FILE_SCHEME,
+ this._on_local_file_request.bind(this));
+ },
+
+ _on_endless_uri_request: function (request) {
+ let uri = request.get_uri();
+
+ // get the name and parameters for the desired function
+ let f_call = uri.substr((WH2_URI_SCHEME + '://').length).split('?');
+ let function_name = decodeURI(f_call[0]);
+
+ if (!this._web_actions.hasOwnProperty(function_name))
+ throw new Error(('Undefined WebHelper action "%s". Did you define it with ' +
+ 'WebHelper.Application.define_web_action()?').format(function_name));
+
+ let parameters = {};
+ if (f_call[1]) {
+ // there are parameters
+ let params = f_call[1].split('&');
+ params.forEach(function (entry) {
+ let param = entry.split('=');
+
+ if (param.length == 2) {
+ param[0] = decodeURIComponent(param[0]);
+ param[1] = decodeURIComponent(param[1]);
+ // and now we add it...
+ parameters[param[0]] = param[1];
+ }
+ });
+ }
+
+ (this._web_actions[function_name].bind(this))(parameters);
+
+ // Don't call request.finish(), because we don't want to finish the
+ // action, which would involve loading a new page. The request dies
+ // if we return from this function without calling ref() or finish()
+ // on it.
+ },
+
+ _on_local_file_request: function (request) {
+ let path = request.get_path();
+ let file = Gio.File.new_for_path(path);
+ let [content_type, certain] = Gio.content_type_guess(path, null);
+ try {
+ let stream = file.read(null);
+ request.finish(stream, -1, content_type);
+ } catch (error) {
+ request.finish_error(error);
+ }
+ },
+
+ // DBus implementations
+
+ Gettext: function (string) {
+ return this._gettext(string);
+ },
+
+ NGettext: function (singular, plural, number) {
+ return this._ngettext(singular, plural, number);
+ },
+
+ // Public API
+
+ /**
+ * Method: set_gettext
+ * Define function which translates text
+ *
+ * Parameters:
+ * gettext_func - a function, or null
+ *
+ * When you plan to translate text in your web application, set this
+ * property to the translation function.
+ * The function must take one parameter, a string, and also return a
+ * string.
+ * The canonical example is gettext().
+ *
+ * This function will be called with each string to translate when you call
+ * <translate_html()>.
+ * The function is also made available directly to the browser-side
+ * Javascript as *gettext()*, a property of the global object.
+ *
+ * Pass null for _gettext_func_ to unset the translation function.
+ *
+ * If this function has not been called, or has most recently been called
+ * with null, then it is illegal to call <translate_html()>.
+ *
+ * Example:
+ * > const Gettext = imports.gettext;
+ * > const GETTEXT_DOMAIN = 'smoke-grinder';
+ * >
+ * > webhelper.set_gettext(Gettext.dgettext.bind(null, GETTEXT_DOMAIN));
+ */
+ set_gettext: function (gettext_func) {
+ if (gettext_func !== null && typeof gettext_func !== 'function')
+ throw new Error('The translation function must be a function, or ' +
+ 'null.');
+ this._gettext = gettext_func;
+ },
+
+ /**
+ * Method: get_gettext
+ * Retrieve the currently set translation function
+ *
+ * Returns:
+ * the translation function previously set with <set_gettext()>, or null
+ * if none is currently set.
+ */
+ get_gettext: function () {
+ return this._gettext;
+ },
+
+ /**
+ * Method: set_ngettext
+ * Define function which translates singular/plural text
+ *
+ * Parameters:
+ * ngettext_func - a function, or null
+ *
+ * When you plan to translate text in your web application, set this
+ * property to the translation function.
+ * The function must take three parameters: a string singular message, a
+ * string plural message, and a number for which to generate the message.
+ * The function must return a string.
+ * The canonical example is ngettext().
+ *
+ * This function is made available directly to the browser-side Javascript
+ * as *ngettext()*, a property of the global object.
+ *
+ * Pass null for _ngettext_func_ to unset the translation function.
+ *
+ * If this function has not been called, or has most recently been called
+ * with null, then it is illegal to call *ngettext()* in the client-side
+ * Javascript.
+ *
+ * Example:
+ * > const WebHelper2 = imports.webhelper2;
+ * > const Gettext = imports.gettext;
+ * > const GETTEXT_DOMAIN = 'smoke-grinder';
+ * >
+ * > let webhelper = new WebHelper2.WebHelper('com.example.SmokeGrinder');
+ * > webhelper.set_gettext(Gettext.dngettext.bind(null, GETTEXT_DOMAIN));
+ */
+ set_ngettext: function (ngettext_func) {
+ if (ngettext_func !== null && typeof ngettext_func !== 'function')
+ throw new Error('The translation function must be a function, or ' +
+ 'null.');
+ this._ngettext = ngettext_func;
+ },
+
+ /**
+ * Method: get_ngettext
+ * Retrieve the currently set singular/plural translation function
+ *
+ * Returns:
+ * the translation function previously set with <set_ngettext()>, or null
+ * if none is currently set.
+ */
+ get_ngettext: function () {
+ return this._ngettext;
+ },
+
+ /**
+ * Method: translate_html
+ * Translate the HTML page in a webview asynchronously
+ *
+ * Parameters:
+ * webview - a *WebKit2.WebView* with HTML loaded
+ * cancellable - a *Gio.Cancellable*, or null
+ * callback - a function that takes two parameters: this <WebHelper>
+ * object, and a result object; or null if you don't want a callback
+ *
+ * Perform translation on all HTML elements marked with name="translatable"
+ * in the HTML document displaying in _webview_.
+ * The translation will be performed using the function you have set using
+ * <set_gettext()>.
+ *
+ * When the translation is finished, _callback_ will be called.
+ * You can get the result of the operation by calling
+ * <translate_html_finish()> with the _result_ object passed to _callback_.
+ *
+ * You can optionally pass _cancellable_ if you want to be able to cancel
+ * the operation.
+ *
+ * Example:
+ * > webview.connect('load-changed', (webview, event) => {
+ * > if (event === WebKit2.LoadEvent.FINISHED)
+ * > webhelper.translate_html(webview, null, (obj, res) => {
+ * > webhelper.translate_html_finish(res);
+ * > // Translation done, show the page
+ * > webview.show_all();
+ * > });
+ * > });
+ */
+ translate_html: function (webview, cancellable, callback) {
+ let task = { callback: callback };
+ // Wait for the DBus interface to appear on the bus
+ task.watch_id = Gio.DBus.watch_name(Gio.BusType.SESSION,
+ this._extension_name, Gio.BusNameWatcherFlags.NONE,
+ (connection) => {
+ // name appeared
+ let webview_object_path = DBUS_WEBVIEW_EXPORT_PATH +
+ webview.get_page_id();
+ // Warning: this._ProxyConstructor will do a synchronous
+ // operation unless you pass in a callback
+ new this._ProxyConstructor(connection, this._extension_name,
+ webview_object_path, (proxy, error) =>
+ {
+ if (error) {
+ this._translate_callback(task, null, error);
+ return;
+ }
+ if (cancellable)
+ proxy.TranslateRemote(cancellable,
+ this._translate_callback.bind(this, task));
+ else
+ proxy.TranslateRemote(this._translate_callback.bind(this,
+ task));
+ }, cancellable);
+ },
+ null); // do nothing when name vanishes
+ return task;
+ },
+
+ _translate_callback: function (task, result, error) {
+ Gio.DBus.unwatch_name(task.watch_id);
+ if (error)
+ task.error = error;
+ if (task.callback)
+ task.callback(this, task);
+ },
+
+ /**
+ * Method: translate_html_finish
+ * Get the result of <translate_html()>
+ *
+ * Parameters:
+ * res - result object passed to your callback
+ *
+ * Finishes an operation started by <translate_html()>.
+ * If the operation ended in an error, this function will throw that error.
+ */
+ translate_html_finish: function (res) {
+ if (res.error)
+ throw res.error;
+ },
+
+ /**
+ * Method: define_web_action
+ * Define an action that may be invoked from a WebView
+ *
+ * Parameters:
+ * name - a string, which must be a valid URI location.
+ * implementation - a function (see Callback Parameters below.)
+ *
+ * Callback Parameters:
+ * dict - object containing properties corresponding to the query
+ * parameters that the web action was called with
+ *
+ * Sets up an action that may be invoked from an HTML document inside a
+ * WebView, or from the in-browser Javascript environment inside a WebView.
+ * If you set up an action "setVolume" as follows:
+ * > webhelper.define_web_action('setVolume', function (dict) { ... });
+ * Then you can invoke the action inside the HTML document, e.g. as the
+ * target of a link, as follows:
+ * > <a href="webhelper://setVolume?volume=11">This one goes to 11</a>
+ * Or from the in-browser Javascript, by navigating to the action URI, as
+ * follows:
+ * > window.location.href = 'webhelper://setVolume?volume=11';
+ *
+ * In both cases, the function would then be called with the _dict_
+ * parameter equal to
+ * > { "volume": "11" }
+ *
+ * If an action called _name_ is already defined, the new _implementation_
+ * replaces the old one.
+ */
+ define_web_action: function (name, implementation) {
+ if (typeof implementation !== 'function') {
+ throw new Error('The implementation of a web action must be a ' +
+ 'function.');
+ }
+ this._web_actions[name] = implementation;
+ },
+
+ /**
+ * Method: define_web_actions
+ * Define several web actions at once
+ *
+ * Parameters:
+ * dict - an object, with web action names as property names, and their
+ * implementations as values
+ *
+ * Convenience method to define more than one web action at once.
+ * Calls <define_web_action()> on each property of _dict_.
+ *
+ * *Note* This API is Javascript-only. It will not be implemented in C.
+ */
+ define_web_actions: function (dict) {
+ Object.keys(dict).forEach((key) =>
+ this.define_web_action(key, dict[key]));
+ },
+
+ /**
+ * Method: unregister
+ * Break the connection to WebKit
+ *
+ * Breaks the connection to all webviews and removes all DBus objects.
+ * You should call this in your application's *vfunc_dbus_unregister()*
+ * implementation.
+ *
+ * After this function has been called, no WebHelper functionality will
+ * work.
+ */
+ unregister: function () {
+ this._dbus_impl.unexport_from_connection(this.connection);
+ },
+});
diff --git a/webhelper/webhelper2_old.js b/webhelper/webhelper2_old.js
deleted file mode 100644
index 809504e..0000000
--- a/webhelper/webhelper2_old.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2015 Endless Mobile, Inc.
-
-/* exported WebHelper */
-
-imports.gi.versions.WebKit2 = '3.0';
-window.WebHelper2Private = imports.gi.WebHelper2OldPrivate;
-window.WebKit2 = imports.gi.WebKit2;
-window.LIBEXEC_SUBDIR = 'webhelper2old';
-
-const Common = imports.webhelper_private.common;
-const WebHelper = Common.WebHelper;
diff --git a/webhelper/webhelper_private/common.js b/webhelper/webhelper_private/common.js
deleted file mode 100644
index 33aa2a0..0000000
--- a/webhelper/webhelper_private/common.js
+++ /dev/null
@@ -1,530 +0,0 @@
-// Copyright 2015 Endless Mobile, Inc.
-
-// The following constant and two modules must be in the global namespace before
-// importing this file: LIBEXEC_SUBDIR, WebKit2, WebHelper2Private
-
-/* exported WebHelper */
-
-const Format = imports.format;
-const Gio = imports.gi.Gio;
-const GLib = imports.gi.GLib;
-const GObject = imports.gi.GObject;
-const Lang = imports.lang;
-
-String.prototype.format = Format.format;
-
-const Config = imports.webhelper_private.config;
-
-const WH2_URI_SCHEME = 'webhelper';
-const WH2_LOCAL_FILE_SCHEME = 'local';
-const DBUS_WEBVIEW_EXPORT_PATH = '/com/endlessm/webview/';
-const WH2_DBUS_EXTENSION_INTERFACE = '\
- <node> \
- <interface name="com.endlessm.WebHelper2.Translation"> \
- <method name="Translate"/> \
- </interface> \
- </node>';
-const WH2_DBUS_MAIN_PROGRAM_INTERFACE = '\
- <node> \
- <interface name="com.endlessm.WebHelper2.Gettext"> \
- <method name="Gettext"> \
- <arg name="message" type="s" direction="in"/> \
- <arg name="translation" type="s" direction="out"/> \
- </method> \
- <method name="NGettext"> \
- <arg name="message_singular" type="s" direction="in"/> \
- <arg name="message_plural" type="s" direction="in"/> \
- <arg name="number" type="t" direction="in"/> \
- <arg name="translation" type="s" direction="out"/> \
- </method> \
- </interface> \
- </node>';
-
-
-/**
- * Namespace: WebHelper2
- * Convenience library for running web applications
- *
- * WebHelper is a convenience library for displaying web applications inside a
- * GTK container running WebKitGTK.
- * WebHelper2 is the WebKit2 version.
- *
- * Although WebKit provides an easy way of communicating from GTK code to
- * the in-browser Javascript, through the execute_script() method, it is not so
- * easy to communicate the other way around.
- *
- * WebHelper solves that problem by detecting when the web page navigates to a
- * custom action URI.
- * The custom URI corresponds to a function that you define using
- * <WebHelper.define_web_action()>, and you can pass parameters to the
- * function.
- *
- * Another often-encountered problem is localizing text through the same API as
- * your main GTK program.
- * WebHelper solves this problem by allowing you to mark strings in your HTML
- * page and translating them through a function of your choice when you run
- * <WebHelper.translate_html()>.
- * It also exposes a *gettext()* function in the client-side Javascript.
- *
- * For cases where you need to load local files for your web applications,
- * WebHelper also provides the local:// URI scheme.
- * For this to work, you must also load your main page via the local:// URI
- * scheme.
- */
-
-/**
- * Class: WebHelper
- * Helper object for a WebKit2 web application
- *
- * Constructor parameters:
- * props - a dictionary of construction properties and values (default {})
- *
- * The application class for your web application should create <WebHelper> after
- * registering itself in the session bus (i.e. not inside *vfunc_dbus_register()*),
- * with appropriate <well-known-name> and <connection> parameters.
- * A good place to do this would be in your *vfunc_startup()* implementation, where
- * you can also do further setup on it, such as defining web actions.
- *
- * There is no need to set up specially any web views that you create, unlike
- * WebKit1 where you must set up <Application.web_actions_handler()>.
- *
- * Example:
- * > const TestApplication = new Lang.Class({
- * > Name: 'TestApplication',
- * > Extends: Gtk.Application,
- * >
- * > vfunc_dbus_register: function (connection, object_path) {
- * > this._webhelper = new WebHelper2.WebHelper({
- * > well_known_name: this.application_id,
- * > connection: connection,
- * > });
- * > return this.parent(connection, object_path);
- * > },
- * >
- * > vfunc_startup: function () {
- * > this.parent();
- * >
- * > this._webhelper.set_gettext(Gettext.dgettext.bind(null,
- * > GETTEXT_DOMAIN));
- * >
- * > let window = new Gtk.Window();
- * > let webview = new WebKit2.WebView();
- * > webview.connect('load-changed', (webview, event) => {
- * > if (event === WebKit2.LoadEvent.FINISHED)
- * > this._webhelper.translate_html(webview, null, (obj, res) => {
- * > this._webhelper.translate_html_finish(res);
- * > window.show_all();
- * > });
- * > });
- * > window.add(webview);
- * > webview.load_uri('file:///path/to/my/page.html');
- * > },
- * >
- * > vfunc_dbus_unregister: function (connection, object_path) {
- * > this.parent(connection, object_path);
- * > this._webhelper.unregister();
- * > },
- * >});
- * >
- * >let app = new TestApplication({
- * > application_id: 'com.example.SmokeGrinder',
- * >});
- * >app.run(ARGV);
- */
-const WebHelper = new Lang.Class({
- Name: 'WebHelper',
- GTypeName: 'Wh2WebHelper',
- Extends: GObject.Object,
- Properties: {
- /**
- * Property: well-known-name
- * Well-known bus name owned by the calling program
- *
- * Type:
- * string
- *
- * This property is required at construction time.
- * It must conform to <the rules for well-known bus names at
- * http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names>.
- *
- * This must be a well-known bus name that your program owns.
- * The easiest way to ensure that is to use your application's ID, since
- * every application class registers its ID as a bus name.
- */
- 'well-known-name': GObject.ParamSpec.string('well-known-name',
- 'Well-known name',
- 'Well-known bus name owned by the calling program',
- GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
- ''),
- /**
- * Property: connection
- * DBus connection owned by the calling program
- *
- * Type:
- * *Gio.DBusConnection*
- *
- * This property is required at construction time.
- *
- * This must be a DBus connection object that your program owns.
- * The easiest way to ensure that is to use the connection object passed
- * in to your application's *vfunc_dbus_register()* function.
- */
- 'connection': GObject.ParamSpec.object('connection', 'Connection',
- 'DBus connection owned by the calling program',
- GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
- Gio.DBusConnection.$gtype),
- },
-
- _init: function (props={}) {
- this._web_actions = {};
- this._gettext = null;
- this._ngettext = null;
- this._ProxyConstructor =
- Gio.DBusProxy.makeProxyWrapper(WH2_DBUS_EXTENSION_INTERFACE);
- this.parent(props);
-
- if (this.well_known_name === '')
- throw new Error('The "well-known-name" parameter is required.');
- this._extension_name = this.well_known_name + '.webhelper';
-
- // Set up Webkit to load our web extension
- let context = window.WebKit2.WebContext.get_default();
- context.connect('initialize-web-extensions', () => {
- let libexec = Gio.File.new_for_path(Config.LIBEXECDIR);
- let path = libexec.get_child(window.LIBEXEC_SUBDIR).get_path();
-
- let localpath = GLib.getenv('WEBHELPER_UNINSTALLED_EXTENSION_DIR');
- if (localpath)
- path = localpath;
-
- context.set_web_extensions_directory(path);
- context.set_web_extensions_initialization_user_data(new GLib.Variant('s',
- this.well_known_name));
- });
-
- // Export our own DBus interface
- this._dbus_impl =
- Gio.DBusExportedObject.wrapJSObject(WH2_DBUS_MAIN_PROGRAM_INTERFACE,
- this);
- this._dbus_impl.export(this.connection, '/com/endlessm/gettext');
-
- // Set up handling for custom URIs
- window.WebHelper2Private.register_uri_scheme(WH2_URI_SCHEME,
- this._on_endless_uri_request.bind(this));
- window.WebHelper2Private.register_uri_scheme(WH2_LOCAL_FILE_SCHEME,
- this._on_local_file_request.bind(this));
- },
-
- _on_endless_uri_request: function (request) {
- let uri = request.get_uri();
-
- // get the name and parameters for the desired function
- let f_call = uri.substr((WH2_URI_SCHEME + '://').length).split('?');
- let function_name = decodeURI(f_call[0]);
-
- if (!this._web_actions.hasOwnProperty(function_name))
- throw new Error(('Undefined WebHelper action "%s". Did you define it with ' +
- 'WebHelper.Application.define_web_action()?').format(function_name));
-
- let parameters = {};
- if (f_call[1]) {
- // there are parameters
- let params = f_call[1].split('&');
- params.forEach(function (entry) {
- let param = entry.split('=');
-
- if (param.length == 2) {
- param[0] = decodeURIComponent(param[0]);
- param[1] = decodeURIComponent(param[1]);
- // and now we add it...
- parameters[param[0]] = param[1];
- }
- });
- }
-
- (this._web_actions[function_name].bind(this))(parameters);
-
- // Don't call request.finish(), because we don't want to finish the
- // action, which would involve loading a new page. The request dies
- // if we return from this function without calling ref() or finish()
- // on it.
- },
-
- _on_local_file_request: function (request) {
- let path = request.get_path();
- let file = Gio.File.new_for_path(path);
- let [content_type, certain] = Gio.content_type_guess(path, null);
- try {
- let stream = file.read(null);
- request.finish(stream, -1, content_type);
- } catch (error) {
- request.finish_error(error);
- }
- },
-
- // DBus implementations
-
- Gettext: function (string) {
- return this._gettext(string);
- },
-
- NGettext: function (singular, plural, number) {
- return this._ngettext(singular, plural, number);
- },
-
- // Public API
-
- /**
- * Method: set_gettext
- * Define function which translates text
- *
- * Parameters:
- * gettext_func - a function, or null
- *
- * When you plan to translate text in your web application, set this
- * property to the translation function.
- * The function must take one parameter, a string, and also return a
- * string.
- * The canonical example is gettext().
- *
- * This function will be called with each string to translate when you call
- * <translate_html()>.
- * The function is also made available directly to the browser-side
- * Javascript as *gettext()*, a property of the global object.
- *
- * Pass null for _gettext_func_ to unset the translation function.
- *
- * If this function has not been called, or has most recently been called
- * with null, then it is illegal to call <translate_html()>.
- *
- * Example:
- * > const Gettext = imports.gettext;
- * > const GETTEXT_DOMAIN = 'smoke-grinder';
- * >
- * > webhelper.set_gettext(Gettext.dgettext.bind(null, GETTEXT_DOMAIN));
- */
- set_gettext: function (gettext_func) {
- if (gettext_func !== null && typeof gettext_func !== 'function')
- throw new Error('The translation function must be a function, or ' +
- 'null.');
- this._gettext = gettext_func;
- },
-
- /**
- * Method: get_gettext
- * Retrieve the currently set translation function
- *
- * Returns:
- * the translation function previously set with <set_gettext()>, or null
- * if none is currently set.
- */
- get_gettext: function () {
- return this._gettext;
- },
-
- /**
- * Method: set_ngettext
- * Define function which translates singular/plural text
- *
- * Parameters:
- * ngettext_func - a function, or null
- *
- * When you plan to translate text in your web application, set this
- * property to the translation function.
- * The function must take three parameters: a string singular message, a
- * string plural message, and a number for which to generate the message.
- * The function must return a string.
- * The canonical example is ngettext().
- *
- * This function is made available directly to the browser-side Javascript
- * as *ngettext()*, a property of the global object.
- *
- * Pass null for _ngettext_func_ to unset the translation function.
- *
- * If this function has not been called, or has most recently been called
- * with null, then it is illegal to call *ngettext()* in the client-side
- * Javascript.
- *
- * Example:
- * > const WebHelper2 = imports.webhelper2;
- * > const Gettext = imports.gettext;
- * > const GETTEXT_DOMAIN = 'smoke-grinder';
- * >
- * > let webhelper = new WebHelper2.WebHelper('com.example.SmokeGrinder');
- * > webhelper.set_gettext(Gettext.dngettext.bind(null, GETTEXT_DOMAIN));
- */
- set_ngettext: function (ngettext_func) {
- if (ngettext_func !== null && typeof ngettext_func !== 'function')
- throw new Error('The translation function must be a function, or ' +
- 'null.');
- this._ngettext = ngettext_func;
- },
-
- /**
- * Method: get_ngettext
- * Retrieve the currently set singular/plural translation function
- *
- * Returns:
- * the translation function previously set with <set_ngettext()>, or null
- * if none is currently set.
- */
- get_ngettext: function () {
- return this._ngettext;
- },
-
- /**
- * Method: translate_html
- * Translate the HTML page in a webview asynchronously
- *
- * Parameters:
- * webview - a *WebKit2.WebView* with HTML loaded
- * cancellable - a *Gio.Cancellable*, or null
- * callback - a function that takes two parameters: this <WebHelper>
- * object, and a result object; or null if you don't want a callback
- *
- * Perform translation on all HTML elements marked with name="translatable"
- * in the HTML document displaying in _webview_.
- * The translation will be performed using the function you have set using
- * <set_gettext()>.
- *
- * When the translation is finished, _callback_ will be called.
- * You can get the result of the operation by calling
- * <translate_html_finish()> with the _result_ object passed to _callback_.
- *
- * You can optionally pass _cancellable_ if you want to be able to cancel
- * the operation.
- *
- * Example:
- * > webview.connect('load-changed', (webview, event) => {
- * > if (event === WebKit2.LoadEvent.FINISHED)
- * > webhelper.translate_html(webview, null, (obj, res) => {
- * > webhelper.translate_html_finish(res);
- * > // Translation done, show the page
- * > webview.show_all();
- * > });
- * > });
- */
- translate_html: function (webview, cancellable, callback) {
- let task = { callback: callback };
- // Wait for the DBus interface to appear on the bus
- task.watch_id = Gio.DBus.watch_name(Gio.BusType.SESSION,
- this._extension_name, Gio.BusNameWatcherFlags.NONE,
- (connection) => {
- // name appeared
- let webview_object_path = DBUS_WEBVIEW_EXPORT_PATH +
- webview.get_page_id();
- // Warning: this._ProxyConstructor will do a synchronous
- // operation unless you pass in a callback
- new this._ProxyConstructor(connection, this._extension_name,
- webview_object_path, (proxy, error) =>
- {
- if (error) {
- this._translate_callback(task, null, error);
- return;
- }
- if (cancellable)
- proxy.TranslateRemote(cancellable,
- this._translate_callback.bind(this, task));
- else
- proxy.TranslateRemote(this._translate_callback.bind(this,
- task));
- }, cancellable);
- },
- null); // do nothing when name vanishes
- return task;
- },
-
- _translate_callback: function (task, result, error) {
- Gio.DBus.unwatch_name(task.watch_id);
- if (error)
- task.error = error;
- if (task.callback)
- task.callback(this, task);
- },
-
- /**
- * Method: translate_html_finish
- * Get the result of <translate_html()>
- *
- * Parameters:
- * res - result object passed to your callback
- *
- * Finishes an operation started by <translate_html()>.
- * If the operation ended in an error, this function will throw that error.
- */
- translate_html_finish: function (res) {
- if (res.error)
- throw res.error;
- },
-
- /**
- * Method: define_web_action
- * Define an action that may be invoked from a WebView
- *
- * Parameters:
- * name - a string, which must be a valid URI location.
- * implementation - a function (see Callback Parameters below.)
- *
- * Callback Parameters:
- * dict - object containing properties corresponding to the query
- * parameters that the web action was called with
- *
- * Sets up an action that may be invoked from an HTML document inside a
- * WebView, or from the in-browser Javascript environment inside a WebView.
- * If you set up an action "setVolume" as follows:
- * > webhelper.define_web_action('setVolume', function (dict) { ... });
- * Then you can invoke the action inside the HTML document, e.g. as the
- * target of a link, as follows:
- * > <a href="webhelper://setVolume?volume=11">This one goes to 11</a>
- * Or from the in-browser Javascript, by navigating to the action URI, as
- * follows:
- * > window.location.href = 'webhelper://setVolume?volume=11';
- *
- * In both cases, the function would then be called with the _dict_
- * parameter equal to
- * > { "volume": "11" }
- *
- * If an action called _name_ is already defined, the new _implementation_
- * replaces the old one.
- */
- define_web_action: function (name, implementation) {
- if (typeof implementation !== 'function') {
- throw new Error('The implementation of a web action must be a ' +
- 'function.');
- }
- this._web_actions[name] = implementation;
- },
-
- /**
- * Method: define_web_actions
- * Define several web actions at once
- *
- * Parameters:
- * dict - an object, with web action names as property names, and their
- * implementations as values
- *
- * Convenience method to define more than one web action at once.
- * Calls <define_web_action()> on each property of _dict_.
- *
- * *Note* This API is Javascript-only. It will not be implemented in C.
- */
- define_web_actions: function (dict) {
- Object.keys(dict).forEach((key) =>
- this.define_web_action(key, dict[key]));
- },
-
- /**
- * Method: unregister
- * Break the connection to WebKit
- *
- * Breaks the connection to all webviews and removes all DBus objects.
- * You should call this in your application's *vfunc_dbus_unregister()*
- * implementation.
- *
- * After this function has been called, no WebHelper functionality will
- * work.
- */
- unregister: function () {
- this._dbus_impl.unexport_from_connection(this.connection);
- },
-});