• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from __future__ import annotations
6
7import logging
8from typing import TYPE_CHECKING, Optional
9
10from crossbench import compat
11from crossbench import path as pth
12from crossbench.browsers.attributes import BrowserAttributes
13from crossbench.browsers.browser import Browser
14
15if TYPE_CHECKING:
16  from crossbench import plt
17  from crossbench.browsers.settings import Settings
18
19
20SAFARIDRIVER_PATH = pth.AnyPosixPath("/usr/bin/safaridriver")
21
22
23def find_safaridriver(bin_path: pth.AnyPath,
24                      platform: plt.Platform) -> pth.AnyPath:
25  assert platform.is_file(bin_path), f"Invalid binary path: {bin_path}"
26  driver_path = bin_path.parent / "safaridriver"
27  if platform.exists(driver_path):
28    return driver_path
29  # The system-default Safari version doesn't come with the driver
30  assert compat.is_relative_to(bin_path, Safari.default_path(platform)), (
31      f"Expected default Safari.app binary but got {bin_path}")
32  return SAFARIDRIVER_PATH
33
34
35class Safari(Browser):
36
37  @classmethod
38  def default_path(cls, platform: plt.Platform) -> pth.AnyPath:
39    return platform.path("/Applications/Safari.app")
40
41  @classmethod
42  def technology_preview_path(cls, platform: plt.Platform) -> pth.AnyPath:
43    return platform.path("/Applications/Safari Technology Preview.app")
44
45  def __init__(self,
46               label: str,
47               path: pth.AnyPath,
48               settings: Optional[Settings] = None):
49    super().__init__(label, path, settings=settings)
50    assert self.platform.is_macos, "Safari only works on MacOS"
51    self.bundle_name: str = ""
52
53  def _setup_path(self, path: Optional[pth.AnyPath] = None) -> None:
54    super()._setup_path(path)
55    assert self.path
56    self.bundle_name = self.path.stem.replace(" ", "")
57
58  def _setup_cache_dir(self, settings: Settings) -> None:
59    assert settings.cache_dir is None, (
60        "Cannot set custom cache dir for Safari")
61    assert self.bundle_name, "Missing bundle_name"
62    self.cache_dir = self.platform.home() / (
63        f"Library/Containers/com.apple.{self.bundle_name}/Data/Library/Caches")
64
65  @property
66  def type_name(self) -> str:
67    return "safari"
68
69  @property
70  def attributes(self) -> BrowserAttributes:
71    return BrowserAttributes.SAFARI
72
73  def clear_cache(self) -> None:
74    self._clear_cache()
75
76  def _clear_cache(self) -> None:
77    logging.info("CLEAR CACHE: %s", self)
78    self.platform.exec_apple_script(f"""
79      tell application "{self.app_path}" to activate
80      tell application "System Events"
81          keystroke "e" using {{command down, option down}}
82      end tell""")
83
84  def _extract_version(self) -> str:
85    # Use the shipped safaridriver to get the more detailed version
86    # TODO: support remote platform
87    driver_version = self.platform.app_version(
88        find_safaridriver(self.path, self.platform))
89    # Input: "Included with Safari 16.6 (18615.3.6.11.1)"
90    # Output: " (18615.3.6.11.1)"
91    driver_version = " (" + driver_version.split(" (", maxsplit=1)[1]
92    assert self.path
93    app_path = self.path.parents[2]
94    return self.platform.app_version(app_path) + driver_version
95