Files

134 lines
6.3 KiB
JavaScript

// 基础 env 模板:提供网页端最小对象,并按描述符、原型链和 native-like 规则固化。
// 复制到 case/result 或 case/env 后,必须根据 fixtures 和 RuyiTrace 证据调整具体字段。
'use strict';
let protect = null;
try { protect = require('./native-protect'); } catch { protect = null; }
function defineValue(obj, key, value, options = {}) {
if (protect && protect.defineValue) return protect.defineValue(obj, key, value, options);
Object.defineProperty(obj, key, {
value,
writable: options.writable ?? false,
enumerable: options.enumerable ?? true,
configurable: options.configurable ?? true,
});
return value;
}
function defineGetter(obj, key, getter, options = {}) {
Object.defineProperty(obj, key, {
get: getter,
enumerable: options.enumerable ?? true,
configurable: options.configurable ?? true,
});
return getter;
}
function markNativeFunction(fn, name) {
if (protect && protect.markNativeFunction) return protect.markNativeFunction(fn, name);
return fn;
}
function markObjectToString(obj, tag) {
if (protect && protect.markObjectToString) return protect.markObjectToString(obj, tag);
try { Object.defineProperty(obj, Symbol.toStringTag, { value: tag, enumerable: false, configurable: true }); } catch {}
return obj;
}
function defineNativeGetter(obj, key, impl, options = {}, addon) {
if (protect && protect.defineNativeGetter) return protect.defineNativeGetter(obj, key, impl, options, addon);
return defineGetter(obj, key, impl, options);
}
function defineNativeValue(obj, key, impl, options = {}, addon) {
if (protect && protect.defineNativeValue) return protect.defineNativeValue(obj, key, impl, options, addon);
return defineValue(obj, key, impl, { writable: true, ...options });
}
function createNativeConstructor(name, length, impl, addon) {
if (protect && protect.createNativeConstructor) return protect.createNativeConstructor(name, length, impl, addon);
function Ctor(...args) { return impl.call(this, new.target ? true : false, ...args); }
try { Object.defineProperty(Ctor, 'name', { value: name, configurable: true }); } catch {}
return markNativeFunction(Ctor, name);
}
function throwIllegalConstructor(name, addon) {
if (protect && protect.throwIllegalConstructor) return protect.throwIllegalConstructor(name, addon);
throw new TypeError(`Failed to construct '${name}': Illegal constructor`);
}
function installBaseEnv(globalObject, fixture = {}, options = {}) {
const addon = options.addon || null;
const browser = fixture.browser || {};
const pageUrl = fixture.pageUrl || fixture.apiUrl || 'https://example.com/';
const url = new URL(pageUrl);
const Window = createNativeConstructor('Window', 0, function Window(isNew) {
return throwIllegalConstructor('Window', addon);
}, addon);
const Location = createNativeConstructor('Location', 0, function Location(isNew) {
return throwIllegalConstructor('Location', addon);
}, addon);
const Navigator = createNativeConstructor('Navigator', 0, function Navigator(isNew) {
return throwIllegalConstructor('Navigator', addon);
}, addon);
defineValue(Window.prototype, 'constructor', Window, { writable: true, enumerable: false, configurable: true });
defineValue(Location.prototype, 'constructor', Location, { writable: true, enumerable: false, configurable: true });
defineValue(Navigator.prototype, 'constructor', Navigator, { writable: true, enumerable: false, configurable: true });
markObjectToString(Window.prototype, 'Window');
markObjectToString(Location.prototype, 'Location');
markObjectToString(Navigator.prototype, 'Navigator');
const location = Object.create(Location.prototype);
const locationState = {
href: url.href,
origin: url.origin,
protocol: url.protocol,
host: url.host,
hostname: url.hostname,
port: url.port,
pathname: url.pathname,
search: url.search,
hash: url.hash,
};
for (const key of ['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash']) {
defineNativeGetter(Location.prototype, key, function () { return locationState[key]; }, { enumerable: true, configurable: true, name: key }, addon);
}
defineNativeValue(Location.prototype, 'toString', function toString() { return this.href; }, { enumerable: false, configurable: true, writable: true, name: 'toString', length: 0 }, addon);
markObjectToString(location, 'Location');
const navigator = Object.create(Navigator.prototype);
const navState = {
userAgent: browser.userAgent || '',
language: browser.language || 'zh-CN',
languages: browser.languages || ['zh-CN', 'zh'],
platform: browser.platform || 'Win32',
hardwareConcurrency: browser.hardwareConcurrency ?? 8,
deviceMemory: browser.deviceMemory ?? 8,
webdriver: browser.webdriver ?? false,
cookieEnabled: browser.cookieEnabled ?? true,
};
for (const key of Object.keys(navState)) {
defineNativeGetter(Navigator.prototype, key, function () { return navState[key]; }, { enumerable: true, configurable: true, name: key }, addon);
}
markObjectToString(navigator, 'Navigator');
defineValue(globalObject, 'Window', Window, { writable: true, enumerable: false, configurable: true });
defineValue(globalObject, 'Location', Location, { writable: true, enumerable: false, configurable: true });
defineValue(globalObject, 'Navigator', Navigator, { writable: true, enumerable: false, configurable: true });
defineValue(globalObject, 'window', globalObject, { writable: false, enumerable: true, configurable: true });
defineValue(globalObject, 'self', globalObject, { writable: false, enumerable: true, configurable: true });
defineValue(globalObject, 'top', globalObject, { writable: false, enumerable: true, configurable: true });
defineValue(globalObject, 'parent', globalObject, { writable: false, enumerable: true, configurable: true });
defineValue(globalObject, 'location', location, { writable: false, enumerable: true, configurable: true });
defineValue(globalObject, 'navigator', navigator, { writable: false, enumerable: true, configurable: true });
markObjectToString(globalObject, 'Window');
return { Window, Location, Navigator, location, navigator };
}
module.exports = { installBaseEnv };