aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2022-03-04 15:56:50 +0100
committerWojtek Kosior <koszko@koszko.org>2022-03-04 16:36:44 +0100
commit587c1a88475c162b820d94c37e2cd18eb4422276 (patch)
tree96b30c12159f63b30c621e227df634056424f92f
parent652e4476fdc2c7177145c0d993159f84a5f64c55 (diff)
downloadbrowser-extension-587c1a88475c162b820d94c37e2cd18eb4422276.tar.gz
browser-extension-587c1a88475c162b820d94c37e2cd18eb4422276.zip
display an informative message in settings page if IndexedDB cannot be accessed
-rw-r--r--html/settings.html95
-rw-r--r--html/settings.js50
-rw-r--r--test/haketilo_test/test_integration.py7
-rw-r--r--test/haketilo_test/unit/test_indexeddb.py1
-rw-r--r--test/haketilo_test/unit/test_settings.py61
5 files changed, 181 insertions, 33 deletions
diff --git a/html/settings.html b/html/settings.html
index 7abb870..e33f112 100644
--- a/html/settings.html
+++ b/html/settings.html
@@ -35,6 +35,15 @@
#LOADCSS html/base.css
#LOADCSS html/grid.css
<style>
+ #loader, #indexeddb_error {
+ margin: auto;
+ padding: 1em;
+ max-width: 800px;
+ }
+ #indexeddb_error p {
+ margin-bottom: 1em;
+ }
+
/* Style top menu items. */
#tab_heads>* {
background-color: #70AF70;
@@ -114,34 +123,72 @@
#INCLUDE html/item_preview.html
#INCLUDE html/text_entry_list.html
#INCLUDE html/payload_create.html
- <ul id="tab_heads">
- <li id="blocking_head"> Blocking </li>
- <li id="mappings_head"> Mappings </li>
- <li id="resources_head"> Resources </li>
- <li id="new_payload_head" class="active_head"> New payload </li>
- <li id="repos_head"> Repositories </li>
- </ul>
- <div id="top_menu_line" class="top_line"></div>
- <div id="blocking_tab" class="tab">
- <div id="blocking_editable_container" class="grid_2">
- <div id="blocking_list_container">
- <h3>Block scripts on</h3>
- </div>
- <div id="allowing_list_container">
- <h3>Allow scripts on</h3>
+ <div id="loader">
+ Loading settings page...
+ </div>
+ <div id="indexeddb_error" class="hide">
+ <p>
+ Cannot display settings page.
+ </p>
+ <p>
+ Haketilo could not access IndexedDB. IndexedDB is an in-browser
+ database in which Haketilo stores all its configuration. Without it, the
+ settings page is non-operational.
+ <p>
+#IF MOZILLA
+ <div id="indexeddb_private_mode_explanation" class="hide">
+ <p>
+ This issue is the result of using Haketilo in Private Browsing mode.
+ For privacy reasons your browser blocks access to IndexedDB in
+ private windows and this unfortunately also affects Haketilo's
+ settings page.
+ </p>
+ <p>
+ You can sacrifice this single privacy feature and enable IndexedDB
+ access in private windows by navigating to "about:config" in the URL
+ bar, agreeing to accept the risk and setting the
+ "dom.indexedDB.privateBrowsing.enabled" preference to "true". Those
+ pages that have their scripts blocked will still be unable to access
+ IndexedDB.
+ </p>
+ <p>
+ Alternatively, you can open Haketilo's settings in a separate,
+ non-private window. The configuration you make there will take
+ effect on pages opened in Private Browsing mode as well.
+ </p>
</div>
- <div id="default_policy_dialog" class="grid_col_both text_center">
+#ENDIF
+ </div>
+ <div id="main_view" class="hide">
+ <ul id="tab_heads">
+ <li id="blocking_head"> Blocking </li>
+ <li id="mappings_head"> Mappings </li>
+ <li id="resources_head"> Resources </li>
+ <li id="new_payload_head" class="active_head"> New payload </li>
+ <li id="repos_head"> Repositories </li>
+ </ul>
+ <div id="top_menu_line" class="top_line"></div>
+ <div id="blocking_tab" class="tab">
+ <div id="blocking_editable_container" class="grid_2">
+ <div id="blocking_list_container">
+ <h3>Block scripts on</h3>
+ </div>
+ <div id="allowing_list_container">
+ <h3>Allow scripts on</h3>
+ </div>
+ <div id="default_policy_dialog" class="grid_col_both text_center">
#INCLUDE html/default_blocking_policy.html
+ </div>
</div>
+ <div id="blocking_dialog_container" class="hide"></div>
+ </div>
+ <div id="mappings_tab" class="tab"></div>
+ <div id="resources_tab" class="tab"></div>
+ <div id="new_payload_tab" class="tab active_tab"></div>
+ <div id="repos_tab" class="tab">
+ <div id="repos_list_container"></div>
+ <div id="repos_dialog_container" class="hide"></div>
</div>
- <div id="blocking_dialog_container" class="hide"></div>
- </div>
- <div id="mappings_tab" class="tab"></div>
- <div id="resources_tab" class="tab"></div>
- <div id="new_payload_tab" class="tab active_tab"></div>
- <div id="repos_tab" class="tab">
- <div id="repos_list_container"></div>
- <div id="repos_dialog_container" class="hide"></div>
</div>
#LOADJS html/settings.js
</body>
diff --git a/html/settings.js b/html/settings.js
index 36eeb47..686cc11 100644
--- a/html/settings.js
+++ b/html/settings.js
@@ -41,8 +41,13 @@
* proprietary program, I am not going to enforce this in court.
*/
+#IMPORT common/indexeddb.js AS haketilodb
#IMPORT html/dialog.js
+#IF MOZILLA
+#FROM common/browser.js IMPORT browser
+#ENDIF
+
#FROM html/DOM_helpers.js IMPORT by_id
#FROM html/text_entry_list.js IMPORT blocking_allowing_lists, repo_list
#FROM html/item_list.js IMPORT mapping_list, resource_list
@@ -118,8 +123,43 @@ async function set_up_repos_tab() {
containers[0].append((await repo_list(dialog_ctx)).main_div);
}
-set_up_blocking_tab();
-set_up_mappings_tab();
-set_up_resources_tab();
-set_up_new_payload_tab();
-set_up_repos_tab();
+function set_up_settings_view() {
+ by_id("loader").remove();
+ by_id("main_view").classList.remove("hide");
+
+ set_up_blocking_tab();
+ set_up_mappings_tab();
+ set_up_resources_tab();
+ set_up_new_payload_tab();
+ set_up_repos_tab();
+}
+
+#IF MOZILLA
+async function show_indexeddb_error() {
+ const this_tab = await browser.tabs.getCurrent();
+ if (this_tab.incognito)
+ by_id("indexeddb_private_mode_explanation").classList.remove("hide");
+#ELSE
+function show_indexeddb_error() {
+#ENDIF
+ by_id("loader").remove();
+ by_id("indexeddb_error").classList.remove("hide");
+}
+
+async function init_settings_page() {
+ try {
+ await haketilodb.get();
+ } catch(e) {
+ console.error(e);
+ show_indexeddb_error();
+ return;
+ }
+
+ set_up_settings_view();
+}
+
+#IF UNIT_TEST
+window.init_settings_page = init_settings_page;
+#ELSE
+init_settings_page();
+#ENDIF
diff --git a/test/haketilo_test/test_integration.py b/test/haketilo_test/test_integration.py
index 87d1827..b77afbd 100644
--- a/test/haketilo_test/test_integration.py
+++ b/test/haketilo_test/test_integration.py
@@ -19,6 +19,10 @@ Haketilo integration tests
import pytest
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.common.by import By
+
from .extension_crafting import get_extension_base_url
@pytest.mark.usefixtures('haketilo')
@@ -30,6 +34,9 @@ def test_integration(driver):
base_url = get_extension_base_url(driver)
driver.get(base_url + 'html/settings.html')
+ WebDriverWait(driver, 10)\
+ .until(EC.visibility_of_element_located((By.ID, "main_view")))
+
for tab_head_id, item_text in [
('resources_head', 'Haketilo demonstrational script'),
('mappings_head', 'Haketilo demonstrational message'),
diff --git a/test/haketilo_test/unit/test_indexeddb.py b/test/haketilo_test/unit/test_indexeddb.py
index c2d5427..773f5c8 100644
--- a/test/haketilo_test/unit/test_indexeddb.py
+++ b/test/haketilo_test/unit/test_indexeddb.py
@@ -21,7 +21,6 @@ import pytest
import json
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import WebDriverException
from ..script_loader import load_script
diff --git a/test/haketilo_test/unit/test_settings.py b/test/haketilo_test/unit/test_settings.py
index 7cdb76f..92ec4b6 100644
--- a/test/haketilo_test/unit/test_settings.py
+++ b/test/haketilo_test/unit/test_settings.py
@@ -20,18 +20,24 @@ Haketilo unit tests - entire settings page
import pytest
from .utils import *
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.common.by import By
+
from ..extension_crafting import ExtraHTML
from ..script_loader import load_script
from .utils import *
-@pytest.mark.ext_data({
+ext_data = {
'background_script': broker_js,
'extra_html': ExtraHTML('html/settings.html', wrap_into_htmldoc=False)
-})
+}
+
+@pytest.mark.ext_data(ext_data)
@pytest.mark.usefixtures('webextension')
def test_settings_page_tabs(driver, execute_in_page):
"""
- Test navigation throught the tabs of the settings page.
+ Test navigation through the tabs of the settings page.
"""
# First, put some sample data in IndexedDB.
execute_in_page(load_script('common/indexeddb.js'))
@@ -45,6 +51,13 @@ def test_settings_page_tabs(driver, execute_in_page):
# Now navigate to settings page.
testpage_url = driver.execute_script('return window.location.href;')
driver.get(testpage_url.replace('testpage.html', 'html/settings.html'))
+ execute_in_page('init_settings_page();')
+
+ WebDriverWait(driver, 10)\
+ .until(EC.visibility_of_element_located((By.ID, "main_view")))
+
+ assert driver.find_elements_by_id('loader') == []
+ assert not driver.find_element_by_id('indexeddb_error').is_displayed()
names = ['blocking', 'mappings', 'resources', 'new_payload', 'repos']
tabs = dict([(n, driver.find_element_by_id(f'{n}_tab')) for n in names])
@@ -61,3 +74,45 @@ def test_settings_page_tabs(driver, execute_in_page):
assert 'active_head' not in heads[tab_name_2].get_attribute('class')
assert 'active_tab' not in tabs[tab_name_2].get_attribute('class')
assert not tabs[tab_name_2].is_displayed()
+
+@pytest.mark.ext_data({
+ **ext_data,
+ 'navigate_to': 'html/settings.html'
+})
+@pytest.mark.usefixtures('webextension')
+@pytest.mark.parametrize('incognito', [True, False])
+def test_settings_page_indexeddb_error(driver, execute_in_page, incognito):
+ """
+ Test if failure to access IndexedDB in settings page results in the
+ appropriate message being shown.
+ """
+ execute_in_page(
+ '''{
+ /*
+ * Mock an unavailable IndexedDB. Calling onerror() without having set
+ * "errorCode" on the request is the behavior observed under Mozilla.
+ */
+ indexedDB.open = function() {
+ const dummy_open_request = {};
+ const dummy_event = {target: dummy_open_request};
+ setTimeout(() => dummy_open_request.onerror(dummy_event), 0);
+
+ return dummy_open_request;
+ }
+
+ const dummy_tab = {incognito: arguments[0]};
+ browser.tabs.getCurrent = () => Promise.resolve(dummy_tab);
+
+ init_settings_page();
+ }''',
+ incognito)
+
+ WebDriverWait(driver, 10)\
+ .until(EC.visibility_of_element_located((By.ID, 'indexeddb_error')))
+
+ assert driver.find_elements_by_id('loader') == []
+ assert not driver.find_element_by_id('main_view').is_displayed()
+
+ if incognito:
+ assert driver.find_element_by_id('indexeddb_private_mode_explanation')\
+ .is_displayed()