Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/rust/src/edge.rs
2885 views
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
use crate::config::ManagerConfig;
19
use crate::config::ARCH::{ARM64, X32};
20
use crate::config::OS::{LINUX, MACOS, WINDOWS};
21
use crate::downloads::{parse_json_from_url, read_version_from_link};
22
use crate::files::{compose_driver_path_in_cache, BrowserPath};
23
use crate::metadata::{
24
create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata,
25
};
26
use crate::{
27
create_http_client, get_binary_extension, path_to_string, Logger, SeleniumManager, BETA,
28
DASH_DASH_VERSION, DEV, ENV_PROGRAM_FILES_X86, NIGHTLY, OFFLINE_REQUEST_ERR_MSG, REG_PV_ARG,
29
REG_VERSION_ARG, STABLE,
30
};
31
use anyhow::Error;
32
use reqwest::Client;
33
use serde::{Deserialize, Serialize};
34
use std::collections::HashMap;
35
use std::env;
36
use std::path::{Path, PathBuf};
37
use std::sync::mpsc;
38
use std::sync::mpsc::{Receiver, Sender};
39
40
pub const EDGE_NAMES: &[&str] = &[
41
"edge",
42
EDGE_WINDOWS_AND_LINUX_APP_NAME,
43
"microsoftedge",
44
WEBVIEW2_NAME,
45
];
46
pub const EDGEDRIVER_NAME: &str = "msedgedriver";
47
pub const WEBVIEW2_NAME: &str = "webview2";
48
const DRIVER_URL: &str = "https://msedgedriver.microsoft.com/";
49
const LATEST_STABLE: &str = "LATEST_STABLE";
50
const LATEST_RELEASE: &str = "LATEST_RELEASE";
51
const BROWSER_URL: &str = "https://edgeupdates.microsoft.com/api/products/";
52
const MIN_EDGE_VERSION_DOWNLOAD: i32 = 113;
53
const EDGE_WINDOWS_AND_LINUX_APP_NAME: &str = "msedge";
54
const EDGE_MACOS_APP_NAME: &str = "Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
55
const EDGE_BETA_MACOS_APP_NAME: &str = "Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta";
56
const EDGE_DEV_MACOS_APP_NAME: &str = "Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev";
57
const EDGE_CANARY_MACOS_APP_NAME: &str =
58
"Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary";
59
60
pub struct EdgeManager {
61
pub browser_name: &'static str,
62
pub driver_name: &'static str,
63
pub config: ManagerConfig,
64
pub http_client: Client,
65
pub log: Logger,
66
pub tx: Sender<String>,
67
pub rx: Receiver<String>,
68
pub download_browser: bool,
69
pub browser_url: Option<String>,
70
}
71
72
impl EdgeManager {
73
pub fn new() -> Result<Box<Self>, Error> {
74
Self::new_with_name(EDGE_NAMES[0].to_string())
75
}
76
77
pub fn new_with_name(browser_name: String) -> Result<Box<Self>, Error> {
78
let static_browser_name: &str = Box::leak(browser_name.into_boxed_str());
79
let driver_name = EDGEDRIVER_NAME;
80
let config = ManagerConfig::default(static_browser_name, driver_name);
81
let default_timeout = config.timeout.to_owned();
82
let default_proxy = &config.proxy;
83
let (tx, rx): (Sender<String>, Receiver<String>) = mpsc::channel();
84
Ok(Box::new(EdgeManager {
85
browser_name: static_browser_name,
86
driver_name,
87
http_client: create_http_client(default_timeout, default_proxy)?,
88
config,
89
log: Logger::new(),
90
tx,
91
rx,
92
download_browser: false,
93
browser_url: None,
94
}))
95
}
96
}
97
98
impl SeleniumManager for EdgeManager {
99
fn get_browser_name(&self) -> &str {
100
self.browser_name
101
}
102
103
fn get_browser_names_in_path(&self) -> Vec<&str> {
104
vec![self.get_browser_name()]
105
}
106
107
fn get_http_client(&self) -> &Client {
108
&self.http_client
109
}
110
111
fn set_http_client(&mut self, http_client: Client) {
112
self.http_client = http_client;
113
}
114
115
fn get_browser_path_map(&self) -> HashMap<BrowserPath, &str> {
116
if self.is_webview2() {
117
HashMap::from([(
118
BrowserPath::new(WINDOWS, STABLE),
119
r"Microsoft\EdgeWebView\Application",
120
)])
121
} else {
122
HashMap::from([
123
(
124
BrowserPath::new(WINDOWS, STABLE),
125
r"Microsoft\Edge\Application\msedge.exe",
126
),
127
(
128
BrowserPath::new(WINDOWS, BETA),
129
r"Microsoft\Edge Beta\Application\msedge.exe",
130
),
131
(
132
BrowserPath::new(WINDOWS, DEV),
133
r"Microsoft\Edge Dev\Application\msedge.exe",
134
),
135
(
136
BrowserPath::new(WINDOWS, NIGHTLY),
137
r"Microsoft\Edge SxS\Application\msedge.exe",
138
),
139
(
140
BrowserPath::new(MACOS, STABLE),
141
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
142
),
143
(
144
BrowserPath::new(MACOS, BETA),
145
"/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta",
146
),
147
(
148
BrowserPath::new(MACOS, DEV),
149
"/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev",
150
),
151
(
152
BrowserPath::new(MACOS, NIGHTLY),
153
"/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary",
154
),
155
(BrowserPath::new(LINUX, STABLE), "/usr/bin/microsoft-edge"),
156
(
157
BrowserPath::new(LINUX, BETA),
158
"/usr/bin/microsoft-edge-beta",
159
),
160
(BrowserPath::new(LINUX, DEV), "/usr/bin/microsoft-edge-dev"),
161
])
162
}
163
}
164
165
fn discover_browser_version(&mut self) -> Result<Option<String>, Error> {
166
let (reg_key, reg_version_arg, cmd_version_arg) = if self.is_webview2() {
167
let arch = self.get_arch();
168
if X32.is(arch) {
169
(
170
r"HKLM\SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}",
171
REG_PV_ARG,
172
"",
173
)
174
} else {
175
(
176
r"HKLM\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}",
177
REG_PV_ARG,
178
"",
179
)
180
}
181
} else {
182
(
183
r"HKCU\Software\Microsoft\Edge\BLBeacon",
184
REG_VERSION_ARG,
185
DASH_DASH_VERSION,
186
)
187
};
188
self.general_discover_browser_version(reg_key, reg_version_arg, cmd_version_arg)
189
}
190
191
fn get_driver_name(&self) -> &str {
192
self.driver_name
193
}
194
195
fn request_driver_version(&mut self) -> Result<String, Error> {
196
let mut major_browser_version = self.get_major_browser_version();
197
let cache_path = self.get_cache_path()?;
198
let mut metadata = get_metadata(self.get_logger(), &cache_path);
199
200
match get_driver_version_from_metadata(
201
&metadata.drivers,
202
self.driver_name,
203
major_browser_version.as_str(),
204
) {
205
Some(driver_version) => {
206
self.log.trace(format!(
207
"Driver TTL is valid. Getting {} version from metadata",
208
&self.driver_name
209
));
210
Ok(driver_version)
211
}
212
_ => {
213
self.assert_online_or_err(OFFLINE_REQUEST_ERR_MSG)?;
214
215
if self.is_browser_version_stable()
216
|| major_browser_version.is_empty()
217
|| self.is_browser_version_unstable()
218
{
219
let latest_stable_url = format!(
220
"{}{}",
221
self.get_driver_mirror_url_or_default(DRIVER_URL),
222
LATEST_STABLE
223
);
224
self.log.debug(format!(
225
"Reading {} latest version from {}",
226
&self.driver_name, latest_stable_url
227
));
228
let latest_driver_version = read_version_from_link(
229
self.get_http_client(),
230
&latest_stable_url,
231
self.get_logger(),
232
)?;
233
major_browser_version = self.get_major_version(&latest_driver_version)?;
234
self.log.debug(format!(
235
"Latest {} major version is {}",
236
&self.driver_name, major_browser_version
237
));
238
}
239
let driver_url = format!(
240
"{}{}_{}_{}",
241
self.get_driver_mirror_url_or_default(DRIVER_URL),
242
LATEST_RELEASE,
243
major_browser_version,
244
self.get_os().to_uppercase()
245
);
246
self.log.debug(format!(
247
"Reading {} version from {}",
248
&self.driver_name, driver_url
249
));
250
let driver_version =
251
read_version_from_link(self.get_http_client(), &driver_url, self.get_logger())?;
252
253
let driver_ttl = self.get_ttl();
254
if driver_ttl > 0 && !major_browser_version.is_empty() {
255
metadata.drivers.push(create_driver_metadata(
256
major_browser_version.as_str(),
257
self.driver_name,
258
&driver_version,
259
driver_ttl,
260
));
261
write_metadata(&metadata, self.get_logger(), cache_path);
262
}
263
264
Ok(driver_version)
265
}
266
}
267
}
268
269
fn request_browser_version(&mut self) -> Result<Option<String>, Error> {
270
Ok(None)
271
}
272
273
fn get_driver_url(&mut self) -> Result<String, Error> {
274
let driver_version = self.get_driver_version();
275
let os = self.get_os();
276
let arch = self.get_arch();
277
let driver_label = if WINDOWS.is(os) {
278
if ARM64.is(arch) {
279
"arm64"
280
} else if X32.is(arch) {
281
"win32"
282
} else {
283
"win64"
284
}
285
} else if MACOS.is(os) {
286
if ARM64.is(arch) {
287
"mac64_m1"
288
} else {
289
"mac64"
290
}
291
} else {
292
"linux64"
293
};
294
Ok(format!(
295
"{}{}/edgedriver_{}.zip",
296
self.get_driver_mirror_url_or_default(DRIVER_URL),
297
driver_version,
298
driver_label
299
))
300
}
301
302
fn get_driver_path_in_cache(&self) -> Result<PathBuf, Error> {
303
Ok(compose_driver_path_in_cache(
304
self.get_cache_path()?.unwrap_or_default(),
305
self.driver_name,
306
self.get_os(),
307
self.get_platform_label(),
308
self.get_driver_version(),
309
))
310
}
311
312
fn get_config(&self) -> &ManagerConfig {
313
&self.config
314
}
315
316
fn get_config_mut(&mut self) -> &mut ManagerConfig {
317
&mut self.config
318
}
319
320
fn set_config(&mut self, config: ManagerConfig) {
321
self.config = config;
322
}
323
324
fn get_logger(&self) -> &Logger {
325
&self.log
326
}
327
328
fn set_logger(&mut self, log: Logger) {
329
self.log = log;
330
}
331
332
fn get_sender(&self) -> &Sender<String> {
333
&self.tx
334
}
335
336
fn get_receiver(&self) -> &Receiver<String> {
337
&self.rx
338
}
339
340
fn get_platform_label(&self) -> &str {
341
let os = self.get_os();
342
let arch = self.get_arch();
343
if WINDOWS.is(os) {
344
if ARM64.is(arch) {
345
"win-arm64"
346
} else if X32.is(arch) {
347
"win32"
348
} else {
349
"win64"
350
}
351
} else if MACOS.is(os) {
352
if ARM64.is(arch) {
353
"mac-arm64"
354
} else {
355
"mac64"
356
}
357
} else {
358
"linux64"
359
}
360
}
361
362
fn request_latest_browser_version_from_online(
363
&mut self,
364
browser_version: &str,
365
) -> Result<String, Error> {
366
let browser_name = self.browser_name;
367
let is_fixed_browser_version = !self.is_empty(browser_version)
368
&& !self.is_stable(browser_version)
369
&& !self.is_unstable(browser_version);
370
let browser_url = self.get_browser_mirror_url_or_default(BROWSER_URL);
371
let edge_updates_url = if is_fixed_browser_version {
372
format!("{}?view=enterprise", browser_url)
373
} else {
374
browser_url
375
};
376
self.get_logger().debug(format!(
377
"Checking {} releases on {}",
378
browser_name, edge_updates_url
379
));
380
381
let edge_products =
382
parse_json_from_url::<Vec<EdgeProduct>>(self.get_http_client(), &edge_updates_url)?;
383
384
let edge_channel = if self.is_beta(browser_version) {
385
"Beta"
386
} else if self.is_dev(browser_version) {
387
"Dev"
388
} else if self.is_nightly(browser_version) {
389
"Canary"
390
} else {
391
"Stable"
392
};
393
let products: Vec<&EdgeProduct> = edge_products
394
.iter()
395
.filter(|p| p.product.eq_ignore_ascii_case(edge_channel))
396
.collect();
397
self.get_logger().trace(format!("Products: {:?}", products));
398
399
let os = self.get_os();
400
let arch = self.get_arch();
401
let os_label;
402
let arch_label = if WINDOWS.is(os) {
403
os_label = "Windows";
404
if ARM64.is(arch) {
405
"arm64"
406
} else if X32.is(arch) {
407
"x86"
408
} else {
409
"x64"
410
}
411
} else if MACOS.is(os) {
412
os_label = "MacOS";
413
"universal"
414
} else {
415
os_label = "Linux";
416
"x64"
417
};
418
if products.is_empty() {
419
return self.unavailable_discovery();
420
}
421
422
let releases: Vec<&Release> = products
423
.first()
424
.unwrap()
425
.releases
426
.iter()
427
.filter(|r| {
428
let os_arch = r.platform.eq_ignore_ascii_case(os_label)
429
&& r.architecture.eq_ignore_ascii_case(arch_label);
430
if is_fixed_browser_version {
431
os_arch && r.product_version.starts_with(browser_version)
432
} else {
433
os_arch
434
}
435
})
436
.collect();
437
self.get_logger().trace(format!("Releases: {:?}", releases));
438
439
let package_label = if WINDOWS.is(os) {
440
"msi"
441
} else if MACOS.is(os) {
442
"pkg"
443
} else {
444
"deb"
445
};
446
if releases.is_empty() {
447
return self.unavailable_discovery();
448
}
449
450
let releases_with_artifacts: Vec<&Release> = releases
451
.into_iter()
452
.filter(|r| !r.artifacts.is_empty())
453
.collect();
454
if releases_with_artifacts.is_empty() {
455
return self.unavailable_discovery();
456
}
457
458
let release = releases_with_artifacts.first().unwrap();
459
let artifacts: Vec<&Artifact> = release
460
.artifacts
461
.iter()
462
.filter(|a| a.artifact_name.eq_ignore_ascii_case(package_label))
463
.collect();
464
self.get_logger()
465
.trace(format!("Artifacts: {:?}", artifacts));
466
467
if artifacts.is_empty() {
468
return self.unavailable_discovery();
469
}
470
let artifact = artifacts.first().unwrap();
471
let browser_version = release.product_version.clone();
472
self.browser_url = Some(artifact.location.clone());
473
474
Ok(browser_version)
475
}
476
477
fn request_fixed_browser_version_from_online(
478
&mut self,
479
browser_version: &str,
480
) -> Result<String, Error> {
481
self.request_latest_browser_version_from_online(browser_version)
482
}
483
484
fn get_min_browser_version_for_download(&self) -> Result<i32, Error> {
485
Ok(MIN_EDGE_VERSION_DOWNLOAD)
486
}
487
488
fn get_browser_binary_path(&mut self, browser_version: &str) -> Result<PathBuf, Error> {
489
let browser_in_cache = self.get_browser_path_in_cache()?;
490
if MACOS.is(self.get_os()) {
491
let macos_app_name = if self.is_beta(browser_version) {
492
EDGE_BETA_MACOS_APP_NAME
493
} else if self.is_dev(browser_version) {
494
EDGE_DEV_MACOS_APP_NAME
495
} else if self.is_nightly(browser_version) {
496
EDGE_CANARY_MACOS_APP_NAME
497
} else {
498
EDGE_MACOS_APP_NAME
499
};
500
Ok(browser_in_cache.join(macos_app_name))
501
} else if WINDOWS.is(self.get_os()) {
502
let browser_path = if self.is_unstable(browser_version) {
503
self.get_browser_path_from_version(browser_version)
504
.to_string()
505
} else {
506
format!(
507
r"Microsoft\Edge\Application\{}\msedge.exe",
508
self.get_browser_version()
509
)
510
};
511
let mut full_browser_path = Path::new(&browser_path).to_path_buf();
512
if WINDOWS.is(self.get_os()) {
513
let env_value = env::var(ENV_PROGRAM_FILES_X86).unwrap_or_default();
514
let parent_path = Path::new(&env_value);
515
full_browser_path = parent_path.join(&browser_path);
516
}
517
Ok((&path_to_string(&full_browser_path)).into())
518
} else {
519
Ok(browser_in_cache.join(format!(
520
"{}{}",
521
EDGE_WINDOWS_AND_LINUX_APP_NAME,
522
get_binary_extension(self.get_os())
523
)))
524
}
525
}
526
527
fn get_browser_url_for_download(&mut self, browser_version: &str) -> Result<String, Error> {
528
if self.browser_url.is_none() {
529
self.request_latest_browser_version_from_online(browser_version)?;
530
}
531
Ok(self.browser_url.clone().unwrap())
532
}
533
534
fn get_browser_label_for_download(&self, browser_version: &str) -> Result<Option<&str>, Error> {
535
let browser_label = if self.is_beta(browser_version) {
536
"msedge-beta"
537
} else if self.is_dev(browser_version) {
538
"msedge-dev"
539
} else if self.is_nightly(browser_version) {
540
"msedge-canary"
541
} else {
542
"msedge"
543
};
544
Ok(Some(browser_label))
545
}
546
547
fn is_download_browser(&self) -> bool {
548
self.download_browser
549
}
550
551
fn set_download_browser(&mut self, download_browser: bool) {
552
self.download_browser = download_browser;
553
}
554
555
fn is_snap(&self, _browser_path: &str) -> bool {
556
false
557
}
558
559
fn get_snap_path(&self) -> Option<PathBuf> {
560
None
561
}
562
}
563
564
#[derive(Serialize, Deserialize, Debug)]
565
pub struct EdgeProduct {
566
#[serde(rename = "Product", alias = "product")]
567
pub product: String,
568
#[serde(rename = "Releases", alias = "releases")]
569
pub releases: Vec<Release>,
570
}
571
572
#[derive(Serialize, Deserialize, Debug)]
573
pub struct Release {
574
#[serde(rename = "ReleaseId", alias = "releaseId")]
575
pub release_id: u32,
576
#[serde(rename = "Platform", alias = "platform")]
577
pub platform: String,
578
#[serde(rename = "Architecture", alias = "architecture")]
579
pub architecture: String,
580
#[serde(rename = "CVEs", alias = "cves")]
581
pub cves: Vec<String>,
582
#[serde(rename = "ProductVersion", alias = "productVersion")]
583
pub product_version: String,
584
#[serde(rename = "Artifacts", alias = "artifacts")]
585
pub artifacts: Vec<Artifact>,
586
#[serde(rename = "PublishedTime", alias = "publishedTime")]
587
pub published_time: String,
588
#[serde(rename = "ExpectedExpiryDate", alias = "expectedExpiryDate")]
589
pub expected_expiry_date: String,
590
}
591
592
#[derive(Serialize, Deserialize, Debug)]
593
pub struct Artifact {
594
#[serde(rename = "ArtifactName", alias = "artifactName")]
595
pub artifact_name: String,
596
#[serde(rename = "Location", alias = "location")]
597
pub location: String,
598
#[serde(rename = "Hash", alias = "hash")]
599
pub hash: String,
600
#[serde(rename = "HashAlgorithm", alias = "hashAlgorithm")]
601
pub hash_algorithm: String,
602
#[serde(rename = "SizeInBytes", alias = "sizeInBytes")]
603
pub size_in_bytes: u32,
604
}
605
606