How to use mojo_from_native_origin method in wpt

Best JavaScript code snippet using wpt

webxr-test.js

Source:webxr-test.js Github

copy

Full Screen

1'use strict';2// This polyfill library implements the WebXR Test API as specified here:3// https://github.com/immersive-web/webxr-test-api4const default_standing = new gfx.mojom.Transform();5default_standing.matrix = [1, 0, 0, 0,6 0, 1, 0, 0,7 0, 0, 1, 0,8 0, 1.65, 0, 1];9const default_stage_parameters = {10 standingTransform: default_standing,11 bounds: null12};13function getMatrixFromTransform(transform) {14 const x = transform.orientation[0];15 const y = transform.orientation[1];16 const z = transform.orientation[2];17 const w = transform.orientation[3];18 const m11 = 1.0 - 2.0 * (y * y + z * z);19 const m21 = 2.0 * (x * y + z * w);20 const m31 = 2.0 * (x * z - y * w);21 const m12 = 2.0 * (x * y - z * w);22 const m22 = 1.0 - 2.0 * (x * x + z * z);23 const m32 = 2.0 * (y * z + x * w);24 const m13 = 2.0 * (x * z + y * w);25 const m23 = 2.0 * (y * z - x * w);26 const m33 = 1.0 - 2.0 * (x * x + y * y);27 const m14 = transform.position[0];28 const m24 = transform.position[1];29 const m34 = transform.position[2];30 // Column-major linearized order is expected.31 return [m11, m21, m31, 0,32 m12, m22, m32, 0,33 m13, m23, m33, 0,34 m14, m24, m34, 1];35}36function composeGFXTransform(fakeTransformInit) {37 const transform = new gfx.mojom.Transform();38 transform.matrix = getMatrixFromTransform(fakeTransformInit);39 return transform;40}41class ChromeXRTest {42 constructor() {43 this.mockVRService_ = new MockVRService(mojo.frameInterfaces);44 }45 simulateDeviceConnection(init_params) {46 return Promise.resolve(this.mockVRService_.addRuntime(init_params));47 }48 disconnectAllDevices() {49 this.mockVRService_.removeAllRuntimes(device);50 return Promise.resolve();51 }52 simulateUserActivation(callback) {53 const button = document.createElement('button');54 button.textContent = 'click to continue test';55 button.style.display = 'block';56 button.style.fontSize = '20px';57 button.style.padding = '10px';58 button.onclick = () => {59 callback();60 document.body.removeChild(button);61 };62 document.body.appendChild(button);63 test_driver.click(button);64 }65 Debug(name, msg) {66 console.log(new Date().toISOString() + ' DEBUG[' + name + '] ' + msg);67 }68}69// Mocking class definitions70// Mock service implements the VRService mojo interface.71class MockVRService {72 constructor() {73 this.bindingSet_ = new mojo.BindingSet(device.mojom.VRService);74 this.runtimes_ = [];75 this.interceptor_ =76 new MojoInterfaceInterceptor(device.mojom.VRService.name);77 this.interceptor_.oninterfacerequest = e =>78 this.bindingSet_.addBinding(this, e.handle);79 this.interceptor_.start();80 }81 // Test methods82 addRuntime(fakeDeviceInit) {83 const runtime = new MockRuntime(fakeDeviceInit, this);84 this.runtimes_.push(runtime);85 if (this.client_) {86 this.client_.onDeviceChanged();87 }88 return runtime;89 }90 removeAllRuntimes() {91 if (this.client_) {92 this.client_.onDeviceChanged();93 }94 this.runtimes_ = [];95 }96 removeRuntime(device) {97 const index = this.runtimes_.indexOf(device);98 if (index >= 0) {99 this.runtimes_.splice(index, 1);100 if (this.client_) {101 this.client_.onDeviceChanged();102 }103 }104 }105 setClient(client) {106 if (this.client_) {107 throw new Error("setClient should only be called once");108 }109 this.client_ = client;110 }111 requestSession(sessionOptions, was_activation) {112 const requests = [];113 // Request a session from all the runtimes.114 for (let i = 0; i < this.runtimes_.length; i++) {115 requests[i] = this.runtimes_[i].requestRuntimeSession(sessionOptions);116 }117 return Promise.all(requests).then((results) => {118 // Find and return the first successful result.119 for (let i = 0; i < results.length; i++) {120 if (results[i].session) {121 // Construct a dummy metrics recorder122 const metricsRecorderPtr = new device.mojom.XRSessionMetricsRecorderPtr();123 const metricsRecorderRequest = mojo.makeRequest(metricsRecorderPtr);124 const metricsRecorderBinding = new mojo.Binding(125 device.mojom.XRSessionMetricsRecorder, new MockXRSessionMetricsRecorder(), metricsRecorderRequest);126 const success = {127 session: results[i].session,128 metricsRecorder: metricsRecorderPtr,129 };130 return {131 result: {132 success : success,133 $tag : 0134 }135 };136 }137 }138 // If there were no successful results, returns a null session.139 return {140 result: {141 failureReason : device.mojom.RequestSessionError.NO_RUNTIME_FOUND,142 $tag : 1143 }144 };145 });146 }147 exitPresent() {148 return Promise.resolve();149 }150 supportsSession(sessionOptions) {151 const requests = [];152 // Check supports on all the runtimes.153 for (let i = 0; i < this.runtimes_.length; i++) {154 requests[i] = this.runtimes_[i].runtimeSupportsSession(sessionOptions);155 }156 return Promise.all(requests).then((results) => {157 // Find and return the first successful result.158 for (let i = 0; i < results.length; i++) {159 if (results[i].supportsSession) {160 return results[i];161 }162 }163 // If there were no successful results, returns false.164 return {supportsSession: false};165 });166 }167}168// Implements XRFrameDataProvider and XRPresentationProvider. Maintains a mock169// for XRPresentationProvider.170class MockRuntime {171 // Mapping from string feature names to the corresponding mojo types.172 // This is exposed as a member for extensibility.173 static featureToMojoMap = {174 'viewer': device.mojom.XRSessionFeature.REF_SPACE_VIEWER,175 'local': device.mojom.XRSessionFeature.REF_SPACE_LOCAL,176 'local-floor': device.mojom.XRSessionFeature.REF_SPACE_LOCAL_FLOOR,177 'bounded-floor': device.mojom.XRSessionFeature.REF_SPACE_BOUNDED_FLOOR,178 'unbounded': device.mojom.XRSessionFeature.REF_SPACE_UNBOUNDED,179 'hit-test': device.mojom.XRSessionFeature.HIT_TEST,180 'dom-overlay': device.mojom.XRSessionFeature.DOM_OVERLAY,181 'light-estimation': device.mojom.XRSessionFeature.LIGHT_ESTIMATION,182 };183 static sessionModeToMojoMap = {184 "inline": device.mojom.XRSessionMode.kInline,185 "immersive-vr": device.mojom.XRSessionMode.kImmersiveVr,186 "immersive-ar": device.mojom.XRSessionMode.kImmersiveAr,187 };188 constructor(fakeDeviceInit, service) {189 this.sessionClient_ = new device.mojom.XRSessionClientPtr();190 this.presentation_provider_ = new MockXRPresentationProvider();191 this.pose_ = null;192 this.next_frame_id_ = 0;193 this.bounds_ = null;194 this.send_mojo_space_reset_ = false;195 this.stageParameters_ = null;196 this.stageParametersUpdated_ = false;197 this.service_ = service;198 this.framesOfReference = {};199 this.input_sources_ = new Map();200 this.next_input_source_index_ = 1;201 // Currently active hit test subscriptons.202 this.hitTestSubscriptions_ = new Map();203 // Currently active transient hit test subscriptions.204 this.transientHitTestSubscriptions_ = new Map();205 // ID of the next subscription to be assigned.206 this.next_hit_test_id_ = 1;207 let supportedModes = [];208 if (fakeDeviceInit.supportedModes) {209 supportedModes = fakeDeviceInit.supportedModes.slice();210 if (fakeDeviceInit.supportedModes.length === 0) {211 supportedModes = ["inline"];212 }213 } else {214 // Back-compat mode.215 console.warn("Please use `supportedModes` to signal which modes are supported by this device.");216 if (fakeDeviceInit.supportsImmersive == null) {217 throw new TypeError("'supportsImmersive' must be set");218 }219 supportedModes = ["inline"];220 if (fakeDeviceInit.supportsImmersive) {221 supportedModes.push("immersive-vr");222 }223 }224 this.supportedModes_ = this._convertModesToEnum(supportedModes);225 // Initialize DisplayInfo first to set the defaults, then override with226 // anything from the deviceInit227 if (this.supportedModes_.includes(device.mojom.XRSessionMode.kImmersiveVr)228 || this.supportedModes_.includes(device.mojom.XRSessionMode.kImmersiveAr)) {229 this.displayInfo_ = this.getImmersiveDisplayInfo();230 } else if (this.supportedModes_.includes(device.mojom.XRSessionMode.kInline)) {231 this.displayInfo_ = this.getNonImmersiveDisplayInfo();232 } else {233 // This should never happen!234 console.error("Device has empty supported modes array!");235 throw new InvalidStateError();236 }237 if (fakeDeviceInit.viewerOrigin != null) {238 this.setViewerOrigin(fakeDeviceInit.viewerOrigin);239 }240 if (fakeDeviceInit.floorOrigin != null) {241 this.setFloorOrigin(fakeDeviceInit.floorOrigin);242 }243 if (fakeDeviceInit.world) {244 this.world_ = fakeDeviceInit.world;245 }246 // This appropriately handles if the coordinates are null247 this.setBoundsGeometry(fakeDeviceInit.boundsCoordinates);248 this.setViews(fakeDeviceInit.views);249 // Need to support webVR which doesn't have a notion of features250 this.setFeatures(fakeDeviceInit.supportedFeatures || []);251 }252 _convertModeToEnum(sessionMode) {253 if (sessionMode in MockRuntime.sessionModeToMojoMap) {254 return MockRuntime.sessionModeToMojoMap[sessionMode];255 }256 throw new TypeError("Unrecognized value for XRSessionMode enum: " + sessionMode);257 }258 _convertModesToEnum(sessionModes) {259 return sessionModes.map(mode => this._convertModeToEnum(mode));260 }261 // Test API methods.262 disconnect() {263 this.service_.removeRuntime(this);264 this.presentation_provider_.Close();265 if (this.sessionClient_.ptr.isBound()) {266 this.sessionClient_.ptr.reset();267 }268 return Promise.resolve();269 }270 setViews(views) {271 if (views) {272 let changed = false;273 for (let i = 0; i < views.length; i++) {274 if (views[i].eye == 'left') {275 this.displayInfo_.leftEye = this.getEye(views[i]);276 changed = true;277 } else if (views[i].eye == 'right') {278 this.displayInfo_.rightEye = this.getEye(views[i]);279 changed = true;280 }281 }282 if (changed && this.sessionClient_.ptr.isBound()) {283 this.sessionClient_.onChanged(this.displayInfo_);284 }285 }286 }287 setViewerOrigin(origin, emulatedPosition = false) {288 const p = origin.position;289 const q = origin.orientation;290 this.pose_ = {291 orientation: { x: q[0], y: q[1], z: q[2], w: q[3] },292 position: { x: p[0], y: p[1], z: p[2] },293 emulatedPosition: emulatedPosition,294 angularVelocity: null,295 linearVelocity: null,296 angularAcceleration: null,297 linearAcceleration: null,298 inputState: null,299 poseIndex: 0300 };301 }302 clearViewerOrigin() {303 this.pose_ = null;304 }305 simulateVisibilityChange(visibilityState) {306 let mojoState = null;307 switch (visibilityState) {308 case "visible":309 mojoState = device.mojom.XRVisibilityState.VISIBLE;310 break;311 case "visible-blurred":312 mojoState = device.mojom.XRVisibilityState.VISIBLE_BLURRED;313 break;314 case "hidden":315 mojoState = device.mojom.XRVisibilityState.HIDDEN;316 break;317 }318 if (mojoState) {319 this.sessionClient_.onVisibilityStateChanged(mojoState);320 }321 }322 setBoundsGeometry(bounds) {323 if (bounds == null) {324 this.bounds_ = null;325 } else if (bounds.length < 3) {326 throw new Error("Bounds must have a length of at least 3");327 } else {328 this.bounds_ = bounds;329 }330 // We can only set bounds if we have stageParameters set; otherwise, we331 // don't know the transform from local space to bounds space.332 // We'll cache the bounds so that they can be set in the future if the333 // floorLevel transform is set, but we won't update them just yet.334 if (this.stageParameters_) {335 this.stageParameters_.bounds = this.bounds_;336 this.onStageParametersUpdated();337 }338 }339 setFloorOrigin(floorOrigin) {340 if (!this.stageParameters_) {341 this.stageParameters_ = default_stage_parameters;342 this.stageParameters_.bounds = this.bounds_;343 }344 this.stageParameters_.standingTransform = new gfx.mojom.Transform();345 this.stageParameters_.standingTransform.matrix =346 getMatrixFromTransform(floorOrigin);347 this.onStageParametersUpdated();348 }349 clearFloorOrigin() {350 if (this.stageParameters_) {351 this.stageParameters_ = null;352 this.onStageParametersUpdated();353 }354 }355 onStageParametersUpdated() {356 // Indicate for the frame loop that the stage parameters have been updated.357 this.stageParametersUpdated_ = true;358 this.displayInfo_.stageParameters = this.stageParameters_;359 if (this.sessionClient_.ptr.isBound()) {360 this.sessionClient_.onChanged(this.displayInfo_);361 }362 }363 simulateResetPose() {364 this.send_mojo_space_reset_ = true;365 }366 simulateInputSourceConnection(fakeInputSourceInit) {367 const index = this.next_input_source_index_;368 this.next_input_source_index_++;369 const source = new MockXRInputSource(fakeInputSourceInit, index, this);370 this.input_sources_.set(index, source);371 return source;372 }373 // Helper methods374 getNonImmersiveDisplayInfo() {375 const displayInfo = this.getImmersiveDisplayInfo();376 displayInfo.capabilities.canPresent = false;377 displayInfo.leftEye = null;378 displayInfo.rightEye = null;379 return displayInfo;380 }381 // Function to generate some valid display information for the device.382 getImmersiveDisplayInfo() {383 return {384 displayName: 'FakeDevice',385 capabilities: {386 hasPosition: false,387 hasExternalDisplay: false,388 canPresent: true,389 maxLayers: 1390 },391 stageParameters: null,392 leftEye: {393 fieldOfView: {394 upDegrees: 48.316,395 downDegrees: 50.099,396 leftDegrees: 50.899,397 rightDegrees: 35.197398 },399 headFromEye: composeGFXTransform({400 position: [-0.032, 0, 0],401 orientation: [0, 0, 0, 1]402 }),403 renderWidth: 20,404 renderHeight: 20405 },406 rightEye: {407 fieldOfView: {408 upDegrees: 48.316,409 downDegrees: 50.099,410 leftDegrees: 50.899,411 rightDegrees: 35.197412 },413 headFromEye: composeGFXTransform({414 position: [0.032, 0, 0],415 orientation: [0, 0, 0, 1]416 }),417 renderWidth: 20,418 renderHeight: 20419 },420 webxrDefaultFramebufferScale: 0.7,421 };422 }423 // This function converts between the matrix provided by the WebXR test API424 // and the internal data representation.425 getEye(fakeXRViewInit) {426 let fov = null;427 if (fakeXRViewInit.fieldOfView) {428 fov = {429 upDegrees: fakeXRViewInit.fieldOfView.upDegrees,430 downDegrees: fakeXRViewInit.fieldOfView.downDegrees,431 leftDegrees: fakeXRViewInit.fieldOfView.leftDegrees,432 rightDegrees: fakeXRViewInit.fieldOfView.rightDegrees433 };434 } else {435 const m = fakeXRViewInit.projectionMatrix;436 function toDegrees(tan) {437 return Math.atan(tan) * 180 / Math.PI;438 }439 const leftTan = (1 - m[8]) / m[0];440 const rightTan = (1 + m[8]) / m[0];441 const upTan = (1 + m[9]) / m[5];442 const downTan = (1 - m[9]) / m[5];443 fov = {444 upDegrees: toDegrees(upTan),445 downDegrees: toDegrees(downTan),446 leftDegrees: toDegrees(leftTan),447 rightDegrees: toDegrees(rightTan)448 };449 }450 return {451 fieldOfView: fov,452 headFromEye: composeGFXTransform(fakeXRViewInit.viewOffset),453 renderWidth: fakeXRViewInit.resolution.width,454 renderHeight: fakeXRViewInit.resolution.height455 };456 }457 setFeatures(supportedFeatures) {458 function convertFeatureToMojom(feature) {459 if (feature in MockRuntime.featureToMojoMap) {460 return MockRuntime.featureToMojoMap[feature];461 } else {462 return device.mojom.XRSessionFeature.INVALID;463 }464 }465 this.supportedFeatures_ = [];466 for (let i = 0; i < supportedFeatures.length; i++) {467 const feature = convertFeatureToMojom(supportedFeatures[i]);468 if (feature !== device.mojom.XRSessionFeature.INVALID) {469 this.supportedFeatures_.push(feature);470 }471 }472 }473 // These methods are intended to be used by MockXRInputSource only.474 addInputSource(source) {475 if (!this.input_sources_.has(source.source_id_)) {476 this.input_sources_.set(source.source_id_, source);477 }478 }479 removeInputSource(source) {480 this.input_sources_.delete(source.source_id_);481 }482 // Extension point for non-standard modules.483 _injectAdditionalFrameData(options, frameData) {484 }485 // Mojo function implementations.486 // XRFrameDataProvider implementation.487 getFrameData(options) {488 const mojo_space_reset = this.send_mojo_space_reset_;489 this.send_mojo_space_reset_ = false;490 const stage_parameters_updated = this.stageParametersUpdated_;491 this.stageParametersUpdated_ = false;492 if (this.pose_) {493 this.pose_.poseIndex++;494 }495 // Setting the input_state to null tests a slightly different path than496 // the browser tests where if the last input source is removed, the device497 // code always sends up an empty array, but it's also valid mojom to send498 // up a null array.499 let input_state = null;500 if (this.input_sources_.size > 0) {501 input_state = [];502 for (const input_source of this.input_sources_.values()) {503 input_state.push(input_source.getInputSourceState());504 }505 }506 // Convert current document time to monotonic time.507 let now = window.performance.now() / 1000.0;508 const diff = now - internals.monotonicTimeToZeroBasedDocumentTime(now);509 now += diff;510 now *= 1000000;511 const frameData = {512 pose: this.pose_,513 mojoSpaceReset: mojo_space_reset,514 inputState: input_state,515 timeDelta: {516 microseconds: now,517 },518 frameId: this.next_frame_id_++,519 bufferHolder: null,520 bufferSize: {},521 stageParameters: this.stageParameters_,522 stageParametersUpdated: stage_parameters_updated,523 };524 this._calculateHitTestResults(frameData);525 this._injectAdditionalFrameData(options, frameData);526 return Promise.resolve({527 frameData: frameData,528 });529 }530 getEnvironmentIntegrationProvider(environmentProviderRequest) {531 this.environmentProviderBinding_ = new mojo.AssociatedBinding(532 device.mojom.XREnvironmentIntegrationProvider, this,533 environmentProviderRequest);534 }535 // Note that if getEnvironmentProvider hasn't finished running yet this will536 // be undefined. It's recommended that you allow a successful task to post537 // first before attempting to close.538 closeEnvironmentIntegrationProvider() {539 this.environmentProviderBinding_.close();540 }541 closeDataProvider() {542 this.dataProviderBinding_.close();543 }544 updateSessionGeometry(frame_size, display_rotation) {545 // This function must exist to ensure that calls to it do not crash, but we546 // do not have any use for this data at present.547 }548 // XREnvironmentIntegrationProvider implementation:549 subscribeToHitTest(nativeOriginInformation, entityTypes, ray) {550 if (!this.supportedModes_.includes(device.mojom.XRSessionMode.kImmersiveAr)) {551 // Reject outside of AR.552 return Promise.resolve({553 result : device.mojom.SubscribeToHitTestResult.FAILURE_GENERIC,554 subscriptionId : 0555 });556 }557 if (nativeOriginInformation.$tag == device.mojom.XRNativeOriginInformation.Tags.inputSourceId) {558 if (!this.input_sources_.has(nativeOriginInformation.inputSourceId)) {559 // Reject - unknown input source ID.560 return Promise.resolve({561 result : device.mojom.SubscribeToHitTestResult.FAILURE_GENERIC,562 subscriptionId : 0563 });564 }565 } else if (nativeOriginInformation.$tag == device.mojom.XRNativeOriginInformation.Tags.referenceSpaceCategory) {566 // Bounded_floor & unbounded ref spaces are not yet supported for AR:567 if (nativeOriginInformation.referenceSpaceCategory == device.mojom.XRReferenceSpaceCategory.UNBOUNDED568 || nativeOriginInformation.referenceSpaceCategory == device.mojom.XRReferenceSpaceCategory.BOUNDED_FLOOR) {569 return Promise.resolve({570 result : device.mojom.SubscribeToHitTestResult.FAILURE_GENERIC,571 subscriptionId : 0572 });573 }574 } else {575 // Planes and anchors are not yet supported by the mock interface.576 return Promise.resolve({577 result : device.mojom.SubscribeToHitTestResult.FAILURE_GENERIC,578 subscriptionId : 0579 });580 }581 // Store the subscription information as-is:582 const id = this.next_hit_test_id_++;583 this.hitTestSubscriptions_.set(id, { nativeOriginInformation, entityTypes, ray });584 return Promise.resolve({585 result : device.mojom.SubscribeToHitTestResult.SUCCESS,586 subscriptionId : id587 });588 }589 subscribeToHitTestForTransientInput(profileName, entityTypes, ray){590 if (!this.supportedModes_.includes(device.mojom.XRSessionMode.kImmersiveAr)) {591 // Reject outside of AR.592 return Promise.resolve({593 result : device.mojom.SubscribeToHitTestResult.FAILURE_GENERIC,594 subscriptionId : 0595 });596 }597 // Store the subscription information as-is:598 const id = this.next_hit_test_id_++;599 this.transientHitTestSubscriptions_.set(id, { profileName, entityTypes, ray });600 return Promise.resolve({601 result : device.mojom.SubscribeToHitTestResult.SUCCESS,602 subscriptionId : id603 });604 }605 // Utility function606 requestRuntimeSession(sessionOptions) {607 return this.runtimeSupportsSession(sessionOptions).then((result) => {608 // The JavaScript bindings convert c_style_names to camelCase names.609 const options = new device.mojom.XRPresentationTransportOptions();610 options.transportMethod =611 device.mojom.XRPresentationTransportMethod.SUBMIT_AS_MAILBOX_HOLDER;612 options.waitForTransferNotification = true;613 options.waitForRenderNotification = true;614 let submit_frame_sink;615 if (result.supportsSession) {616 submit_frame_sink = {617 clientReceiver: this.presentation_provider_.getClientReceiver(),618 provider: this.presentation_provider_.bindProvider(sessionOptions),619 transportOptions: options620 };621 const dataProviderPtr = new device.mojom.XRFrameDataProviderPtr();622 const dataProviderRequest = mojo.makeRequest(dataProviderPtr);623 this.dataProviderBinding_ = new mojo.Binding(624 device.mojom.XRFrameDataProvider, this, dataProviderRequest);625 const clientReceiver = mojo.makeRequest(this.sessionClient_);626 const enabled_features = [];627 for (let i = 0; i < sessionOptions.requiredFeatures.length; i++) {628 if (this.supportedFeatures_.indexOf(sessionOptions.requiredFeatures[i]) !== -1) {629 enabled_features.push(sessionOptions.requiredFeatures[i]);630 } else {631 return Promise.resolve({session: null});632 }633 }634 for (let i =0; i < sessionOptions.optionalFeatures.length; i++) {635 if (this.supportedFeatures_.indexOf(sessionOptions.optionalFeatures[i]) !== -1) {636 enabled_features.push(sessionOptions.optionalFeatures[i]);637 }638 }639 return Promise.resolve({640 session: {641 submitFrameSink: submit_frame_sink,642 dataProvider: dataProviderPtr,643 clientReceiver: clientReceiver,644 displayInfo: this.displayInfo_,645 enabledFeatures: enabled_features,646 }647 });648 } else {649 return Promise.resolve({session: null});650 }651 });652 }653 runtimeSupportsSession(options) {654 return Promise.resolve({655 supportsSession: this.supportedModes_.includes(options.mode)656 });657 }658 // Private functions - hit test implementation:659 // Modifies passed in frameData to add hit test results.660 _calculateHitTestResults(frameData) {661 if (!this.supportedModes_.includes(device.mojom.XRSessionMode.kImmersiveAr)) {662 return;663 }664 frameData.hitTestSubscriptionResults = new device.mojom.XRHitTestSubscriptionResultsData();665 frameData.hitTestSubscriptionResults.results = [];666 frameData.hitTestSubscriptionResults.transientInputResults = [];667 if (!this.world_) {668 return;669 }670 // Non-transient hit test:671 for (const [id, subscription] of this.hitTestSubscriptions_) {672 const mojo_from_native_origin = this._getMojoFromNativeOrigin(subscription.nativeOriginInformation);673 if (!mojo_from_native_origin) continue;674 const [mojo_ray_origin, mojo_ray_direction] = this._transformRayToMojoSpace(675 subscription.ray,676 mojo_from_native_origin677 );678 const results = this._hitTestWorld(mojo_ray_origin, mojo_ray_direction, subscription.entityTypes);679 const result = new device.mojom.XRHitTestSubscriptionResultData();680 result.subscriptionId = id;681 result.hitTestResults = results;682 frameData.hitTestSubscriptionResults.results.push(result);683 }684 // Transient hit test:685 const mojo_from_viewer = this._getMojoFromViewer();686 for (const [id, subscription] of this.transientHitTestSubscriptions_) {687 const result = new device.mojom.XRHitTestTransientInputSubscriptionResultData();688 result.subscriptionId = id;689 result.inputSourceIdToHitTestResults = new Map();690 // Find all input sources that match the profile name:691 const matching_input_sources = Array.from(this.input_sources_.values())692 .filter(input_source => input_source.profiles_.includes(subscription.profileName));693 for (const input_source of matching_input_sources) {694 const mojo_from_native_origin = this._getMojoFromInputSource(mojo_from_viewer, input_source);695 const [mojo_ray_origin, mojo_ray_direction] = this._transformRayToMojoSpace(696 subscription.ray,697 mojo_from_native_origin698 );699 const results = this._hitTestWorld(mojo_ray_origin, mojo_ray_direction, subscription.entityTypes);700 result.inputSourceIdToHitTestResults.set(input_source.source_id_, results);701 }702 frameData.hitTestSubscriptionResults.transientInputResults.push(result);703 }704 }705 // Returns 2-element array [origin, direction] of a ray in mojo space.706 // |ray| is expressed relative to native origin.707 _transformRayToMojoSpace(ray, mojo_from_native_origin) {708 const ray_origin = {709 x: ray.origin.x,710 y: ray.origin.y,711 z: ray.origin.z,712 w: 1713 };714 const ray_direction = {715 x: ray.direction.x,716 y: ray.direction.y,717 z: ray.direction.z,718 w: 0719 };720 const mojo_ray_origin = XRMathHelper.transform_by_matrix(721 mojo_from_native_origin,722 ray_origin);723 const mojo_ray_direction = XRMathHelper.transform_by_matrix(724 mojo_from_native_origin,725 ray_direction);726 return [mojo_ray_origin, mojo_ray_direction];727 }728 // Hit tests the passed in ray (expressed as origin and direction) against the mocked world data.729 _hitTestWorld(origin, direction, entityTypes) {730 let result = [];731 for (const region of this.world_.hitTestRegions) {732 const partial_result = this._hitTestRegion(733 region,734 origin, direction,735 entityTypes);736 result = result.concat(partial_result);737 }738 return result.sort((lhs, rhs) => lhs.distance - rhs.distance);739 }740 // Hit tests the passed in ray (expressed as origin and direction) against world region.741 // |entityTypes| is a set of FakeXRRegionTypes.742 // |region| is FakeXRRegion.743 // Returns array of XRHitResults, each entry will be decorated with the distance from the ray origin (along the ray).744 _hitTestRegion(region, origin, direction, entityTypes) {745 const regionNameToMojoEnum = {746 "point":device.mojom.EntityTypeForHitTest.POINT,747 "plane":device.mojom.EntityTypeForHitTest.PLANE,748 "mesh":null749 };750 if (!entityTypes.includes(regionNameToMojoEnum[region.type])) {751 return [];752 }753 const result = [];754 for (const face of region.faces) {755 const maybe_hit = this._hitTestFace(face, origin, direction);756 if (maybe_hit) {757 result.push(maybe_hit);758 }759 }760 // The results should be sorted by distance and there should be no 2 entries with761 // the same distance from ray origin - that would mean they are the same point.762 // This situation is possible when a ray intersects the region through an edge shared763 // by 2 faces.764 return result.sort((lhs, rhs) => lhs.distance - rhs.distance)765 .filter((val, index, array) => index === 0 || val.distance !== array[index - 1].distance);766 }767 // Hit tests the passed in ray (expressed as origin and direction) against a single face.768 // |face|, |origin|, and |direction| are specified in world (aka mojo) coordinates.769 // |face| is an array of DOMPointInits.770 // Returns null if the face does not intersect with the ray, otherwise the result is771 // an XRHitResult with matrix describing the pose of the intersection point.772 _hitTestFace(face, origin, direction) {773 const add = XRMathHelper.add;774 const sub = XRMathHelper.sub;775 const mul = XRMathHelper.mul;776 const normalize = XRMathHelper.normalize;777 const dot = XRMathHelper.dot;778 const cross = XRMathHelper.cross;779 const neg = XRMathHelper.neg;780 //1. Calculate plane normal in world coordinates.781 const point_A = face.vertices[0];782 const point_B = face.vertices[1];783 const point_C = face.vertices[2];784 const edge_AB = sub(point_B, point_A);785 const edge_AC = sub(point_C, point_A);786 const normal = normalize(cross(edge_AB, edge_AC));787 const numerator = dot(sub(point_A, origin), normal);788 const denominator = dot(direction, normal);789 if (Math.abs(denominator) < XRMathHelper.EPSILON) {790 // Planes are nearly parallel - there's either infinitely many intersection points or 0.791 // Both cases signify a "no hit" for us.792 return null;793 } else {794 // Single intersection point between the infinite plane and the line (*not* ray).795 // Need to calculate the hit test matrix taking into account the face vertices.796 const distance = numerator / denominator;797 if (distance < 0) {798 // Line - plane intersection exists, but not the half-line - plane does not.799 return null;800 } else {801 const intersection_point = add(origin, mul(distance, direction));802 // Since we are treating the face as a solid, flip the normal so that its803 // half-space will contain the ray origin.804 const y_axis = denominator > 0 ? neg(normal) : normal;805 let z_axis = null;806 const cos_direction_and_y_axis = dot(direction, y_axis);807 if (Math.abs(cos_direction_and_y_axis) > (1 - XRMathHelper.EPSILON)) {808 // Ray and the hit test normal are co-linear - try using the 'up' or 'right' vector's projection on the face plane as the Z axis.809 // Note: this edge case is currently not covered by the spec.810 const up = {x: 0.0, y: 1.0, z: 0.0, w: 0.0};811 const right = {x: 1.0, y: 0.0, z: 0.0, w: 0.0};812 z_axis = Math.abs(dot(up, y_axis)) > (1 - XRMathHelper.EPSILON)813 ? sub(up, mul(dot(right, y_axis), y_axis)) // `up is also co-linear with hit test normal, use `right`814 : sub(up, mul(dot(up, y_axis), y_axis)); // `up` is not co-linear with hit test normal, use it815 } else {816 // Project the ray direction onto the plane, negate it and use as a Z axis.817 z_axis = neg(sub(direction, mul(cos_direction_and_y_axis, y_axis))); // Z should point towards the ray origin, not away.818 }819 const x_axis = normalize(cross(y_axis, z_axis));820 // Filter out the points not in polygon.821 if (!XRMathHelper.pointInFace(intersection_point, face)) {822 return null;823 }824 const hitResult = new device.mojom.XRHitResult();825 hitResult.hitMatrix = new gfx.mojom.Transform();826 hitResult.distance = distance; // Extend the object with additional information used by higher layers.827 // It will not be serialized over mojom.828 hitResult.hitMatrix.matrix = new Array(16);829 hitResult.hitMatrix.matrix[0] = x_axis.x;830 hitResult.hitMatrix.matrix[1] = x_axis.y;831 hitResult.hitMatrix.matrix[2] = x_axis.z;832 hitResult.hitMatrix.matrix[3] = 0;833 hitResult.hitMatrix.matrix[4] = y_axis.x;834 hitResult.hitMatrix.matrix[5] = y_axis.y;835 hitResult.hitMatrix.matrix[6] = y_axis.z;836 hitResult.hitMatrix.matrix[7] = 0;837 hitResult.hitMatrix.matrix[8] = z_axis.x;838 hitResult.hitMatrix.matrix[9] = z_axis.y;839 hitResult.hitMatrix.matrix[10] = z_axis.z;840 hitResult.hitMatrix.matrix[11] = 0;841 hitResult.hitMatrix.matrix[12] = intersection_point.x;842 hitResult.hitMatrix.matrix[13] = intersection_point.y;843 hitResult.hitMatrix.matrix[14] = intersection_point.z;844 hitResult.hitMatrix.matrix[15] = 1;845 return hitResult;846 }847 }848 }849 _getMojoFromInputSource(mojo_from_viewer, input_source) {850 if (input_source.target_ray_mode_ === 'gaze') { // XRTargetRayMode::GAZING851 // If the pointer origin is gaze, then the result is852 // just mojo_from_viewer.853 return mojo_from_viewer;854 } else if (input_source.target_ray_mode_ === 'tracked-pointer') { // XRTargetRayMode:::POINTING855 // If the pointer origin is tracked-pointer, the result is just856 // mojo_from_input*input_from_pointer.857 return XRMathHelper.mul4x4(858 input_source.mojo_from_input_.matrix,859 input_source.input_from_pointer_.matrix);860 } else if (input_source.target_ray_mode_ === 'screen') { // XRTargetRayMode::TAPPING861 // If the pointer origin is screen, the input_from_pointer is862 // equivalent to viewer_from_pointer and the result is863 // mojo_from_viewer*viewer_from_pointer.864 return XRMathHelper.mul4x4(865 mojo_from_viewer,866 input_source.input_from_pointer_.matrix);867 } else {868 return null;869 }870 }871 _getMojoFromViewer() {872 const transform = {873 position: [874 this.pose_.position.x,875 this.pose_.position.y,876 this.pose_.position.z],877 orientation: [878 this.pose_.orientation.x,879 this.pose_.orientation.y,880 this.pose_.orientation.z,881 this.pose_.orientation.w],882 };883 return getMatrixFromTransform(transform);884 }885 _getMojoFromNativeOrigin(nativeOriginInformation) {886 const identity = function() {887 return [888 1, 0, 0, 0,889 0, 1, 0, 0,890 0, 0, 1, 0,891 0, 0, 0, 1892 ];893 };894 const mojo_from_viewer = this._getMojoFromViewer();895 if (nativeOriginInformation.$tag == device.mojom.XRNativeOriginInformation.Tags.inputSourceId) {896 if (!this.input_sources_.has(nativeOriginInformation.inputSourceId)) {897 return null;898 } else {899 const inputSource = this.input_sources_.get(nativeOriginInformation.inputSourceId);900 return this._getMojoFromInputSource(mojo_from_viewer, inputSource);901 }902 } else if (nativeOriginInformation.$tag == device.mojom.XRNativeOriginInformation.Tags.referenceSpaceCategory) {903 switch (nativeOriginInformation.referenceSpaceCategory) {904 case device.mojom.XRReferenceSpaceCategory.LOCAL:905 return identity();906 case device.mojom.XRReferenceSpaceCategory.LOCAL_FLOOR:907 if (this.stageParameters_ == null || this.stageParameters_.standingTransform == null) {908 console.warn("Standing transform not available.");909 return null;910 }911 // this.stageParameters_.standingTransform = floor_from_mojo aka native_origin_from_mojo912 return XRMathHelper.inverse(this.stageParameters_.standingTransform.matrix);913 case device.mojom.XRReferenceSpaceCategory.VIEWER:914 return mojo_from_viewer;915 case device.mojom.XRReferenceSpaceCategory.BOUNDED_FLOOR:916 return null;917 case device.mojom.XRReferenceSpaceCategory.UNBOUNDED:918 return null;919 default:920 throw new TypeError("Unrecognized XRReferenceSpaceCategory!");921 }922 } else {923 // Anchors & planes are not yet supported for hit test.924 return null;925 }926 }927}928class MockXRSessionMetricsRecorder {929 reportFeatureUsed(feature) {930 // Do nothing931 }932}933class MockXRInputSource {934 constructor(fakeInputSourceInit, id, pairedDevice) {935 this.source_id_ = id;936 this.pairedDevice_ = pairedDevice;937 this.handedness_ = fakeInputSourceInit.handedness;938 this.target_ray_mode_ = fakeInputSourceInit.targetRayMode;939 if (fakeInputSourceInit.pointerOrigin == null) {940 throw new TypeError("FakeXRInputSourceInit.pointerOrigin is required.");941 }942 this.setPointerOrigin(fakeInputSourceInit.pointerOrigin);943 this.setProfiles(fakeInputSourceInit.profiles);944 this.primary_input_pressed_ = false;945 if (fakeInputSourceInit.selectionStarted != null) {946 this.primary_input_pressed_ = fakeInputSourceInit.selectionStarted;947 }948 this.primary_input_clicked_ = false;949 if (fakeInputSourceInit.selectionClicked != null) {950 this.primary_input_clicked_ = fakeInputSourceInit.selectionClicked;951 }952 this.primary_squeeze_pressed_ = false;953 this.primary_squeeze_clicked_ = false;954 this.mojo_from_input_ = null;955 if (fakeInputSourceInit.gripOrigin != null) {956 this.setGripOrigin(fakeInputSourceInit.gripOrigin);957 }958 // This properly handles if supportedButtons were not specified.959 this.setSupportedButtons(fakeInputSourceInit.supportedButtons);960 this.emulated_position_ = false;961 this.desc_dirty_ = true;962 }963 // Webxr-test-api964 setHandedness(handedness) {965 if (this.handedness_ != handedness) {966 this.desc_dirty_ = true;967 this.handedness_ = handedness;968 }969 }970 setTargetRayMode(targetRayMode) {971 if (this.target_ray_mode_ != targetRayMode) {972 this.desc_dirty_ = true;973 this.target_ray_mode_ = targetRayMode;974 }975 }976 setProfiles(profiles) {977 this.desc_dirty_ = true;978 this.profiles_ = profiles;979 }980 setGripOrigin(transform, emulatedPosition = false) {981 // grip_origin was renamed to mojo_from_input in mojo982 this.mojo_from_input_ = new gfx.mojom.Transform();983 this.mojo_from_input_.matrix = getMatrixFromTransform(transform);984 this.emulated_position_ = emulatedPosition;985 }986 clearGripOrigin() {987 // grip_origin was renamed to mojo_from_input in mojo988 if (this.mojo_from_input_ != null) {989 this.mojo_from_input_ = null;990 this.emulated_position_ = false;991 }992 }993 setPointerOrigin(transform, emulatedPosition = false) {994 // pointer_origin was renamed to input_from_pointer in mojo995 this.desc_dirty_ = true;996 this.input_from_pointer_ = new gfx.mojom.Transform();997 this.input_from_pointer_.matrix = getMatrixFromTransform(transform);998 this.emulated_position_ = emulatedPosition;999 }1000 disconnect() {1001 this.pairedDevice_.removeInputSource(this);1002 }1003 reconnect() {1004 this.pairedDevice_.addInputSource(this);1005 }1006 startSelection() {1007 this.primary_input_pressed_ = true;1008 if (this.gamepad_) {1009 this.gamepad_.buttons[0].pressed = true;1010 this.gamepad_.buttons[0].touched = true;1011 }1012 }1013 endSelection() {1014 if (!this.primary_input_pressed_) {1015 throw new Error("Attempted to end selection which was not started");1016 }1017 this.primary_input_pressed_ = false;1018 this.primary_input_clicked_ = true;1019 if (this.gamepad_) {1020 this.gamepad_.buttons[0].pressed = false;1021 this.gamepad_.buttons[0].touched = false;1022 }1023 }1024 simulateSelect() {1025 this.primary_input_clicked_ = true;1026 }1027 setSupportedButtons(supportedButtons) {1028 this.gamepad_ = null;1029 this.supported_buttons_ = [];1030 // If there are no supported buttons, we can stop now.1031 if (supportedButtons == null || supportedButtons.length < 1) {1032 return;1033 }1034 const supported_button_map = {};1035 this.gamepad_ = this.getEmptyGamepad();1036 for (let i = 0; i < supportedButtons.length; i++) {1037 const buttonType = supportedButtons[i].buttonType;1038 this.supported_buttons_.push(buttonType);1039 supported_button_map[buttonType] = supportedButtons[i];1040 }1041 // Let's start by building the button state in order of priority:1042 // Primary button is index 0.1043 this.gamepad_.buttons.push({1044 pressed: this.primary_input_pressed_,1045 touched: this.primary_input_pressed_,1046 value: this.primary_input_pressed_ ? 1.0 : 0.01047 });1048 // Now add the rest of our buttons1049 this.addGamepadButton(supported_button_map['grip']);1050 this.addGamepadButton(supported_button_map['touchpad']);1051 this.addGamepadButton(supported_button_map['thumbstick']);1052 this.addGamepadButton(supported_button_map['optional-button']);1053 this.addGamepadButton(supported_button_map['optional-thumbstick']);1054 // Finally, back-fill placeholder buttons/axes1055 for (let i = 0; i < this.gamepad_.buttons.length; i++) {1056 if (this.gamepad_.buttons[i] == null) {1057 this.gamepad_.buttons[i] = {1058 pressed: false,1059 touched: false,1060 value: 01061 };1062 }1063 }1064 for (let i=0; i < this.gamepad_.axes.length; i++) {1065 if (this.gamepad_.axes[i] == null) {1066 this.gamepad_.axes[i] = 0;1067 }1068 }1069 }1070 updateButtonState(buttonState) {1071 if (this.supported_buttons_.indexOf(buttonState.buttonType) == -1) {1072 throw new Error("Tried to update state on an unsupported button");1073 }1074 const buttonIndex = this.getButtonIndex(buttonState.buttonType);1075 const axesStartIndex = this.getAxesStartIndex(buttonState.buttonType);1076 if (buttonIndex == -1) {1077 throw new Error("Unknown Button Type!");1078 }1079 // is this a 'squeeze' button?1080 if (buttonIndex === this.getButtonIndex('grip')) {1081 // squeeze1082 if (buttonState.pressed) {1083 this.primary_squeeze_pressed_ = true;1084 } else if (this.gamepad_.buttons[buttonIndex].pressed) {1085 this.primary_squeeze_clicked_ = true;1086 this.primary_squeeze_pressed_ = false;1087 } else {1088 this.primary_squeeze_clicked_ = false;1089 this.primary_squeeze_pressed_ = false;1090 }1091 }1092 this.gamepad_.buttons[buttonIndex].pressed = buttonState.pressed;1093 this.gamepad_.buttons[buttonIndex].touched = buttonState.touched;1094 this.gamepad_.buttons[buttonIndex].value = buttonState.pressedValue;1095 if (axesStartIndex != -1) {1096 this.gamepad_.axes[axesStartIndex] = buttonState.xValue == null ? 0.0 : buttonState.xValue;1097 this.gamepad_.axes[axesStartIndex + 1] = buttonState.yValue == null ? 0.0 : buttonState.yValue;1098 }1099 }1100 // Helpers for Mojom1101 getInputSourceState() {1102 const input_state = new device.mojom.XRInputSourceState();1103 input_state.sourceId = this.source_id_;1104 input_state.primaryInputPressed = this.primary_input_pressed_;1105 input_state.primaryInputClicked = this.primary_input_clicked_;1106 input_state.primarySqueezePressed = this.primary_squeeze_pressed_;1107 input_state.primarySqueezeClicked = this.primary_squeeze_clicked_;1108 // Setting the input source's "clicked" state should generate one "select"1109 // event. Reset the input value to prevent it from continuously generating1110 // events.1111 this.primary_input_clicked_ = false;1112 // Setting the input source's "clicked" state should generate one "squeeze"1113 // event. Reset the input value to prevent it from continuously generating1114 // events.1115 this.primary_squeeze_clicked_ = false;1116 input_state.mojoFromInput = this.mojo_from_input_;1117 input_state.gamepad = this.gamepad_;1118 input_state.emulatedPosition = this.emulated_position_;1119 if (this.desc_dirty_) {1120 const input_desc = new device.mojom.XRInputSourceDescription();1121 switch (this.target_ray_mode_) {1122 case 'gaze':1123 input_desc.targetRayMode = device.mojom.XRTargetRayMode.GAZING;1124 break;1125 case 'tracked-pointer':1126 input_desc.targetRayMode = device.mojom.XRTargetRayMode.POINTING;1127 break;1128 case 'screen':1129 input_desc.targetRayMode = device.mojom.XRTargetRayMode.TAPPING;1130 break;1131 default:1132 throw new Error('Unhandled target ray mode ' + this.target_ray_mode_);1133 }1134 switch (this.handedness_) {1135 case 'left':1136 input_desc.handedness = device.mojom.XRHandedness.LEFT;1137 break;1138 case 'right':1139 input_desc.handedness = device.mojom.XRHandedness.RIGHT;1140 break;1141 default:1142 input_desc.handedness = device.mojom.XRHandedness.NONE;1143 break;1144 }1145 input_desc.inputFromPointer = this.input_from_pointer_;1146 input_desc.profiles = this.profiles_;1147 input_state.description = input_desc;1148 this.desc_dirty_ = false;1149 }1150 // Pointer data for DOM Overlay, set by setOverlayPointerPosition()1151 if (this.overlay_pointer_position_) {1152 input_state.overlayPointerPosition = this.overlay_pointer_position_;1153 this.overlay_pointer_position_ = null;1154 }1155 return input_state;1156 }1157 setOverlayPointerPosition(x, y) {1158 this.overlay_pointer_position_ = {x: x, y: y};1159 }1160 getEmptyGamepad() {1161 // Mojo complains if some of the properties on Gamepad are null, so set1162 // everything to reasonable defaults that tests can override.1163 const gamepad = {1164 connected: true,1165 id: "",1166 timestamp: 0,1167 axes: [],1168 buttons: [],1169 mapping: "xr-standard",1170 display_id: 0,1171 };1172 switch (this.handedness_) {1173 case 'left':1174 gamepad.hand = device.mojom.GamepadHand.GamepadHandLeft;1175 break;1176 case 'right':1177 gamepad.hand = device.mojom.GamepadHand.GamepadHandRight;1178 break;1179 default:1180 gamepad.hand = device.mojom.GamepadHand.GamepadHandNone;1181 break;1182 }1183 return gamepad;1184 }1185 addGamepadButton(buttonState) {1186 if (buttonState == null) {1187 return;1188 }1189 const buttonIndex = this.getButtonIndex(buttonState.buttonType);1190 const axesStartIndex = this.getAxesStartIndex(buttonState.buttonType);1191 if (buttonIndex == -1) {1192 throw new Error("Unknown Button Type!");1193 }1194 this.gamepad_.buttons[buttonIndex] = {1195 pressed: buttonState.pressed,1196 touched: buttonState.touched,1197 value: buttonState.pressedValue1198 };1199 // Add x/y value if supported.1200 if (axesStartIndex != -1) {1201 this.gamepad_.axes[axesStartIndex] = (buttonState.xValue == null ? 0.0 : buttonSate.xValue);1202 this.gamepad_.axes[axesStartIndex + 1] = (buttonState.yValue == null ? 0.0 : buttonSate.yValue);1203 }1204 }1205 // General Helper methods1206 getButtonIndex(buttonType) {1207 switch (buttonType) {1208 case 'grip':1209 return 1;1210 case 'touchpad':1211 return 2;1212 case 'thumbstick':1213 return 3;1214 case 'optional-button':1215 return 4;1216 case 'optional-thumbstick':1217 return 5;1218 default:1219 return -1;1220 }1221 }1222 getAxesStartIndex(buttonType) {1223 switch (buttonType) {1224 case 'touchpad':1225 return 0;1226 case 'thumbstick':1227 return 2;1228 case 'optional-thumbstick':1229 return 4;1230 default:1231 return -1;1232 }1233 }1234}1235// Mojo helper classes1236class MockXRPresentationProvider {1237 constructor() {1238 this.binding_ = new mojo.Binding(device.mojom.XRPresentationProvider, this);1239 this.submit_frame_count_ = 0;1240 this.missing_frame_count_ = 0;1241 }1242 bindProvider(request) {1243 const providerPtr = new device.mojom.XRPresentationProviderPtr();1244 const providerRequest = mojo.makeRequest(providerPtr);1245 this.binding_.close();1246 this.binding_ = new mojo.Binding(1247 device.mojom.XRPresentationProvider, this, providerRequest);1248 return providerPtr;1249 }1250 getClientReceiver() {1251 this.submitFrameClient_ = new device.mojom.XRPresentationClientPtr();1252 return mojo.makeRequest(this.submitFrameClient_);1253 }1254 // XRPresentationProvider mojo implementation1255 submitFrameMissing(frameId, mailboxHolder, timeWaited) {1256 this.missing_frame_count_++;1257 }1258 submitFrame(frameId, mailboxHolder, timeWaited) {1259 this.submit_frame_count_++;1260 // Trigger the submit completion callbacks here. WARNING: The1261 // Javascript-based mojo mocks are *not* re-entrant. It's OK to1262 // wait for these notifications on the next frame, but waiting1263 // within the current frame would never finish since the incoming1264 // calls would be queued until the current execution context finishes.1265 this.submitFrameClient_.onSubmitFrameTransferred(true);1266 this.submitFrameClient_.onSubmitFrameRendered();1267 }1268 // Utility methods1269 Close() {1270 this.binding_.close();1271 }1272}1273// This is a temporary workaround for the fact that spinning up webxr before1274// the mojo interceptors are created will cause the interceptors to not get1275// registered, so we have to create this before we query xr;1276const XRTest = new ChromeXRTest();1277// This test API is also used to run Chrome's internal legacy VR tests; however,1278// those fail if navigator.xr has been used. Those tests will set a bool telling1279// us not to try to check navigator.xr1280if ((typeof legacy_vr_test === 'undefined') || !legacy_vr_test) {1281 // Some tests may run in the http context where navigator.xr isn't exposed1282 // This should just be to test that it isn't exposed, but don't try to set up1283 // the test framework in this case.1284 if (navigator.xr) {1285 navigator.xr.test = XRTest;1286 }1287} else {1288 navigator.vr = { test: XRTest };...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');2console.log(mojo);3const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');4console.log(mojo);5const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');6console.log(mojo);7const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');8console.log(mojo);9const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');10console.log(mojo);11const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');12console.log(mojo);13const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');14console.log(mojo);15const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');16console.log(mojo);17const { mojo_from_native_origin } = require('./mojo_from_native_origin.js');

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require("web-platform-test");2var origin = wpt.mojo_from_native_origin(url);3console.log(origin);4var wpt = require("web-platform-test");5var origin = wpt.mojo_from_native_origin(url);6console.log(origin);

