UNPKG

8.75 kBJavaScriptView Raw
1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18/**
19 * @fileoverview Manages Firefox binaries. This module is considered internal;
20 * users should use {@link ./firefox selenium-webdriver/firefox}.
21 */
22
23'use strict';
24
25const child = require('child_process'),
26 fs = require('fs'),
27 path = require('path'),
28 util = require('util');
29
30const isDevMode = require('../lib/devmode'),
31 Symbols = require('../lib/symbols'),
32 io = require('../io'),
33 exec = require('../io/exec');
34
35
36
37/** @const */
38const NO_FOCUS_LIB_X86 = isDevMode ?
39 path.join(__dirname, '../../../../cpp/prebuilt/i386/libnoblur.so') :
40 path.join(__dirname, '../lib/firefox/i386/libnoblur.so') ;
41
42/** @const */
43const NO_FOCUS_LIB_AMD64 = isDevMode ?
44 path.join(__dirname, '../../../../cpp/prebuilt/amd64/libnoblur64.so') :
45 path.join(__dirname, '../lib/firefox/amd64/libnoblur64.so') ;
46
47const X_IGNORE_NO_FOCUS_LIB = 'x_ignore_nofocus.so';
48
49
50let foundBinary = null;
51let foundDevBinary = null;
52
53
54/**
55 * Checks the default Windows Firefox locations in Program Files.
56 *
57 * @param {boolean=} opt_dev Whether to find the Developer Edition.
58 * @return {!Promise<?string>} A promise for the located executable.
59 * The promise will resolve to {@code null} if Firefox was not found.
60 */
61function defaultWindowsLocation(opt_dev) {
62 var files = [
63 process.env['PROGRAMFILES'] || 'C:\\Program Files',
64 process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
65 ].map(function(prefix) {
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 });
71 return io.exists(files[0]).then(function(exists) {
72 return exists ? files[0] : io.exists(files[1]).then(function(exists) {
73 return exists ? files[1] : null;
74 });
75 });
76}
77
78
79/**
80 * Locates the Firefox binary for the current system.
81 *
82 * @param {boolean=} opt_dev Whether to find the Developer Edition. This only
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.
86 */
87function findFirefox(opt_dev) {
88 if (opt_dev && foundDevBinary) {
89 return foundDevBinary;
90 }
91
92 if (!opt_dev && foundBinary) {
93 return foundBinary;
94 }
95
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
103 } else if (process.platform === 'win32') {
104 found = defaultWindowsLocation(opt_dev);
105
106 } else {
107 found = Promise.resolve(io.findInPath('firefox'));
108 }
109
110 found = found.then(found => {
111 if (found) {
112 return found;
113 }
114 throw Error('Could not locate Firefox on the current system');
115 });
116
117 if (opt_dev) {
118 return foundDevBinary = found;
119 } else {
120 return foundBinary = found;
121 }
122}
123
124
125/**
126 * Copies the no focus libs into the given profile directory.
127 * @param {string} profileDir Path to the profile directory to install into.
128 * @return {!Promise<string>} The LD_LIBRARY_PATH prefix string to use
129 * for the installed libs.
130 */
131function installNoFocusLibs(profileDir) {
132 var x86 = path.join(profileDir, 'x86');
133 var amd64 = path.join(profileDir, 'amd64');
134
135 return io.mkdir(x86)
136 .then(() => copyLib(NO_FOCUS_LIB_X86, x86))
137 .then(() => io.mkdir(amd64))
138 .then(() => copyLib(NO_FOCUS_LIB_AMD64, amd64))
139 .then(function() {
140 return x86 + ':' + amd64;
141 });
142
143 function copyLib(src, dir) {
144 return io.copy(src, path.join(dir, X_IGNORE_NO_FOCUS_LIB));
145 }
146}
147
148
149/**
150 * Provides a mechanism to configure and launch Firefox in a subprocess for
151 * use with WebDriver.
152 *
153 * 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 OSX and
155 * Windows, this class will look for Firefox in the current platform's default
156 * installation location (e.g. /Applications/Firefox.app on OSX). For all other
157 * platforms, the Firefox executable must be available on your system `PATH`.
158 *
159 * @final
160 */
161class Binary {
162 /**
163 * @param {string=} opt_exe Path to the Firefox binary to use.
164 */
165 constructor(opt_exe) {
166 /** @private {(string|undefined)} */
167 this.exe_ = opt_exe;
168
169 /** @private {!Array.<string>} */
170 this.args_ = [];
171
172 /** @private {!Object<string, string>} */
173 this.env_ = {};
174 Object.assign(this.env_, process.env, {
175 MOZ_CRASHREPORTER_DISABLE: '1',
176 MOZ_NO_REMOTE: '1',
177 NO_EM_RESTART: '1'
178 });
179
180 /** @private {boolean} */
181 this.devEdition_ = false;
182 }
183
184 /**
185 * @return {(string|undefined)} The path to the Firefox executable to use, or
186 * `undefined` if WebDriver should attempt to locate Firefox automatically
187 * on the current system.
188 */
189 getExe() {
190 return this.exe_;
191 }
192
193 /**
194 * Add arguments to the command line used to start Firefox.
195 * @param {...(string|!Array.<string>)} var_args Either the arguments to add
196 * as varargs, or the arguments as an array.
197 */
198 addArguments(var_args) {
199 for (var i = 0; i < arguments.length; i++) {
200 if (Array.isArray(arguments[i])) {
201 this.args_ = this.args_.concat(arguments[i]);
202 } else {
203 this.args_.push(arguments[i]);
204 }
205 }
206 }
207
208 /**
209 * @return {!Array<string>} The command line arguments to use when starting
210 * the browser.
211 */
212 getArguments() {
213 return this.args_;
214 }
215
216 /**
217 * Specifies whether to use Firefox Developer Edition instead of the normal
218 * stable channel. Setting this option has no effect if this instance was
219 * created with a path to a specific Firefox binary.
220 *
221 * This method has no effect on Unix systems where the Firefox application
222 * has the same (default) name regardless of version.
223 *
224 * @param {boolean=} opt_use Whether to use the developer edition. Defaults to
225 * true.
226 */
227 useDevEdition(opt_use) {
228 this.devEdition_ = opt_use === undefined || !!opt_use;
229 }
230
231 /**
232 * Returns a promise for the Firefox executable used by this instance. The
233 * returned promise will be immediately resolved if the user supplied an
234 * executable path when this instance was created. Otherwise, an attempt will
235 * be made to find Firefox on the current system.
236 *
237 * @return {!Promise<string>} a promise for the path to the Firefox executable
238 * used by this instance.
239 */
240 locate() {
241 return Promise.resolve(this.exe_ || findFirefox(this.devEdition_));
242 }
243
244 /**
245 * Launches Firefox and returns a promise that will be fulfilled when the
246 * process terminates.
247 * @param {string} profile Path to the profile directory to use.
248 * @return {!Promise<!exec.Command>} A promise for the handle to the started
249 * subprocess.
250 */
251 launch(profile) {
252 let env = {};
253 Object.assign(env, this.env_, {XRE_PROFILE_PATH: profile});
254
255 let args = ['-foreground'].concat(this.args_);
256
257 return this.locate().then(function(firefox) {
258 if (process.platform === 'win32' || process.platform === 'darwin') {
259 return exec(firefox, {args: args, env: env});
260 }
261 return installNoFocusLibs(profile).then(function(ldLibraryPath) {
262 env['LD_LIBRARY_PATH'] = ldLibraryPath + ':' + env['LD_LIBRARY_PATH'];
263 env['LD_PRELOAD'] = X_IGNORE_NO_FOCUS_LIB;
264 return exec(firefox, {args: args, env: env});
265 });
266 });
267 }
268
269 /**
270 * Returns a promise for the wire representation of this binary. Note: the
271 * FirefoxDriver only supports passing the path to the binary executable over
272 * the wire; all command line arguments and environment variables will be
273 * discarded.
274 *
275 * @return {!Promise<string>} A promise for this binary's wire representation.
276 */
277 [Symbols.serialize]() {
278 return this.locate();
279 }
280}
281
282
283// PUBLIC API
284
285
286exports.Binary = Binary;
287