pymobiledevice3 4.14.6__py3-none-any.whl → 7.0.6__py3-none-any.whl
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.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- misc/understanding_idevice_protocol_layers.md +15 -10
- pymobiledevice3/__main__.py +317 -127
- pymobiledevice3/_version.py +22 -4
- pymobiledevice3/bonjour.py +358 -113
- pymobiledevice3/ca.py +253 -16
- pymobiledevice3/cli/activation.py +31 -23
- pymobiledevice3/cli/afc.py +49 -40
- pymobiledevice3/cli/amfi.py +16 -21
- pymobiledevice3/cli/apps.py +87 -42
- pymobiledevice3/cli/backup.py +160 -90
- pymobiledevice3/cli/bonjour.py +44 -40
- pymobiledevice3/cli/cli_common.py +204 -198
- pymobiledevice3/cli/companion_proxy.py +14 -14
- pymobiledevice3/cli/crash.py +105 -56
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +42 -0
- pymobiledevice3/cli/lockdown.py +108 -103
- pymobiledevice3/cli/mounter.py +158 -99
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +45 -24
- pymobiledevice3/cli/power_assertion.py +18 -17
- pymobiledevice3/cli/processes.py +17 -23
- pymobiledevice3/cli/profile.py +165 -109
- pymobiledevice3/cli/provision.py +35 -34
- pymobiledevice3/cli/remote.py +217 -129
- pymobiledevice3/cli/restore.py +159 -143
- pymobiledevice3/cli/springboard.py +63 -53
- pymobiledevice3/cli/syslog.py +193 -86
- pymobiledevice3/cli/usbmux.py +73 -33
- pymobiledevice3/cli/version.py +5 -7
- pymobiledevice3/cli/webinspector.py +376 -214
- pymobiledevice3/common.py +3 -1
- pymobiledevice3/exceptions.py +182 -58
- pymobiledevice3/irecv.py +52 -53
- pymobiledevice3/irecv_devices.py +1489 -464
- pymobiledevice3/lockdown.py +473 -275
- pymobiledevice3/lockdown_service_provider.py +15 -8
- pymobiledevice3/osu/os_utils.py +27 -9
- pymobiledevice3/osu/posix_util.py +34 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +102 -21
- pymobiledevice3/remote/common.py +8 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
- pymobiledevice3/remote/core_device/file_service.py +53 -23
- pymobiledevice3/remote/remote_service_discovery.py +79 -45
- pymobiledevice3/remote/remotexpc.py +73 -44
- pymobiledevice3/remote/tunnel_service.py +442 -317
- pymobiledevice3/remote/utils.py +14 -13
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +20 -16
- pymobiledevice3/resources/notifications.txt +144 -0
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +110 -21
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +59 -12
- pymobiledevice3/restore/fdr.py +46 -48
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +163 -0
- pymobiledevice3/restore/mbn.py +587 -0
- pymobiledevice3/restore/recovery.py +151 -151
- pymobiledevice3/restore/restore.py +562 -544
- pymobiledevice3/restore/restore_options.py +131 -110
- pymobiledevice3/restore/restored_client.py +51 -31
- pymobiledevice3/restore/tss.py +385 -267
- pymobiledevice3/service_connection.py +252 -59
- pymobiledevice3/services/accessibilityaudit.py +202 -120
- pymobiledevice3/services/afc.py +962 -365
- pymobiledevice3/services/amfi.py +24 -30
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +71 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +101 -79
- pymobiledevice3/services/diagnostics.py +973 -967
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
- pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +9 -8
- pymobiledevice3/services/house_arrest.py +16 -15
- pymobiledevice3/services/idam.py +20 -0
- pymobiledevice3/services/installation_proxy.py +173 -81
- pymobiledevice3/services/lockdown_service.py +20 -10
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +147 -64
- pymobiledevice3/services/mobile_config.py +331 -294
- pymobiledevice3/services/mobile_image_mounter.py +141 -113
- pymobiledevice3/services/mobilebackup2.py +203 -145
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +134 -74
- pymobiledevice3/services/pcapd.py +314 -302
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +21 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +15 -12
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +11 -11
- pymobiledevice3/services/web_protocol/automation_session.py +251 -239
- pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +114 -111
- pymobiledevice3/services/web_protocol/element.py +124 -111
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
- pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
- pymobiledevice3/services/web_protocol/switch_to.py +30 -27
- pymobiledevice3/services/webinspector.py +189 -155
- pymobiledevice3/tcp_forwarder.py +87 -69
- pymobiledevice3/tunneld/__init__.py +0 -0
- pymobiledevice3/tunneld/api.py +63 -0
- pymobiledevice3/tunneld/server.py +603 -0
- pymobiledevice3/usbmux.py +198 -147
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
- pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
- pymobiledevice3/cli/developer.py +0 -1215
- pymobiledevice3/cli/diagnostics.py +0 -99
- pymobiledevice3/tunneld.py +0 -524
- pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextlib
|
|
2
3
|
import json
|
|
3
4
|
import logging
|
|
4
5
|
import uuid
|
|
@@ -6,10 +7,11 @@ from dataclasses import dataclass, fields
|
|
|
6
7
|
from enum import Enum
|
|
7
8
|
from typing import Optional, Union
|
|
8
9
|
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
WebInspectorNotEnabledError
|
|
10
|
+
from pymobiledevice3.exceptions import (
|
|
11
|
+
LaunchingApplicationError,
|
|
12
|
+
RemoteAutomationNotEnabledError,
|
|
13
|
+
WebInspectorNotEnabledError,
|
|
14
|
+
)
|
|
13
15
|
from pymobiledevice3.lockdown import LockdownClient
|
|
14
16
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
15
17
|
from pymobiledevice3.service_connection import ServiceConnection
|
|
@@ -17,55 +19,55 @@ from pymobiledevice3.services.web_protocol.automation_session import AutomationS
|
|
|
17
19
|
from pymobiledevice3.services.web_protocol.inspector_session import InspectorSession
|
|
18
20
|
from pymobiledevice3.services.web_protocol.session_protocol import SessionProtocol
|
|
19
21
|
|
|
20
|
-
SAFARI =
|
|
22
|
+
SAFARI = "com.apple.mobilesafari"
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
def key_to_pid(key: str) -> int:
|
|
24
|
-
return int(key.split(
|
|
26
|
+
return int(key.split(":")[1])
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
class WirTypes(Enum):
|
|
28
|
-
AUTOMATION =
|
|
29
|
-
ITML =
|
|
30
|
-
JAVASCRIPT =
|
|
31
|
-
PAGE =
|
|
32
|
-
SERVICE_WORKER =
|
|
33
|
-
WEB =
|
|
34
|
-
WEB_PAGE =
|
|
35
|
-
AUTOMATICALLY_PAUSE =
|
|
30
|
+
AUTOMATION = "WIRTypeAutomation"
|
|
31
|
+
ITML = "WIRTypeITML"
|
|
32
|
+
JAVASCRIPT = "WIRTypeJavaScript"
|
|
33
|
+
PAGE = "WIRTypePage"
|
|
34
|
+
SERVICE_WORKER = "WIRTypeServiceWorker"
|
|
35
|
+
WEB = "WIRTypeWeb"
|
|
36
|
+
WEB_PAGE = "WIRTypeWebPage"
|
|
37
|
+
AUTOMATICALLY_PAUSE = "WIRAutomaticallyPause"
|
|
36
38
|
|
|
37
39
|
|
|
38
40
|
class AutomationAvailability(Enum):
|
|
39
|
-
NOT_AVAILABLE =
|
|
40
|
-
AVAILABLE =
|
|
41
|
-
UNKNOWN =
|
|
41
|
+
NOT_AVAILABLE = "WIRAutomationAvailabilityNotAvailable"
|
|
42
|
+
AVAILABLE = "WIRAutomationAvailabilityAvailable"
|
|
43
|
+
UNKNOWN = "WIRAutomationAvailabilityUnknown"
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
@dataclass
|
|
45
47
|
class Page:
|
|
46
48
|
id_: int
|
|
47
49
|
type_: WirTypes
|
|
48
|
-
web_url: str =
|
|
49
|
-
web_title: str =
|
|
50
|
+
web_url: str = ""
|
|
51
|
+
web_title: str = ""
|
|
50
52
|
automation_is_paired_key: bool = False
|
|
51
|
-
automation_name: str =
|
|
52
|
-
automation_version: str =
|
|
53
|
-
automation_session_id: str =
|
|
54
|
-
automation_connection_id: str =
|
|
53
|
+
automation_name: str = ""
|
|
54
|
+
automation_version: str = ""
|
|
55
|
+
automation_session_id: str = ""
|
|
56
|
+
automation_connection_id: str = ""
|
|
55
57
|
|
|
56
58
|
@classmethod
|
|
57
|
-
def from_page_dictionary(cls, page_dict: dict) ->
|
|
58
|
-
p = cls(page_dict[
|
|
59
|
+
def from_page_dictionary(cls, page_dict: dict) -> "Page":
|
|
60
|
+
p = cls(page_dict["WIRPageIdentifierKey"], WirTypes(page_dict["WIRTypeKey"]))
|
|
59
61
|
if p.type_ in (WirTypes.WEB, WirTypes.WEB_PAGE):
|
|
60
|
-
p.web_title = page_dict[
|
|
61
|
-
p.web_url = page_dict[
|
|
62
|
+
p.web_title = page_dict["WIRTitleKey"]
|
|
63
|
+
p.web_url = page_dict["WIRURLKey"]
|
|
62
64
|
if p.type_ == WirTypes.AUTOMATION:
|
|
63
|
-
p.automation_is_paired_key = page_dict[
|
|
64
|
-
p.automation_name = page_dict[
|
|
65
|
-
p.automation_version = page_dict[
|
|
66
|
-
p.automation_session_id = page_dict[
|
|
67
|
-
if
|
|
68
|
-
p.automation_connection_id = page_dict[
|
|
65
|
+
p.automation_is_paired_key = page_dict["WIRAutomationTargetIsPairedKey"]
|
|
66
|
+
p.automation_name = page_dict["WIRAutomationTargetNameKey"]
|
|
67
|
+
p.automation_version = page_dict["WIRAutomationTargetVersionKey"]
|
|
68
|
+
p.automation_session_id = page_dict["WIRSessionIdentifierKey"]
|
|
69
|
+
if "WIRConnectionIdentifierKey" in page_dict:
|
|
70
|
+
p.automation_connection_id = page_dict["WIRConnectionIdentifierKey"]
|
|
69
71
|
return p
|
|
70
72
|
|
|
71
73
|
def update(self, page_dict: dict):
|
|
@@ -74,7 +76,7 @@ class Page:
|
|
|
74
76
|
setattr(self, field.name, getattr(new_p, field.name))
|
|
75
77
|
|
|
76
78
|
def __str__(self):
|
|
77
|
-
return f
|
|
79
|
+
return f"id: {self.id_}, title: {self.web_title}, url: {self.web_url}"
|
|
78
80
|
|
|
79
81
|
|
|
80
82
|
@dataclass
|
|
@@ -87,42 +89,42 @@ class Application:
|
|
|
87
89
|
active: int
|
|
88
90
|
proxy: bool
|
|
89
91
|
ready: bool
|
|
90
|
-
host: str =
|
|
92
|
+
host: str = ""
|
|
91
93
|
|
|
92
94
|
@classmethod
|
|
93
|
-
def from_application_dictionary(cls, app_dict) ->
|
|
95
|
+
def from_application_dictionary(cls, app_dict) -> "Application":
|
|
94
96
|
return cls(
|
|
95
|
-
app_dict[
|
|
96
|
-
app_dict[
|
|
97
|
-
key_to_pid(app_dict[
|
|
98
|
-
app_dict[
|
|
99
|
-
AutomationAvailability(app_dict[
|
|
100
|
-
app_dict[
|
|
101
|
-
app_dict[
|
|
102
|
-
app_dict[
|
|
103
|
-
app_dict.get(
|
|
97
|
+
app_dict["WIRApplicationIdentifierKey"],
|
|
98
|
+
app_dict["WIRApplicationBundleIdentifierKey"],
|
|
99
|
+
key_to_pid(app_dict["WIRApplicationIdentifierKey"]),
|
|
100
|
+
app_dict["WIRApplicationNameKey"],
|
|
101
|
+
AutomationAvailability(app_dict["WIRAutomationAvailabilityKey"]),
|
|
102
|
+
app_dict["WIRIsApplicationActiveKey"],
|
|
103
|
+
app_dict["WIRIsApplicationProxyKey"],
|
|
104
|
+
app_dict["WIRIsApplicationReadyKey"],
|
|
105
|
+
app_dict.get("WIRHostApplicationIdentifierKey", ""),
|
|
104
106
|
)
|
|
105
107
|
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
@dataclass
|
|
110
|
+
class ApplicationPage:
|
|
111
|
+
application: Application
|
|
112
|
+
page: Page
|
|
110
113
|
|
|
111
|
-
def
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
nest_asyncio.apply(loop)
|
|
114
|
+
def __str__(self) -> str:
|
|
115
|
+
return f"<{self.application.name}({self.application.pid}) TYPE:{self.page.type_.value} URL:{self.page.web_url}>"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class WebinspectorService:
|
|
119
|
+
SERVICE_NAME = "com.apple.webinspector"
|
|
120
|
+
RSD_SERVICE_NAME = "com.apple.webinspector.shim.remote"
|
|
119
121
|
|
|
122
|
+
def __init__(self, lockdown: LockdownServiceProvider):
|
|
120
123
|
if isinstance(lockdown, LockdownClient):
|
|
121
124
|
self.service_name = self.SERVICE_NAME
|
|
122
125
|
else:
|
|
123
126
|
self.service_name = self.RSD_SERVICE_NAME
|
|
124
127
|
|
|
125
|
-
self.loop = loop
|
|
126
128
|
self.logger = logging.getLogger(__name__)
|
|
127
129
|
self.lockdown = lockdown
|
|
128
130
|
self.service: Optional[ServiceConnection] = None
|
|
@@ -133,33 +135,31 @@ class WebinspectorService:
|
|
|
133
135
|
self.wir_message_results = {}
|
|
134
136
|
self.wir_events = []
|
|
135
137
|
self.receive_handlers = {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
"_rpc_reportCurrentState:": self._handle_report_current_state,
|
|
139
|
+
"_rpc_reportConnectedApplicationList:": self._handle_report_connected_application_list,
|
|
140
|
+
"_rpc_reportConnectedDriverList:": self._handle_report_connected_driver_list,
|
|
141
|
+
"_rpc_applicationSentListing:": self._handle_application_sent_listing,
|
|
142
|
+
"_rpc_applicationUpdated:": self._handle_application_updated,
|
|
143
|
+
"_rpc_applicationConnected:": self._handle_application_connected,
|
|
144
|
+
"_rpc_applicationSentData:": self._handle_application_sent_data,
|
|
145
|
+
"_rpc_applicationDisconnected:": self._handle_application_disconnected,
|
|
144
146
|
}
|
|
145
147
|
self._recv_task: Optional[asyncio.Task] = None
|
|
146
148
|
|
|
147
|
-
def connect(self, timeout: Union[float, int] = None):
|
|
148
|
-
self.service = self.
|
|
149
|
-
self.
|
|
149
|
+
async def connect(self, timeout: Optional[Union[float, int]] = None):
|
|
150
|
+
self.service = await self.lockdown.aio_start_lockdown_service(self.service_name)
|
|
151
|
+
await self._report_identifier()
|
|
150
152
|
try:
|
|
151
|
-
self._handle_recv(
|
|
153
|
+
await self._handle_recv(await asyncio.wait_for(self._recv_message(), timeout))
|
|
152
154
|
except asyncio.TimeoutError as e:
|
|
153
155
|
raise WebInspectorNotEnabledError from e
|
|
154
|
-
self._recv_task =
|
|
156
|
+
self._recv_task = asyncio.create_task(self._receiving_task())
|
|
155
157
|
|
|
156
|
-
def close(self):
|
|
158
|
+
async def close(self):
|
|
157
159
|
self._recv_task.cancel()
|
|
158
|
-
|
|
159
|
-
self.
|
|
160
|
-
|
|
161
|
-
pass
|
|
162
|
-
self.await_(self.service.aio_close())
|
|
160
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
161
|
+
await self._recv_task
|
|
162
|
+
await self.service.aio_close()
|
|
163
163
|
|
|
164
164
|
async def _recv_message(self):
|
|
165
165
|
while True:
|
|
@@ -170,41 +170,57 @@ class WebinspectorService:
|
|
|
170
170
|
|
|
171
171
|
async def _receiving_task(self):
|
|
172
172
|
while True:
|
|
173
|
-
self._handle_recv(await self._recv_message())
|
|
173
|
+
await self._handle_recv(await self._recv_message())
|
|
174
174
|
|
|
175
|
-
def automation_session(self, app: Application) -> AutomationSession:
|
|
176
|
-
if self.state ==
|
|
175
|
+
async def automation_session(self, app: Application) -> AutomationSession:
|
|
176
|
+
if self.state == "WIRAutomationAvailabilityNotAvailable":
|
|
177
177
|
raise RemoteAutomationNotEnabledError()
|
|
178
178
|
session_id = str(uuid.uuid4()).upper()
|
|
179
|
-
self.
|
|
180
|
-
self.
|
|
181
|
-
page = self.
|
|
182
|
-
self.
|
|
183
|
-
self.
|
|
179
|
+
await self._forward_automation_session_request(session_id, app.id_)
|
|
180
|
+
await self._forward_get_listing(app.id_)
|
|
181
|
+
page = await self._wait_for_page(session_id)
|
|
182
|
+
await self._forward_socket_setup(session_id, app.id_, page.id_)
|
|
183
|
+
await self._forward_get_listing(app.id_)
|
|
184
184
|
while not page.automation_connection_id:
|
|
185
|
-
|
|
185
|
+
await asyncio.sleep(0)
|
|
186
186
|
return AutomationSession(SessionProtocol(self, session_id, app, page))
|
|
187
187
|
|
|
188
|
-
async def inspector_session(self, app: Application, page: Page
|
|
188
|
+
async def inspector_session(self, app: Application, page: Page) -> InspectorSession:
|
|
189
189
|
session_id = str(uuid.uuid4()).upper()
|
|
190
|
-
return await InspectorSession.create(
|
|
191
|
-
|
|
190
|
+
return await InspectorSession.create(
|
|
191
|
+
SessionProtocol(self, session_id, app, page, method_prefix=""),
|
|
192
|
+
wait_target=page.type_ != WirTypes.JAVASCRIPT,
|
|
193
|
+
)
|
|
192
194
|
|
|
193
|
-
def get_open_pages(self) -> dict:
|
|
195
|
+
async def get_open_pages(self) -> dict:
|
|
194
196
|
apps = {}
|
|
195
|
-
|
|
197
|
+
await asyncio.gather(*[self._forward_get_listing(app) for app in self.connected_application])
|
|
196
198
|
for app in self.connected_application:
|
|
197
199
|
if self.application_pages.get(app, False):
|
|
198
200
|
apps[self.connected_application[app].name] = self.application_pages[app].values()
|
|
199
201
|
return apps
|
|
200
202
|
|
|
201
|
-
def
|
|
202
|
-
|
|
203
|
-
self.
|
|
203
|
+
async def get_open_application_pages(self, timeout: float) -> list[ApplicationPage]:
|
|
204
|
+
# Query all connected applications
|
|
205
|
+
await self._get_connected_applications()
|
|
206
|
+
|
|
207
|
+
# Give some time for `webinspectord` to reply with all inspectable applications
|
|
208
|
+
await asyncio.sleep(timeout)
|
|
209
|
+
|
|
210
|
+
result = []
|
|
211
|
+
for app in self.connected_application:
|
|
212
|
+
if self.application_pages.get(app, False):
|
|
213
|
+
for page in self.application_pages[app].values():
|
|
214
|
+
result.append(ApplicationPage(self.connected_application[app], page))
|
|
215
|
+
return result
|
|
216
|
+
|
|
217
|
+
async def open_app(self, bundle: str, timeout: Union[float, int] = 3) -> Application:
|
|
218
|
+
await self._request_application_launch(bundle)
|
|
219
|
+
await self.get_open_pages()
|
|
204
220
|
try:
|
|
205
|
-
return
|
|
206
|
-
except TimeoutError:
|
|
207
|
-
raise LaunchingApplicationError()
|
|
221
|
+
return await asyncio.wait_for(self._wait_for_application(bundle), timeout)
|
|
222
|
+
except TimeoutError as e:
|
|
223
|
+
raise LaunchingApplicationError() from e
|
|
208
224
|
|
|
209
225
|
async def send_socket_data(self, session_id: str, app_id: str, page_id: int, data: dict):
|
|
210
226
|
await self._forward_socket_data(session_id, app_id, page_id, data)
|
|
@@ -217,116 +233,134 @@ class WebinspectorService:
|
|
|
217
233
|
for page in self.application_pages[app_id]:
|
|
218
234
|
if page == page_id:
|
|
219
235
|
return self.connected_application[app_id], self.application_pages[app_id][page_id]
|
|
236
|
+
raise KeyError(f"Page with id {page_id} not found")
|
|
220
237
|
|
|
221
|
-
def flush_input(self, duration: Union[float, int] = 0):
|
|
222
|
-
return
|
|
238
|
+
async def flush_input(self, duration: Union[float, int] = 0):
|
|
239
|
+
return await asyncio.sleep(duration)
|
|
223
240
|
|
|
224
|
-
def
|
|
225
|
-
|
|
241
|
+
async def _handle_recv(self, plist):
|
|
242
|
+
await self.receive_handlers[plist["__selector"]](plist["__argument"])
|
|
226
243
|
|
|
227
|
-
def
|
|
228
|
-
self.
|
|
244
|
+
async def _handle_report_current_state(self, arg):
|
|
245
|
+
self.state = arg["WIRAutomationAvailabilityKey"]
|
|
229
246
|
|
|
230
|
-
def
|
|
231
|
-
self.state = arg['WIRAutomationAvailabilityKey']
|
|
232
|
-
|
|
233
|
-
def _handle_report_connected_application_list(self, arg):
|
|
247
|
+
async def _handle_report_connected_application_list(self, arg):
|
|
234
248
|
self.connected_application = {}
|
|
235
|
-
for key, application in arg[
|
|
249
|
+
for key, application in arg["WIRApplicationDictionaryKey"].items():
|
|
236
250
|
self.connected_application[key] = Application.from_application_dictionary(application)
|
|
237
251
|
|
|
238
|
-
|
|
252
|
+
# Immediately also query the application pages
|
|
253
|
+
await self._forward_get_listing(self.connected_application[key].id_)
|
|
254
|
+
|
|
255
|
+
async def _handle_report_connected_driver_list(self, arg):
|
|
239
256
|
pass
|
|
240
257
|
|
|
241
|
-
def _handle_application_sent_listing(self, arg):
|
|
242
|
-
if arg[
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
258
|
+
async def _handle_application_sent_listing(self, arg):
|
|
259
|
+
if arg["WIRApplicationIdentifierKey"] in self.application_pages:
|
|
260
|
+
# Update existing application pages
|
|
261
|
+
for id_, page in arg["WIRListingKey"].items():
|
|
262
|
+
if id_ in self.application_pages[arg["WIRApplicationIdentifierKey"]]:
|
|
263
|
+
self.application_pages[arg["WIRApplicationIdentifierKey"]][id_].update(page)
|
|
246
264
|
else:
|
|
247
|
-
self.application_pages[arg[
|
|
265
|
+
self.application_pages[arg["WIRApplicationIdentifierKey"]][id_] = Page.from_page_dictionary(page)
|
|
248
266
|
else:
|
|
267
|
+
# Add new application pages
|
|
249
268
|
pages = {}
|
|
250
|
-
for id_, page in arg[
|
|
269
|
+
for id_, page in arg["WIRListingKey"].items():
|
|
251
270
|
pages[id_] = Page.from_page_dictionary(page)
|
|
252
|
-
self.application_pages[arg[
|
|
271
|
+
self.application_pages[arg["WIRApplicationIdentifierKey"]] = pages
|
|
253
272
|
|
|
254
|
-
def _handle_application_updated(self, arg):
|
|
273
|
+
async def _handle_application_updated(self, arg):
|
|
255
274
|
app = Application.from_application_dictionary(arg)
|
|
256
275
|
self.connected_application[app.id_] = app
|
|
257
276
|
|
|
258
|
-
def _handle_application_connected(self, arg):
|
|
277
|
+
async def _handle_application_connected(self, arg):
|
|
259
278
|
app = Application.from_application_dictionary(arg)
|
|
260
279
|
self.connected_application[app.id_] = app
|
|
261
280
|
|
|
262
|
-
def _handle_application_sent_data(self, arg):
|
|
263
|
-
response = json.loads(arg[
|
|
281
|
+
async def _handle_application_sent_data(self, arg):
|
|
282
|
+
response = json.loads(arg["WIRMessageDataKey"])
|
|
264
283
|
|
|
265
|
-
if
|
|
266
|
-
self.wir_message_results[response[
|
|
284
|
+
if "id" in response:
|
|
285
|
+
self.wir_message_results[response["id"]] = response
|
|
267
286
|
else:
|
|
268
287
|
self.wir_events.append(response)
|
|
269
288
|
|
|
270
|
-
def _handle_application_disconnected(self, arg):
|
|
271
|
-
self.connected_application.pop(arg[
|
|
272
|
-
self.application_pages.pop(arg[
|
|
289
|
+
async def _handle_application_disconnected(self, arg):
|
|
290
|
+
self.connected_application.pop(arg["WIRApplicationIdentifierKey"], None)
|
|
291
|
+
self.application_pages.pop(arg["WIRApplicationIdentifierKey"], None)
|
|
273
292
|
|
|
274
293
|
async def _report_identifier(self):
|
|
275
|
-
await self._send_message(
|
|
294
|
+
await self._send_message("_rpc_reportIdentifier:")
|
|
276
295
|
|
|
277
296
|
async def _forward_get_listing(self, app_id):
|
|
278
|
-
self.logger.debug(f
|
|
279
|
-
await self._send_message(
|
|
297
|
+
self.logger.debug(f"Listing app with id {app_id}")
|
|
298
|
+
await self._send_message("_rpc_forwardGetListing:", {"WIRApplicationIdentifierKey": app_id})
|
|
280
299
|
|
|
281
300
|
async def _request_application_launch(self, bundle: str):
|
|
282
|
-
await self._send_message(
|
|
301
|
+
await self._send_message("_rpc_requestApplicationLaunch:", {"WIRApplicationBundleIdentifierKey": bundle})
|
|
302
|
+
|
|
303
|
+
async def _get_connected_applications(self) -> None:
|
|
304
|
+
await self._send_message("_rpc_getConnectedApplications:", {})
|
|
283
305
|
|
|
284
306
|
async def _forward_automation_session_request(self, session_id: str, app_id: str):
|
|
285
|
-
await self._send_message(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
307
|
+
await self._send_message(
|
|
308
|
+
"_rpc_forwardAutomationSessionRequest:",
|
|
309
|
+
{
|
|
310
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
311
|
+
"WIRSessionCapabilitiesKey": {
|
|
312
|
+
"org.webkit.webdriver.webrtc.allow-insecure-media-capture": True,
|
|
313
|
+
"org.webkit.webdriver.webrtc.suppress-ice-candidate-filtering": False,
|
|
314
|
+
},
|
|
315
|
+
"WIRSessionIdentifierKey": session_id,
|
|
290
316
|
},
|
|
291
|
-
|
|
292
|
-
})
|
|
317
|
+
)
|
|
293
318
|
|
|
294
319
|
async def _forward_socket_setup(self, session_id: str, app_id: str, page_id: int, pause: bool = True):
|
|
295
320
|
message = {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
321
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
322
|
+
"WIRPageIdentifierKey": page_id,
|
|
323
|
+
"WIRSenderKey": session_id,
|
|
324
|
+
"WIRMessageDataTypeChunkSupportedKey": 0,
|
|
299
325
|
}
|
|
300
326
|
if not pause:
|
|
301
|
-
message[
|
|
302
|
-
await self._send_message(
|
|
327
|
+
message["WIRAutomaticallyPause"] = False
|
|
328
|
+
await self._send_message("_rpc_forwardSocketSetup:", message)
|
|
303
329
|
|
|
304
330
|
async def _forward_socket_data(self, session_id: str, app_id: str, page_id: int, data: dict):
|
|
305
|
-
await self._send_message(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
331
|
+
await self._send_message(
|
|
332
|
+
"_rpc_forwardSocketData:",
|
|
333
|
+
{
|
|
334
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
335
|
+
"WIRPageIdentifierKey": page_id,
|
|
336
|
+
"WIRSessionIdentifierKey": session_id,
|
|
337
|
+
"WIRSenderKey": session_id,
|
|
338
|
+
"WIRSocketDataKey": json.dumps(data).encode(),
|
|
339
|
+
},
|
|
340
|
+
)
|
|
311
341
|
|
|
312
342
|
async def _forward_indicate_web_view(self, app_id: str, page_id: int, enable: bool):
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
343
|
+
(
|
|
344
|
+
await self._send_message("_rpc_forwardIndicateWebView"),
|
|
345
|
+
{
|
|
346
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
347
|
+
"WIRPageIdentifierKey": page_id,
|
|
348
|
+
"WIRIndicateEnabledKey": enable,
|
|
349
|
+
},
|
|
350
|
+
)
|
|
318
351
|
|
|
319
352
|
async def _send_message(self, selector: str, args=None):
|
|
320
353
|
if args is None:
|
|
321
354
|
args = {}
|
|
322
|
-
args[
|
|
323
|
-
await self.service.aio_send_plist({
|
|
355
|
+
args["WIRConnectionIdentifierKey"] = self.connection_id
|
|
356
|
+
await self.service.aio_send_plist({"__selector": selector, "__argument": args})
|
|
324
357
|
|
|
325
358
|
def _page_by_automation_session(self, session_id: str) -> Page:
|
|
326
359
|
for app_id in self.application_pages:
|
|
327
360
|
for page in self.application_pages[app_id]:
|
|
328
361
|
if page.type_ == WirTypes.AUTOMATION and page.automation_session_id == session_id:
|
|
329
362
|
return page
|
|
363
|
+
raise KeyError(f"Automation session with id {session_id} not found")
|
|
330
364
|
|
|
331
365
|
async def _wait_for_page(self, session_id: str):
|
|
332
366
|
while True:
|
|
@@ -336,7 +370,7 @@ class WebinspectorService:
|
|
|
336
370
|
return page
|
|
337
371
|
await asyncio.sleep(0)
|
|
338
372
|
|
|
339
|
-
async def _wait_for_application(self, bundle: str =
|
|
373
|
+
async def _wait_for_application(self, bundle: str = "", app_id: str = "") -> Application:
|
|
340
374
|
while True:
|
|
341
375
|
for app in self.connected_application.values():
|
|
342
376
|
if bundle and app.bundle == bundle:
|