realm.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. "use strict";
  2. /**
  3. * Copyright 2022 Google LLC.
  4. * Copyright (c) Microsoft Corporation.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. Object.defineProperty(exports, "__esModule", { value: true });
  19. exports.Realm = void 0;
  20. const protocol_js_1 = require("../../../protocol/protocol.js");
  21. const log_js_1 = require("../../../utils/log.js");
  22. const scriptEvaluator_js_1 = require("./scriptEvaluator.js");
  23. class Realm {
  24. #realmStorage;
  25. #browsingContextStorage;
  26. #realmId;
  27. #browsingContextId;
  28. #executionContextId;
  29. #origin;
  30. #type;
  31. #cdpClient;
  32. #eventManager;
  33. #scriptEvaluator;
  34. sandbox;
  35. cdpSessionId;
  36. #logger;
  37. constructor(realmStorage, browsingContextStorage, realmId, browsingContextId, executionContextId, origin, type, sandbox, cdpSessionId, cdpClient, eventManager, logger) {
  38. this.#realmId = realmId;
  39. this.#browsingContextId = browsingContextId;
  40. this.#executionContextId = executionContextId;
  41. this.sandbox = sandbox;
  42. this.#origin = origin;
  43. this.#type = type;
  44. this.cdpSessionId = cdpSessionId;
  45. this.#cdpClient = cdpClient;
  46. this.#realmStorage = realmStorage;
  47. this.#browsingContextStorage = browsingContextStorage;
  48. this.#eventManager = eventManager;
  49. this.#scriptEvaluator = new scriptEvaluator_js_1.ScriptEvaluator(this.#eventManager);
  50. this.#realmStorage.addRealm(this);
  51. this.#logger = logger;
  52. this.#eventManager.registerEvent({
  53. method: protocol_js_1.Script.EventNames.RealmCreated,
  54. params: this.toBiDi(),
  55. }, this.browsingContextId);
  56. }
  57. async #releaseObject(handle) {
  58. try {
  59. await this.cdpClient.sendCommand('Runtime.releaseObject', {
  60. objectId: handle,
  61. });
  62. }
  63. catch (e) {
  64. // Heuristic to determine if the problem is in the unknown handler.
  65. // Ignore the error if so.
  66. if (!(e.code === -32000 && e.message === 'Invalid remote object id')) {
  67. throw e;
  68. }
  69. }
  70. }
  71. async disown(handle) {
  72. // Disowning an object from different realm does nothing.
  73. if (this.#realmStorage.knownHandlesToRealm.get(handle) !== this.realmId) {
  74. return;
  75. }
  76. await this.#releaseObject(handle);
  77. this.#realmStorage.knownHandlesToRealm.delete(handle);
  78. }
  79. cdpToBidiValue(cdpValue, resultOwnership) {
  80. const deepSerializedValue = cdpValue.result.deepSerializedValue;
  81. const bidiValue = this.deepSerializedToBiDi(deepSerializedValue);
  82. if (cdpValue.result.objectId) {
  83. const objectId = cdpValue.result.objectId;
  84. if (resultOwnership === 'root') {
  85. // Extend BiDi value with `handle` based on required `resultOwnership`
  86. // and CDP response but not on the actual BiDi type.
  87. bidiValue.handle = objectId;
  88. // Remember all the handles sent to client.
  89. this.#realmStorage.knownHandlesToRealm.set(objectId, this.realmId);
  90. }
  91. else {
  92. // No need in awaiting for the object to be released.
  93. void this.#releaseObject(objectId).catch((error) => this.#logger?.(log_js_1.LogType.system, error));
  94. }
  95. }
  96. return bidiValue;
  97. }
  98. deepSerializedToBiDi(webDriverValue) {
  99. // This relies on the CDP to implement proper BiDi serialization, except
  100. // backendNodeId/sharedId and `platformobject`.
  101. const result = webDriverValue;
  102. if (Object.hasOwn(result, 'weakLocalObjectReference')) {
  103. result.internalId = `${result.weakLocalObjectReference}`;
  104. delete result['weakLocalObjectReference'];
  105. }
  106. // Platform object is a special case. It should have only `{type: object}`
  107. // without `value` field.
  108. if (result.type === 'platformobject') {
  109. return { type: 'object' };
  110. }
  111. const bidiValue = result.value;
  112. if (bidiValue === undefined) {
  113. return result;
  114. }
  115. if (result.type === 'node') {
  116. if (Object.hasOwn(bidiValue, 'backendNodeId')) {
  117. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
  118. result.sharedId = `${this.navigableId}${scriptEvaluator_js_1.SHARED_ID_DIVIDER}${bidiValue.backendNodeId}`;
  119. delete bidiValue['backendNodeId'];
  120. }
  121. if (Object.hasOwn(bidiValue, 'children')) {
  122. for (const i in bidiValue.children) {
  123. bidiValue.children[i] = this.deepSerializedToBiDi(bidiValue.children[i]);
  124. }
  125. }
  126. }
  127. // Recursively update the nested values.
  128. if (['array', 'set'].includes(webDriverValue.type)) {
  129. for (const i in bidiValue) {
  130. bidiValue[i] = this.deepSerializedToBiDi(bidiValue[i]);
  131. }
  132. }
  133. if (['object', 'map'].includes(webDriverValue.type)) {
  134. for (const i in bidiValue) {
  135. bidiValue[i] = [
  136. this.deepSerializedToBiDi(bidiValue[i][0]),
  137. this.deepSerializedToBiDi(bidiValue[i][1]),
  138. ];
  139. }
  140. }
  141. return result;
  142. }
  143. toBiDi() {
  144. return {
  145. realm: this.realmId,
  146. origin: this.origin,
  147. type: this.type,
  148. context: this.browsingContextId,
  149. ...(this.sandbox === undefined ? {} : { sandbox: this.sandbox }),
  150. };
  151. }
  152. get realmId() {
  153. return this.#realmId;
  154. }
  155. get navigableId() {
  156. return (this.#browsingContextStorage.findContext(this.#browsingContextId)
  157. ?.navigableId ?? 'UNKNOWN');
  158. }
  159. get browsingContextId() {
  160. return this.#browsingContextId;
  161. }
  162. get executionContextId() {
  163. return this.#executionContextId;
  164. }
  165. get origin() {
  166. return this.#origin;
  167. }
  168. get type() {
  169. return this.#type;
  170. }
  171. get cdpClient() {
  172. return this.#cdpClient;
  173. }
  174. async callFunction(functionDeclaration, _this, _arguments, awaitPromise, resultOwnership, serializationOptions) {
  175. const context = this.#browsingContextStorage.getContext(this.browsingContextId);
  176. await context.awaitUnblocked();
  177. return {
  178. result: await this.#scriptEvaluator.callFunction(this, functionDeclaration, _this, _arguments, awaitPromise, resultOwnership, serializationOptions),
  179. };
  180. }
  181. async scriptEvaluate(expression, awaitPromise, resultOwnership, serializationOptions) {
  182. const context = this.#browsingContextStorage.getContext(this.browsingContextId);
  183. await context.awaitUnblocked();
  184. return {
  185. result: await this.#scriptEvaluator.scriptEvaluate(this, expression, awaitPromise, resultOwnership, serializationOptions),
  186. };
  187. }
  188. /**
  189. * Serializes a given CDP object into BiDi, keeping references in the
  190. * target's `globalThis`.
  191. * @param cdpObject CDP remote object to be serialized.
  192. * @param resultOwnership Indicates desired ResultOwnership.
  193. */
  194. async serializeCdpObject(cdpObject, resultOwnership) {
  195. return this.#scriptEvaluator.serializeCdpObject(cdpObject, resultOwnership, this);
  196. }
  197. /**
  198. * Gets the string representation of an object. This is equivalent to
  199. * calling toString() on the object value.
  200. * @param cdpObject CDP remote object representing an object.
  201. * @return string The stringified object.
  202. */
  203. async stringifyObject(cdpObject) {
  204. return scriptEvaluator_js_1.ScriptEvaluator.stringifyObject(cdpObject, this);
  205. }
  206. delete() {
  207. this.#eventManager.registerEvent({
  208. method: protocol_js_1.Script.EventNames.RealmDestroyed,
  209. params: {
  210. realm: this.realmId,
  211. },
  212. }, this.browsingContextId);
  213. }
  214. }
  215. exports.Realm = Realm;
  216. //# sourceMappingURL=realm.js.map