selenium-webdriver 3.0.1 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +92 -0
- package/NOTICE +1 -1
- package/README.md +11 -14
- package/chrome.js +125 -36
- package/edge.js +3 -3
- package/example/async_await_test.js +68 -0
- package/example/chrome_headless.js +47 -0
- package/example/chrome_mobile_emulation.js +1 -1
- package/example/firefox_channels.js +73 -0
- package/example/google_search_test.js +1 -1
- package/firefox/binary.js +114 -57
- package/firefox/extension.js +118 -81
- package/firefox/index.js +67 -135
- package/firefox/profile.js +13 -49
- package/ie.js +2 -2
- package/index.js +6 -3
- package/io/index.js +59 -0
- package/io/zip.js +214 -0
- package/lib/README +2 -3
- package/lib/actions.js +4 -4
- package/lib/atoms/getAttribute.js +9 -11
- package/lib/atoms/is-displayed.js +102 -0
- package/lib/by.js +9 -1
- package/lib/capabilities.js +5 -5
- package/lib/command.js +2 -0
- package/lib/error.js +29 -4
- package/lib/events.js +1 -1
- package/lib/firefox/webdriver.json +2 -1
- package/lib/http.js +84 -69
- package/lib/input.js +1 -1
- package/lib/promise.js +205 -81
- package/lib/symbols.js +1 -1
- package/lib/test/data/click_tests/disabled_element.html +12 -0
- package/lib/test/data/firefox/webextension.xpi +0 -0
- package/lib/test/data/key_logger.html +34 -0
- package/lib/test/data/nestedElements.html +10 -1
- package/lib/test/data/single_text_input.html +12 -0
- package/lib/test/index.js +9 -35
- package/lib/until.js +1 -1
- package/lib/webdriver.js +130 -34
- package/net/portprober.js +1 -1
- package/opera.js +2 -2
- package/package.json +2 -2
- package/phantomjs.js +2 -2
- package/remote/index.js +17 -11
- package/safari.js +47 -8
- package/test/firefox/extension_test.js +41 -17
- package/test/firefox/firefox_test.js +70 -23
- package/test/firefox/profile_test.js +14 -59
- package/test/http/http_test.js +18 -0
- package/test/{io_test.js → io/io_test.js} +40 -1
- package/test/io/zip_test.js +128 -0
- package/test/lib/by_test.js +21 -0
- package/test/lib/error_test.js +20 -1
- package/test/lib/http_test.js +41 -2
- package/test/lib/promise_test.js +19 -0
- package/test/lib/until_test.js +28 -13
- package/test/lib/webdriver_test.js +107 -3
- package/test/page_loading_test.js +0 -6
- package/test/rect_test.js +60 -0
- package/test/remote_test.js +3 -3
- package/test/session_test.js +68 -23
- package/testing/index.js +16 -5
- package/lib/atoms/isDisplayed.js +0 -106
- package/lib/firefox/amd64/libnoblur64.so +0 -0
- package/lib/firefox/i386/libnoblur.so +0 -0
- package/lib/firefox/webdriver.xpi +0 -0
package/firefox/binary.js
CHANGED
|
@@ -47,27 +47,17 @@ const NO_FOCUS_LIB_AMD64 = isDevMode ?
|
|
|
47
47
|
const X_IGNORE_NO_FOCUS_LIB = 'x_ignore_nofocus.so';
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
let foundBinary = null;
|
|
51
|
-
let foundDevBinary = null;
|
|
52
|
-
|
|
53
|
-
|
|
54
50
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* @param {boolean=} opt_dev Whether to find the Developer Edition.
|
|
51
|
+
* @param {string} file Path to the file to find, relative to the program files
|
|
52
|
+
* root.
|
|
58
53
|
* @return {!Promise<?string>} A promise for the located executable.
|
|
59
54
|
* The promise will resolve to {@code null} if Firefox was not found.
|
|
60
55
|
*/
|
|
61
|
-
function
|
|
62
|
-
|
|
56
|
+
function findInProgramFiles(file) {
|
|
57
|
+
let files = [
|
|
63
58
|
process.env['PROGRAMFILES'] || 'C:\\Program Files',
|
|
64
59
|
process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
|
|
65
|
-
].map(
|
|
66
|
-
if (opt_dev) {
|
|
67
|
-
return path.join(prefix, 'Firefox Developer Edition\\firefox.exe');
|
|
68
|
-
}
|
|
69
|
-
return path.join(prefix, 'Mozilla Firefox\\firefox.exe');
|
|
70
|
-
});
|
|
60
|
+
].map(prefix => path.join(prefix, file));
|
|
71
61
|
return io.exists(files[0]).then(function(exists) {
|
|
72
62
|
return exists ? files[0] : io.exists(files[1]).then(function(exists) {
|
|
73
63
|
return exists ? files[1] : null;
|
|
@@ -77,49 +67,104 @@ function defaultWindowsLocation(opt_dev) {
|
|
|
77
67
|
|
|
78
68
|
|
|
79
69
|
/**
|
|
80
|
-
*
|
|
70
|
+
* Provides methods for locating the executable for a Firefox release channel
|
|
71
|
+
* on Windows and MacOS. For other systems (i.e. Linux), Firefox will always
|
|
72
|
+
* be located on the system PATH.
|
|
81
73
|
*
|
|
82
|
-
* @
|
|
83
|
-
* used on Windows and OSX.
|
|
84
|
-
* @return {!Promise<string>} A promise for the located binary. The promise will
|
|
85
|
-
* be rejected if Firefox cannot be located.
|
|
74
|
+
* @final
|
|
86
75
|
*/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
class Channel {
|
|
77
|
+
/**
|
|
78
|
+
* @param {string} darwin The path to check when running on MacOS.
|
|
79
|
+
* @param {string} win32 The path to check when running on Windows.
|
|
80
|
+
*/
|
|
81
|
+
constructor(darwin, win32) {
|
|
82
|
+
/** @private @const */ this.darwin_ = darwin;
|
|
83
|
+
/** @private @const */ this.win32_ = win32;
|
|
84
|
+
/** @private {Promise<string>} */
|
|
85
|
+
this.found_ = null;
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Attempts to locate the Firefox executable for this release channel. This
|
|
90
|
+
* will first check the default installation location for the channel before
|
|
91
|
+
* checking the user's PATH. The returned promise will be rejected if Firefox
|
|
92
|
+
* can not be found.
|
|
93
|
+
*
|
|
94
|
+
* @return {!Promise<string>} A promise for the location of the located
|
|
95
|
+
* Firefox executable.
|
|
96
|
+
*/
|
|
97
|
+
locate() {
|
|
98
|
+
if (this.found_) {
|
|
99
|
+
return this.found_;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let found;
|
|
103
|
+
switch (process.platform) {
|
|
104
|
+
case 'darwin':
|
|
105
|
+
found = io.exists(this.darwin_)
|
|
106
|
+
.then(exists => exists ? this.darwin_ : io.findInPath('firefox'));
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'win32':
|
|
110
|
+
found = findInProgramFiles(this.win32_)
|
|
111
|
+
.then(found => found || io.findInPath('firefox.exe'));
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
default:
|
|
115
|
+
found = Promise.resolve(io.findInPath('firefox'));
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.found_ = found.then(found => {
|
|
120
|
+
if (found) {
|
|
121
|
+
// TODO: verify version info.
|
|
122
|
+
return found;
|
|
123
|
+
}
|
|
124
|
+
throw Error('Could not locate Firefox on the current system');
|
|
125
|
+
});
|
|
126
|
+
return this.found_;
|
|
94
127
|
}
|
|
128
|
+
}
|
|
95
129
|
|
|
96
|
-
let found;
|
|
97
|
-
if (process.platform === 'darwin') {
|
|
98
|
-
let exe = opt_dev
|
|
99
|
-
? '/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin'
|
|
100
|
-
: '/Applications/Firefox.app/Contents/MacOS/firefox-bin';
|
|
101
|
-
found = io.exists(exe).then(exists => exists ? exe : null);
|
|
102
130
|
|
|
103
|
-
|
|
104
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Firefox's developer channel.
|
|
133
|
+
* @const
|
|
134
|
+
* @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#aurora>
|
|
135
|
+
*/
|
|
136
|
+
Channel.AURORA = new Channel(
|
|
137
|
+
'/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin',
|
|
138
|
+
'Firefox Developer Edition\\firefox.exe');
|
|
105
139
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Firefox's beta channel. Note this is provided mainly for convenience as
|
|
142
|
+
* the beta channel has the same installation location as the main release
|
|
143
|
+
* channel.
|
|
144
|
+
* @const
|
|
145
|
+
* @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#beta>
|
|
146
|
+
*/
|
|
147
|
+
Channel.BETA = new Channel(
|
|
148
|
+
'/Applications/Firefox.app/Contents/MacOS/firefox-bin',
|
|
149
|
+
'Mozilla Firefox\\firefox.exe');
|
|
109
150
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Firefox's release channel.
|
|
153
|
+
* @const
|
|
154
|
+
* @see <https://www.mozilla.org/en-US/firefox/desktop/>
|
|
155
|
+
*/
|
|
156
|
+
Channel.RELEASE = new Channel(
|
|
157
|
+
'/Applications/Firefox.app/Contents/MacOS/firefox-bin',
|
|
158
|
+
'Mozilla Firefox\\firefox.exe');
|
|
116
159
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Firefox's nightly release channel.
|
|
162
|
+
* @const
|
|
163
|
+
* @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#nightly>
|
|
164
|
+
*/
|
|
165
|
+
Channel.NIGHTLY = new Channel(
|
|
166
|
+
'/Applications/FirefoxNightly.app/Contents/MacOS/firefox-bin',
|
|
167
|
+
'Nightly\\firefox.exe');
|
|
123
168
|
|
|
124
169
|
|
|
125
170
|
/**
|
|
@@ -151,20 +196,23 @@ function installNoFocusLibs(profileDir) {
|
|
|
151
196
|
* use with WebDriver.
|
|
152
197
|
*
|
|
153
198
|
* If created _without_ a path for the Firefox binary to use, this class will
|
|
154
|
-
* attempt to find Firefox when {@link #launch()} is called. For
|
|
199
|
+
* attempt to find Firefox when {@link #launch()} is called. For MacOS and
|
|
155
200
|
* Windows, this class will look for Firefox in the current platform's default
|
|
156
|
-
* installation location (e.g. /Applications/Firefox.app on
|
|
157
|
-
* platforms, the Firefox executable must be available on your system
|
|
201
|
+
* installation location (e.g. /Applications/Firefox.app on MacOS). For all
|
|
202
|
+
* other platforms, the Firefox executable must be available on your system
|
|
203
|
+
* `PATH`.
|
|
158
204
|
*
|
|
159
205
|
* @final
|
|
160
206
|
*/
|
|
161
207
|
class Binary {
|
|
162
208
|
/**
|
|
163
|
-
* @param {string=}
|
|
209
|
+
* @param {?(string|Channel)=} opt_exeOrChannel Either the path to a specific
|
|
210
|
+
* Firefox binary to use, or a {@link Channel} instance that describes
|
|
211
|
+
* how to locate the desired Firefox version.
|
|
164
212
|
*/
|
|
165
|
-
constructor(
|
|
166
|
-
/** @private {(string|
|
|
167
|
-
this.exe_ =
|
|
213
|
+
constructor(opt_exeOrChannel) {
|
|
214
|
+
/** @private {?(string|Channel)} */
|
|
215
|
+
this.exe_ = opt_exeOrChannel || null;
|
|
168
216
|
|
|
169
217
|
/** @private {!Array.<string>} */
|
|
170
218
|
this.args_ = [];
|
|
@@ -187,7 +235,7 @@ class Binary {
|
|
|
187
235
|
* on the current system.
|
|
188
236
|
*/
|
|
189
237
|
getExe() {
|
|
190
|
-
return this.exe_;
|
|
238
|
+
return typeof this.exe_ === 'string' ? this.exe_ : undefined;
|
|
191
239
|
}
|
|
192
240
|
|
|
193
241
|
/**
|
|
@@ -223,6 +271,8 @@ class Binary {
|
|
|
223
271
|
*
|
|
224
272
|
* @param {boolean=} opt_use Whether to use the developer edition. Defaults to
|
|
225
273
|
* true.
|
|
274
|
+
* @deprecated Use the {@link Channel} class to indicate the desired Firefox
|
|
275
|
+
* version when creating a new binary: `new Binary(Channel.AURORA)`.
|
|
226
276
|
*/
|
|
227
277
|
useDevEdition(opt_use) {
|
|
228
278
|
this.devEdition_ = opt_use === undefined || !!opt_use;
|
|
@@ -238,7 +288,13 @@ class Binary {
|
|
|
238
288
|
* used by this instance.
|
|
239
289
|
*/
|
|
240
290
|
locate() {
|
|
241
|
-
|
|
291
|
+
if (typeof this.exe_ === 'string') {
|
|
292
|
+
return Promise.resolve(this.exe_);
|
|
293
|
+
} else if (this.exe_ instanceof Channel) {
|
|
294
|
+
return this.exe_.locate();
|
|
295
|
+
}
|
|
296
|
+
let channel = this.devEdition_ ? Channel.AURORA : Channel.RELEASE;
|
|
297
|
+
return channel.locate();
|
|
242
298
|
}
|
|
243
299
|
|
|
244
300
|
/**
|
|
@@ -284,4 +340,5 @@ class Binary {
|
|
|
284
340
|
|
|
285
341
|
|
|
286
342
|
exports.Binary = Binary;
|
|
343
|
+
exports.Channel = Channel;
|
|
287
344
|
|
package/firefox/extension.js
CHANGED
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
|
|
20
20
|
'use strict';
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
fs = require('fs'),
|
|
22
|
+
const fs = require('fs'),
|
|
24
23
|
path = require('path'),
|
|
25
24
|
xml = require('xml2js');
|
|
26
25
|
|
|
27
26
|
const io = require('../io');
|
|
27
|
+
const zip = require('../io/zip');
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -56,11 +56,7 @@ function install(extension, dir) {
|
|
|
56
56
|
if (!details.unpack) {
|
|
57
57
|
return io.copy(extension, dst + '.xpi').then(() => details.id);
|
|
58
58
|
} else {
|
|
59
|
-
return
|
|
60
|
-
// TODO: find an async library for inflating a zip archive.
|
|
61
|
-
new AdmZip(extension).extractAllTo(dst, true);
|
|
62
|
-
return details.id;
|
|
63
|
-
});
|
|
59
|
+
return zip.unzip(extension, dst).then(() => details.id);
|
|
64
60
|
}
|
|
65
61
|
} else {
|
|
66
62
|
return io.copyDir(extension, dst).then(() => details.id);
|
|
@@ -79,105 +75,146 @@ var AddonDetails;
|
|
|
79
75
|
var RdfRoot;
|
|
80
76
|
|
|
81
77
|
|
|
82
|
-
|
|
83
78
|
/**
|
|
84
79
|
* Extracts the details needed to install an add-on.
|
|
85
80
|
* @param {string} addonPath Path to the extension directory.
|
|
86
81
|
* @return {!Promise<!AddonDetails>} A promise for the add-on details.
|
|
87
82
|
*/
|
|
88
83
|
function getDetails(addonPath) {
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
id: getNodeText(description, em + 'id'),
|
|
97
|
-
name: getNodeText(description, em + 'name'),
|
|
98
|
-
version: getNodeText(description, em + 'version'),
|
|
99
|
-
unpack: getNodeText(description, em + 'unpack') || false
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
if (typeof details.unpack === 'string') {
|
|
103
|
-
details.unpack = details.unpack.toLowerCase() === 'true';
|
|
84
|
+
return io.stat(addonPath).then((stats) => {
|
|
85
|
+
if (stats.isDirectory()) {
|
|
86
|
+
return parseDirectory(addonPath);
|
|
87
|
+
} else if (addonPath.slice(-4) === '.xpi') {
|
|
88
|
+
return parseXpiFile(addonPath);
|
|
89
|
+
} else {
|
|
90
|
+
throw Error('Add-on path is not an xpi or a directory: ' + addonPath);
|
|
104
91
|
}
|
|
92
|
+
});
|
|
105
93
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Parse an install.rdf for a Firefox add-on.
|
|
96
|
+
* @param {string} rdf The contents of install.rdf for the add-on.
|
|
97
|
+
* @return {!Promise<!AddonDetails>} A promise for the add-on details.
|
|
98
|
+
*/
|
|
99
|
+
function parseInstallRdf(rdf) {
|
|
100
|
+
return parseXml(rdf).then(function(doc) {
|
|
101
|
+
var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#');
|
|
102
|
+
var rdf = getNamespaceId(
|
|
103
|
+
doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
|
|
104
|
+
|
|
105
|
+
var description = doc[rdf + 'RDF'][rdf + 'Description'][0];
|
|
106
|
+
var details = {
|
|
107
|
+
id: getNodeText(description, em + 'id'),
|
|
108
|
+
name: getNodeText(description, em + 'name'),
|
|
109
|
+
version: getNodeText(description, em + 'version'),
|
|
110
|
+
unpack: getNodeText(description, em + 'unpack') || false
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if (typeof details.unpack === 'string') {
|
|
114
|
+
details.unpack = details.unpack.toLowerCase() === 'true';
|
|
115
|
+
}
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
if (!details.id) {
|
|
118
|
+
throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
|
|
119
|
+
}
|
|
112
120
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
121
|
+
return details;
|
|
122
|
+
});
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
function parseXml(text) {
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
xml.parseString(text, (err, data) => {
|
|
127
|
+
if (err) {
|
|
128
|
+
reject(err);
|
|
129
|
+
} else {
|
|
130
|
+
resolve(data);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (namespaces[ns] !== url) {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
136
|
+
function getNodeText(node, name) {
|
|
137
|
+
return node[name] && node[name][0] || '';
|
|
138
|
+
}
|
|
129
139
|
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
function getNamespaceId(doc, url) {
|
|
141
|
+
var keys = Object.keys(doc);
|
|
142
|
+
if (keys.length !== 1) {
|
|
143
|
+
throw new AddonFormatError('Malformed manifest for add-on ' + addonPath);
|
|
132
144
|
}
|
|
133
|
-
return true;
|
|
134
|
-
});
|
|
135
|
-
return id;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
145
|
|
|
146
|
+
var namespaces = /** @type {!RdfRoot} */(doc[keys[0]]).$;
|
|
147
|
+
var id = '';
|
|
148
|
+
Object.keys(namespaces).some(function(ns) {
|
|
149
|
+
if (namespaces[ns] !== url) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
139
152
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
153
|
+
if (ns.indexOf(':') != -1) {
|
|
154
|
+
id = ns.split(':')[1] + ':';
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
});
|
|
158
|
+
return id;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Parse a manifest for a Firefox WebExtension.
|
|
164
|
+
* @param {{
|
|
165
|
+
* name: string,
|
|
166
|
+
* version: string,
|
|
167
|
+
* applications: {gecko:{id:string}}
|
|
168
|
+
* }} json JSON representation of the manifest.
|
|
169
|
+
* @return {!AddonDetails} The add-on details.
|
|
170
|
+
*/
|
|
171
|
+
function parseManifestJson({name, version, applications}) {
|
|
172
|
+
if (!(applications && applications.gecko && applications.gecko.id)) {
|
|
173
|
+
throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
|
|
174
|
+
}
|
|
147
175
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
let zip = new AdmZip(addonPath);
|
|
176
|
+
return {id: applications.gecko.id, name, version, unpack: false};
|
|
177
|
+
}
|
|
151
178
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return
|
|
179
|
+
function parseXpiFile(filePath) {
|
|
180
|
+
return zip.load(filePath).then(archive => {
|
|
181
|
+
if (archive.has('install.rdf')) {
|
|
182
|
+
return archive.getFile('install.rdf')
|
|
183
|
+
.then(buf => parseInstallRdf(buf.toString('utf8')));
|
|
156
184
|
}
|
|
157
185
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (!stats.isDirectory()) {
|
|
163
|
-
throw Error(
|
|
164
|
-
'Add-on path is niether a xpi nor a directory: ' + addonPath);
|
|
186
|
+
if (archive.has('manifest.json')) {
|
|
187
|
+
return archive.getFile('manifest.json')
|
|
188
|
+
.then(buf => JSON.parse(buf.toString('utf8')))
|
|
189
|
+
.then(parseManifestJson);
|
|
165
190
|
}
|
|
166
|
-
|
|
191
|
+
|
|
192
|
+
throw new AddonFormatError(
|
|
193
|
+
`Couldn't find install.rdf or manifest.json in ${filePath}`);
|
|
167
194
|
});
|
|
168
195
|
}
|
|
169
196
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
197
|
+
function parseDirectory(dirPath) {
|
|
198
|
+
const rdfPath = path.join(dirPath, 'install.rdf');
|
|
199
|
+
const jsonPath = path.join(dirPath, 'manifest.json');
|
|
200
|
+
return io.exists(rdfPath)
|
|
201
|
+
.then(rdfExists => {
|
|
202
|
+
if (rdfExists) {
|
|
203
|
+
return io.read(rdfPath)
|
|
204
|
+
.then(buf => parseInstallRdf(buf.toString('utf8')));
|
|
205
|
+
}
|
|
206
|
+
return io.exists(jsonPath)
|
|
207
|
+
.then(jsonExists => {
|
|
208
|
+
if (jsonExists) {
|
|
209
|
+
return io.read(jsonPath)
|
|
210
|
+
.then(buf => JSON.parse(buf.toString('utf8')))
|
|
211
|
+
.then(parseManifestJson);
|
|
212
|
+
}
|
|
213
|
+
throw new AddonFormatError(
|
|
214
|
+
`Couldn't find install.rdf or manifest.json in ${dirPath}`);
|
|
215
|
+
});
|
|
216
|
+
})
|
|
217
|
+
}
|
|
181
218
|
}
|
|
182
219
|
|
|
183
220
|
|