Project

General

Profile

Download (5.42 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / test / extension_crafting.py @ 26e4800d

1
# SPDX-License-Identifier: GPL-3.0-or-later
2

    
3
"""
4
Making temporary WebExtensions for use in the test suite
5
"""
6

    
7
# This file is part of Haketilo.
8
#
9
# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org>
10
#
11
# This program is free software: you can redistribute it and/or modify
12
# it under the terms of the GNU General Public License as published by
13
# the Free Software Foundation, either version 3 of the License, or
14
# (at your option) any later version.
15
#
16
# This program is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
# GNU General Public License for more details.
20
#
21
# You should have received a copy of the GNU General Public License
22
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
#
24
# I, Wojtek Kosior, thereby promise not to sue for violation of this file's
25
# license. Although I request that you do not make use of this code in a
26
# proprietary program, I am not going to enforce this in court.
27

    
28
import json
29
import zipfile
30
from pathlib import Path
31
from uuid import uuid4
32
from tempfile import TemporaryDirectory
33
import shutil
34
import subprocess
35

    
36
from .misc_constants import *
37

    
38
class ManifestTemplateValueToFill:
39
    pass
40

    
41
def manifest_template():
42
    return {
43
        'manifest_version': 2,
44
        'name': 'Haketilo test extension',
45
        'version': '1.0',
46
        'applications': {
47
	    'gecko': {
48
	        'id': ManifestTemplateValueToFill(),
49
	        'strict_min_version': '60.0'
50
	    }
51
        },
52
        'permissions': [
53
	    'contextMenus',
54
	    'webRequest',
55
	    'webRequestBlocking',
56
	    'activeTab',
57
	    'notifications',
58
	    'sessions',
59
	    'storage',
60
	    'tabs',
61
	    '<all_urls>',
62
	    'unlimitedStorage'
63
        ],
64
        'content_security_policy': "object-src 'none'; script-src 'self' https://serve.scrip.ts;",
65
        'web_accessible_resources': ['testpage.html'],
66
        'options_ui': {
67
	    'page': 'testpage.html',
68
	    'open_in_tab': True
69
        },
70
        'background': {
71
	    'persistent': True,
72
	    'scripts': ['__open_test_page.js', 'background.js']
73
        },
74
        'content_scripts': [
75
	    {
76
	        'run_at': 'document_start',
77
	        'matches': ['<all_urls>'],
78
	        'match_about_blank': True,
79
	        'all_frames': True,
80
	        'js': ['content.js']
81
	    }
82
        ]
83
    }
84

    
85
class ExtraHTML:
86
    def __init__(self, html_path, append={}, wrap_into_htmldoc=True):
87
        self.html_path = html_path
88
        self.append = append
89
        self.wrap_into_htmldoc = wrap_into_htmldoc
90

    
91
    def add_to_xpi(self, xpi, tmpdir=None):
92
        if tmpdir is None:
93
            with TemporaryDirectory() as tmpdir:
94
                return self.add_to_xpi(xpi, tmpdir)
95

    
96
        append_flags = []
97
        for filename, code in self.append.items():
98
            append_flags.extend(['-A', f'{filename}:{code}'])
99

    
100
        awk = subprocess.run(
101
            ['awk', '-f', awk_script_name, '--', *unit_test_defines,
102
             *append_flags, '-H', self.html_path, '--write-js-deps',
103
             '--output=files-to-copy', f'--output-dir={tmpdir}'],
104
            stdout=subprocess.PIPE, cwd=script_root, check=True
105
        )
106

    
107
        for path in filter(None, awk.stdout.decode().split('\n')):
108
            xpi.write(script_root / path, path)
109

    
110
        tmpdir = Path(tmpdir)
111
        for path in tmpdir.rglob('*'):
112
            relpath = str(path.relative_to(tmpdir))
113
            if not path.is_dir() and relpath != self.html_path:
114
                xpi.write(path, relpath)
115

    
116
        with open(tmpdir / self.html_path, 'rt') as html_file:
117
            html = html_file.read()
118
            if self.wrap_into_htmldoc:
119
                html = f'<!DOCTYPE html><html><body>{html}</body></html>'
120
            xpi.writestr(self.html_path, html)
121

    
122
default_background_script = ''
123
default_content_script = ''
124
default_test_page = '''
125
<!DOCTYPE html>
126
<html>
127
  <head>
128
    <title>Extension's options page for testing</title>
129
  </head>
130
  <body>
131
    <h1>Extension's options page for testing</h1>
132
  </body>
133
</html>
134
'''
135

    
136
open_test_page_script = '''(() => {
137
const page_url = browser.runtime.getURL("testpage.html");
138
const execute_details = {
139
    code: `window.wrappedJSObject.ext_page_url=${JSON.stringify(page_url)};`
140
};
141
browser.tabs.query({currentWindow: true, active: true})
142
    .then(t => browser.tabs.executeScript(t.id, execute_details));
143
})();'''
144

    
145
def make_extension(destination_dir,
146
                   background_script=default_background_script,
147
                   content_script=default_content_script,
148
                   test_page=default_test_page,
149
                   extra_files={}, extra_html=[]):
150
    if not hasattr(extra_html, '__iter__'):
151
        extra_html = [extra_html]
152
    manifest = manifest_template()
153
    extension_id = '{%s}' % uuid4()
154
    manifest['applications']['gecko']['id'] = extension_id
155
    files = {
156
        'manifest.json'      : json.dumps(manifest),
157
        '__open_test_page.js': open_test_page_script,
158
        'background.js'      : background_script,
159
        'content.js'         : content_script,
160
        'testpage.html'      : test_page,
161
        **extra_files
162
    }
163
    destination_path = destination_dir / f'{extension_id}.xpi'
164
    with zipfile.ZipFile(destination_path, 'x') as xpi:
165
        for filename, contents in files.items():
166
            if hasattr(contents, '__call__'):
167
                contents = contents()
168
            xpi.writestr(filename, contents)
169
        for html in extra_html:
170
            html.add_to_xpi(xpi)
171

    
172
    return destination_path
(4-4/11)