Full Screen

Using AI Code Generation

copy

Full Screen

1var handle = wpt.mojo_from_native_origin(origin);2var handle = wpt.mojo_from_origin(origin);3var handle = wpt.mojo_from_origin(origin);4var handle = wpt.mojo_from_origin(origin);5var handle = wpt.mojo_from_origin(origin);6var handle = wpt.mojo_from_origin(origin);7var handle = wpt.mojo_from_origin(origin);8var handle = wpt.mojo_from_origin(origin);9var handle = wpt.mojo_from_origin(origin);10var handle = wpt.mojo_from_origin(origin);11var handle = wpt.mojo_from_origin(origin);12var handle = wpt.mojo_from_origin(origin);

Full Screen

Using AI Code Generation

copy

Full Screen

1interface Origin {2 NativeOrigin getNativeOrigin();3}4class NativeOrigin {5 static NativeOrigin CreateFromMojoOrigin(const Origin& origin);6 NativeOrigin(const NativeOrigin& other);7 NativeOrigin& operator=(const NativeOrigin& other);8 ~NativeOrigin();9 Origin ToMojoOrigin() const;10};11NativeOrigin::NativeOrigin(const NativeOrigin& other) = default;12NativeOrigin& NativeOrigin::operator=(const NativeOrigin& other) = default;13NativeOrigin::~NativeOrigin() = default;14NativeOrigin NativeOrigin::CreateFromMojoOrigin(const Origin& origin) {15}16Origin NativeOrigin::ToMojoOrigin() const {17}18Origin::NativeOrigin Origin::getNativeOrigin() {19 return NativeOrigin::CreateFromMojoOrigin(this);20}21Origin NativeOrigin::ToMojoOrigin() const {22}

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful