feat: flatpak support (#91)

* Add flatpak manifest

Known issues:
- MOZJS_ARCHIVE need to link manually
- Mozangle build failed due to stddef.h missing
- script build failed due to missing WebIDL python module

* Fix mozangle and script build errors

* Update README.md

* Update gitignore

* Add libmozjs archive

* Import more granular servo crates

* Fix running error in flatpak

* Enable surfman features on Windows

* Enable mozangle features instead

* build(flatpak): add verso desktop file

---------

Co-authored-by: Wu Yuwei <Wu Yu Wei>
Co-authored-by: Jason Tsai <jason@pews.dev>
This commit is contained in:
Ngo Iok Ui (Wu Yu Wei)
2024-08-01 11:27:39 +09:00
committed by GitHub
parent 74addf188b
commit a5bdd6a104
173 changed files with 35625 additions and 331 deletions

1
.gitattributes vendored
View File

@@ -1 +1,2 @@
resources/* linguist-vendored
third_party/* linguist-vendored

3
.gitignore vendored
View File

@@ -3,3 +3,6 @@ Cargo.lock
.DS_Store
.vscode/
resources/
.flatpak-builder/
libmozjs*
cargo-sources.json

184
Cargo.lock generated
View File

@@ -425,10 +425,7 @@ source = "git+https://github.com/servo/servo.git?rev=ed8def2#ed8def28960fd64fa0d
dependencies = [
"bitflags 2.6.0",
"bluetooth_traits",
"blurdroid",
"blurmac",
"blurmock",
"blurz",
"embedder_traits",
"ipc-channel",
"log",
@@ -448,21 +445,6 @@ dependencies = [
"serde",
]
[[package]]
name = "blurdroid"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b23557dd27704797128f9db2816416bef20dad62d4a9768714eeb65f07d296"
[[package]]
name = "blurmac"
version = "0.1.0"
source = "git+https://github.com/servo/servo.git?rev=ed8def2#ed8def28960fd64fa0d00bd67731d594c1042747"
dependencies = [
"log",
"objc",
]
[[package]]
name = "blurmock"
version = "0.1.3"
@@ -472,16 +454,6 @@ dependencies = [
"hex",
]
[[package]]
name = "blurz"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6dae8337ff67fe8ead29a28a0115605753e6a5205d4b6017e9f42f198c3c50a"
dependencies = [
"dbus",
"hex",
]
[[package]]
name = "brotli"
version = "3.5.0"
@@ -816,43 +788,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "compositing"
version = "0.0.1"
source = "git+https://github.com/servo/servo.git?rev=ed8def2#ed8def28960fd64fa0d00bd67731d594c1042747"
dependencies = [
"base",
"canvas",
"compositing_traits",
"crossbeam-channel",
"embedder_traits",
"euclid",
"fnv",
"fonts",
"fonts_traits",
"gleam",
"image 0.24.9",
"ipc-channel",
"keyboard-types",
"libc",
"log",
"net_traits",
"pixels",
"profile_traits",
"script_traits",
"servo-media",
"servo_config",
"servo_geometry",
"servo_url",
"style_traits",
"time 0.1.45",
"toml",
"webrender",
"webrender_api",
"webrender_traits",
"webxr",
]
[[package]]
name = "compositing_traits"
version = "0.0.1"
@@ -1212,16 +1147,6 @@ dependencies = [
"matches",
]
[[package]]
name = "dbus"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819"
dependencies = [
"libc",
"libdbus-sys",
]
[[package]]
name = "deny_public_fields"
version = "0.0.1"
@@ -2823,15 +2748,6 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libdbus-sys"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72"
dependencies = [
"pkg-config",
]
[[package]]
name = "libflate"
version = "0.1.27"
@@ -2871,62 +2787,6 @@ dependencies = [
"redox_syscall 0.4.1",
]
[[package]]
name = "libservo"
version = "0.0.1"
source = "git+https://github.com/servo/servo.git?rev=ed8def2#ed8def28960fd64fa0d00bd67731d594c1042747"
dependencies = [
"background_hang_monitor",
"base",
"bluetooth",
"bluetooth_traits",
"canvas",
"canvas_traits",
"cfg-if",
"compositing",
"compositing_traits",
"constellation",
"crossbeam-channel",
"devtools",
"devtools_traits",
"embedder_traits",
"env_logger",
"euclid",
"fonts",
"fonts_traits",
"gaol",
"gleam",
"ipc-channel",
"keyboard-types",
"layout_thread_2020",
"log",
"media",
"mozangle",
"net",
"net_traits",
"profile",
"profile_traits",
"script",
"script_layout_interface",
"script_traits",
"servo-media",
"servo-media-dummy",
"servo_config",
"servo_geometry",
"servo_url",
"sparkle",
"style",
"style_traits",
"surfman",
"webdriver_server",
"webgpu",
"webrender",
"webrender_api",
"webrender_traits",
"webxr",
"webxr-api",
]
[[package]]
name = "libz-sys"
version = "1.1.18"
@@ -3693,12 +3553,6 @@ dependencies = [
"ttf-parser",
]
[[package]]
name = "owo-colors"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
[[package]]
name = "parking_lot"
version = "0.12.3"
@@ -5562,15 +5416,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "toml_datetime"
version = "0.6.6"
@@ -5821,29 +5666,54 @@ name = "verso"
version = "0.0.1"
dependencies = [
"arboard",
"base",
"block",
"bluetooth",
"bluetooth_traits",
"canvas",
"cargo-packager-resource-resolver",
"cfg_aliases 0.2.1",
"cocoa 0.25.0",
"compositing_traits",
"constellation",
"core-graphics 0.23.2",
"crossbeam-channel",
"devtools",
"embedder_traits",
"env_logger",
"euclid",
"fonts",
"getopts",
"gleam",
"ipc-channel",
"libservo",
"keyboard-types",
"layout_thread_2020",
"log",
"media",
"mozangle",
"net",
"objc",
"objc_id",
"owo-colors",
"profile",
"profile_traits",
"raw-window-handle 0.5.2",
"script",
"script_traits",
"servo-media",
"servo-media-dummy",
"servo_config",
"servo_geometry",
"servo_url",
"sparkle",
"style",
"style_traits",
"surfman",
"thiserror",
"webdriver_server",
"webgpu",
"webrender",
"webrender_api",
"webrender_traits",
"webxr",
"winit",
]

View File

@@ -40,41 +40,66 @@ targets = [
[features]
default = []
packager = ["dep:cargo-packager-resource-resolver"]
flatpak = []
[build-dependencies]
cfg_aliases = "0.2"
[dependencies]
owo-colors = "4.0"
log = "0.4"
thiserror = "1.0"
raw-window-handle = { version = "0.5", features = ["std"] }
arboard = "3.4.0"
# Servo
libservo = { git = "https://github.com/servo/servo.git", rev = "ed8def2", features = ["max_log_level", "native-bluetooth", "webdriver"] }
compositing_traits = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
webdriver_server = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
webrender = { git = "https://github.com/servo/webrender", branch = "0.64", features = ["capture"] }
sparkle = "0.1.26"
webxr = { git = "https://github.com/servo/webxr", features = ["headless"] }
crossbeam-channel = "0.5"
env_logger = "0.10"
euclid = "0.22"
getopts = "0.2.17"
gleam = "0.15"
ipc-channel = "0.18"
keyboard-types = "0.7"
log = "0.4"
raw-window-handle = { version = "0.5", features = ["std"] }
sparkle = "0.1.26"
surfman = { version = "0.9", features = ["chains", "sm-raw-window-handle"] }
thiserror = "1.0"
winit = { version = "0.29", features = ["rwh_05"] }
# Servo repo crates
# libservo = { git = "https://github.com/servo/servo.git", rev = "ed8def2", features = ["max_log_level", "native-bluetooth", "webdriver"] }
base = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
bluetooth = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
bluetooth_traits = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
canvas = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
compositing_traits = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
constellation = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
devtools = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
embedder_traits = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
fonts = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
layout_thread_2020 = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
media = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
net = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
profile = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
profile_traits = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
script = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
script_traits = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
servo_config = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
servo_geometry = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
servo_url = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
webdriver_server = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
webrender_traits = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
webgpu = { git = "https://github.com/servo/servo.git", rev = "ed8def2" }
# Servo org crates
servo-media = { git = "https://github.com/servo/media" }
servo-media-dummy = { git = "https://github.com/servo/media" }
# Servo Compositor
ipc-channel = "0.18"
# Servo
crossbeam-channel = "0.5"
getopts = "0.2.17"
surfman = { version = "0.9", features = ["chains", "sm-raw-window-handle"] }
winit = { version = "0.29", features = ["rwh_05"] }
style = { git = "https://github.com/servo/stylo", branch = "2024-07-16", features = ["servo"] }
style_traits = { git = "https://github.com/servo/stylo", branch = "2024-07-16", features = ["servo"] }
webrender = { git = "https://github.com/servo/webrender", branch = "0.64", features = ["capture"] }
webrender_api = { git = "https://github.com/servo/webrender", branch = "0.64" }
webxr = { git = "https://github.com/servo/webxr", features = ["headless"] }
# Packager feature
cargo-packager-resource-resolver = { version = "0.1.1", features = [
"auto-detect-format",
], optional = true }
[target.'cfg(target_os = "windows")'.dependencies]
libservo = { git = "https://github.com/servo/servo.git", rev = "ed8def2", features = [
"no-wgl",
] }
surfman = { version = "0.9", features = ["sm-angle-default"] }
mozangle = { version = "0.5.1", features = ["egl", "build_dlls"] }
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
block = "0.1"

View File

@@ -48,9 +48,33 @@ cargo run
### Linux
#### Flatpak
For unified environment setup and package experience, we choose Flatpak to build the project from the start.
Please follow [Flatpack Setup](https://flatpak.org/setup/) page to install Flakpak based on your distribution.
- Install flatpak runtimes and extensions:
```sh
flatpak install flathub org.freedesktop.Platform//23.08
flatpak install flathub org.freedesktop.Sdk//23.08
flatpak install flathub org.freedesktop.Sdk.Extension.rust-stable//23.08
flatpak install flathub org.freedesktop.Sdk.Extension.llvm18//23.08
```
- Generate manifests and build:
// TODO Exporting to a repository instead
```sh
python3 ./flatpak-cargo-generator.py ./Cargo.lock -o cargo-sources.json
flatpak-builder --user --install --force-clean target org.versotile.verso.yml
flatpak run org.versotile.verso
```
#### Nix
We also support building Verso in nix shell. But we don't bundle it in nix at the moment.
- For NixOS:
```sh
@@ -63,24 +87,8 @@ nix-shell shell.nix --run 'cargo r'
nix-shell shell.nix --run 'nixGL cargo r'
```
#### Debian-based Distributions
```sh
sudo apt install build-essential python3-pip ccache clang cmake curl \
g++ git gperf libdbus-1-dev libfreetype6-dev libgl1-mesa-dri \
libgles2-mesa-dev libglib2.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-good libgstreamer-plugins-good1.0-dev \
gstreamer1.0-plugins-bad libgstreamer-plugins-bad1.0-dev \
gstreamer1.0-plugins-ugly gstreamer1.0-plugins-base \
libgstreamer-plugins-base1.0-dev gstreamer1.0-libav \
libgstrtspserver-1.0-dev gstreamer1.0-tools libges-1.0-dev \
libharfbuzz-dev liblzma-dev libunwind-dev libunwind-dev libvulkan1 \
libx11-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
libxmu-dev libxmu6 libegl1-mesa-dev llvm-dev m4 xorg-dev \
python3-mako
```
For others, please follow the instructions in [Servo book](https://book.servo.org/hacking/setting-up-your-environment.html#tools-for-linux) to bootstrap first.
If you prefer to build the project without any sandbox, please follow the instructions in [Servo book](https://book.servo.org/hacking/setting-up-your-environment.html#tools-for-linux) to bootstrap.
But please understand we don't triage any build issue without flatpak or nix setup.
## Nightly Release

438
flatpak-cargo-generator.py Normal file
View File

@@ -0,0 +1,438 @@
#!/usr/bin/env python3
__license__ = 'MIT'
import json
from urllib.parse import urlparse, ParseResult, parse_qs
import os
import contextlib
import copy
import glob
import subprocess
import argparse
import logging
import hashlib
import asyncio
from typing import Any, Dict, List, NamedTuple, Optional, Tuple, TypedDict
import aiohttp
import toml
CRATES_IO = 'https://static.crates.io/crates'
CARGO_HOME = 'cargo'
CARGO_CRATES = f'{CARGO_HOME}/vendor'
VENDORED_SOURCES = 'vendored-sources'
GIT_CACHE = 'flatpak-cargo/git'
COMMIT_LEN = 7
@contextlib.contextmanager
def workdir(path: str):
oldpath = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(oldpath)
def canonical_url(url: str) -> ParseResult:
'Converts a string to a Cargo Canonical URL, as per https://github.com/rust-lang/cargo/blob/35c55a93200c84a4de4627f1770f76a8ad268a39/src/cargo/util/canonical_url.rs#L19'
# Hrm. The upstream cargo does not replace those URLs, but if we don't then it doesn't work too well :(
url = url.replace('git+https://', 'https://')
u = urlparse(url)
# It seems cargo drops query and fragment
u = ParseResult(u.scheme, u.netloc, u.path, '', '', '')
u = u._replace(path = u.path.rstrip('/'))
if u.netloc == 'github.com':
u = u._replace(scheme = 'https')
u = u._replace(path = u.path.lower())
if u.path.endswith('.git'):
u = u._replace(path = u.path[:-len('.git')])
return u
def get_git_tarball(repo_url: str, commit: str) -> str:
url = canonical_url(repo_url)
path = url.path.split('/')[1:]
assert len(path) == 2
owner = path[0]
if path[1].endswith('.git'):
repo = path[1].replace('.git', '')
else:
repo = path[1]
if url.hostname == 'github.com':
return f'https://codeload.{url.hostname}/{owner}/{repo}/tar.gz/{commit}'
elif url.hostname.split('.')[0] == 'gitlab': # type: ignore
return f'https://{url.hostname}/{owner}/{repo}/-/archive/{commit}/{repo}-{commit}.tar.gz'
elif url.hostname == 'bitbucket.org':
return f'https://{url.hostname}/{owner}/{repo}/get/{commit}.tar.gz'
else:
raise ValueError(f'Don\'t know how to get tarball for {repo_url}')
async def get_remote_sha256(url: str) -> str:
logging.info(f"started sha256({url})")
sha256 = hashlib.sha256()
async with aiohttp.ClientSession(raise_for_status=True) as http_session:
async with http_session.get(url) as response:
while True:
data = await response.content.read(4096)
if not data:
break
sha256.update(data)
logging.info(f"done sha256({url})")
return sha256.hexdigest()
_TomlType = Dict[str, Any]
def load_toml(tomlfile: str = 'Cargo.lock') -> _TomlType:
with open(tomlfile, 'r') as f:
toml_data = toml.load(f)
return toml_data
def git_repo_name(git_url: str, commit: str) -> str:
name = canonical_url(git_url).path.split('/')[-1]
return f'{name}-{commit[:COMMIT_LEN]}'
def fetch_git_repo(git_url: str, commit: str) -> str:
repo_dir = git_url.replace('://', '_').replace('/', '_')
cache_dir = os.environ.get('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
clone_dir = os.path.join(cache_dir, 'flatpak-cargo', repo_dir)
if not os.path.isdir(os.path.join(clone_dir, '.git')):
subprocess.run(['git', 'clone', '--depth=1', git_url, clone_dir], check=True)
rev_parse_proc = subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=clone_dir, check=True,
stdout=subprocess.PIPE)
head = rev_parse_proc.stdout.decode().strip()
if head[:COMMIT_LEN] != commit[:COMMIT_LEN]:
subprocess.run(['git', 'fetch', 'origin', commit], cwd=clone_dir, check=True)
subprocess.run(['git', 'checkout', commit], cwd=clone_dir, check=True)
# Get the submodules as they might contain dependencies. This is a noop if
# there are no submodules in the repository
subprocess.run(['git', 'submodule', 'update', '--init', '--recursive'], cwd=clone_dir, check=True)
return clone_dir
def update_workspace_keys(pkg, workspace):
for key, item in pkg.items():
# There cannot be a 'workspace' key if the item is not a dict.
if not isinstance(item, dict):
continue;
# Recurse for keys under target.cfg(..)
if key == 'target':
for target in item.values():
update_workspace_keys(target, workspace)
continue;
# dev-dependencies and build-dependencies should reference root dependencies table from workspace
elif key == 'dev-dependencies' or key == 'build-dependencies':
update_workspace_keys(item, workspace.get('dependencies', None))
continue;
if not workspace or not key in workspace:
continue;
workspace_item = workspace[key]
if 'workspace' in item:
if isinstance(workspace_item, dict):
del item['workspace']
for dep_key, workspace_value in workspace_item.items():
# features are additive
if dep_key == 'features' and 'features' in item:
item['features'] += workspace_value
else:
item[dep_key] = workspace_value
elif len(item) > 1:
del item['workspace']
item.update({ 'version': workspace_item })
else:
pkg[key] = workspace_item
else:
update_workspace_keys(item, workspace_item)
class _GitPackage(NamedTuple):
path: str
package: _TomlType
workspace: Optional[_TomlType]
@property
def normalized(self) -> _TomlType:
package = copy.deepcopy(self.package)
if self.workspace is None:
return package
update_workspace_keys(package, self.workspace)
return package
_GitPackagesType = Dict[str, _GitPackage]
async def get_git_repo_packages(git_url: str, commit: str) -> _GitPackagesType:
logging.info('Loading packages from %s', git_url)
git_repo_dir = fetch_git_repo(git_url, commit)
packages: _GitPackagesType = {}
def get_cargo_toml_packages(root_dir: str, workspace: Optional[_TomlType] = None):
assert not os.path.isabs(root_dir) and os.path.isdir(root_dir)
with workdir(root_dir):
if os.path.exists('Cargo.toml'):
cargo_toml = load_toml('Cargo.toml')
workspace = cargo_toml.get('workspace') or workspace
if 'package' in cargo_toml:
packages[cargo_toml['package']['name']] = _GitPackage(
path=os.path.normpath(root_dir),
package=cargo_toml,
workspace=workspace
)
for child in os.scandir(root_dir):
if child.is_dir():
# the workspace can be referenced by any subdirectory
get_cargo_toml_packages(child.path, workspace)
with workdir(git_repo_dir):
get_cargo_toml_packages('.')
assert packages, f"No packages found in {git_repo_dir}"
logging.debug(
'Packages in %s:\n%s',
git_url,
json.dumps(
{k: v.path for k, v in packages.items()},
indent=4,
),
)
return packages
_FlatpakSourceType = Dict[str, Any]
async def get_git_repo_sources(
url: str,
commit: str,
tarball: bool = False,
) -> List[_FlatpakSourceType]:
name = git_repo_name(url, commit)
if tarball:
tarball_url = get_git_tarball(url, commit)
git_repo_sources = [{
'type': 'archive',
'archive-type': 'tar-gzip',
'url': tarball_url,
'sha256': await get_remote_sha256(tarball_url),
'dest': f'{GIT_CACHE}/{name}',
}]
else:
git_repo_sources = [{
'type': 'git',
'url': url,
'commit': commit,
'dest': f'{GIT_CACHE}/{name}',
}]
return git_repo_sources
_GitRepo = TypedDict('_GitRepo', {'lock': asyncio.Lock, 'commits': Dict[str, _GitPackagesType]})
_GitReposType = Dict[str, _GitRepo]
_VendorEntryType = Dict[str, Dict[str, str]]
async def get_git_package_sources(
package: _TomlType,
git_repos: _GitReposType,
) -> Tuple[List[_FlatpakSourceType], _VendorEntryType]:
name = package['name']
source = package['source']
commit = urlparse(source).fragment
assert commit, 'The commit needs to be indicated in the fragement part'
canonical = canonical_url(source)
repo_url = canonical.geturl()
git_repo = git_repos.setdefault(repo_url, {
'commits': {},
'lock': asyncio.Lock(),
})
async with git_repo['lock']:
if commit not in git_repo['commits']:
git_repo['commits'][commit] = await get_git_repo_packages(repo_url, commit)
cargo_vendored_entry: _VendorEntryType = {
repo_url: {
'git': repo_url,
'replace-with': VENDORED_SOURCES,
}
}
rev = parse_qs(urlparse(source).query).get('rev')
tag = parse_qs(urlparse(source).query).get('tag')
branch = parse_qs(urlparse(source).query).get('branch')
if rev:
assert len(rev) == 1
cargo_vendored_entry[repo_url]['rev'] = rev[0]
elif tag:
assert len(tag) == 1
cargo_vendored_entry[repo_url]['tag'] = tag[0]
elif branch:
assert len(branch) == 1
cargo_vendored_entry[repo_url]['branch'] = branch[0]
logging.info("Adding package %s from %s", name, repo_url)
git_pkg = git_repo['commits'][commit][name]
pkg_repo_dir = os.path.join(GIT_CACHE, git_repo_name(repo_url, commit), git_pkg.path)
git_sources: List[_FlatpakSourceType] = [
{
'type': 'shell',
'commands': [
f'cp -r --reflink=auto "{pkg_repo_dir}" "{CARGO_CRATES}/{name}"'
],
},
{
'type': 'inline',
'contents': toml.dumps(git_pkg.normalized),
'dest': f'{CARGO_CRATES}/{name}', #-{version}',
'dest-filename': 'Cargo.toml',
},
{
'type': 'inline',
'contents': json.dumps({'package': None, 'files': {}}),
'dest': f'{CARGO_CRATES}/{name}', #-{version}',
'dest-filename': '.cargo-checksum.json',
}
]
return (git_sources, cargo_vendored_entry)
async def get_package_sources(
package: _TomlType,
cargo_lock: _TomlType,
git_repos: _GitReposType,
) -> Optional[Tuple[List[_FlatpakSourceType], _VendorEntryType]]:
metadata = cargo_lock.get('metadata')
name = package['name']
version = package['version']
if 'source' not in package:
logging.debug('%s has no source', name)
return None
source = package['source']
if source.startswith('git+'):
return await get_git_package_sources(package, git_repos)
key = f'checksum {name} {version} ({source})'
if metadata is not None and key in metadata:
checksum = metadata[key]
elif 'checksum' in package:
checksum = package['checksum']
else:
logging.warning(f'{name} doesn\'t have checksum')
return None
crate_sources = [
{
'type': 'archive',
'archive-type': 'tar-gzip',
'url': f'{CRATES_IO}/{name}/{name}-{version}.crate',
'sha256': checksum,
'dest': f'{CARGO_CRATES}/{name}-{version}',
},
{
'type': 'inline',
'contents': json.dumps({'package': checksum, 'files': {}}),
'dest': f'{CARGO_CRATES}/{name}-{version}',
'dest-filename': '.cargo-checksum.json',
},
]
return (crate_sources, {'crates-io': {'replace-with': VENDORED_SOURCES}})
async def generate_sources(
cargo_lock: _TomlType,
git_tarballs: bool = False,
) -> List[_FlatpakSourceType]:
# {
# "git-repo-url": {
# "lock": asyncio.Lock(),
# "commits": {
# "commit-hash": {
# "package-name": "./relative/package/path"
# }
# }
# }
# }
git_repos: _GitReposType = {}
sources: List[_FlatpakSourceType] = []
package_sources = []
cargo_vendored_sources = {
VENDORED_SOURCES: {'directory': f'{CARGO_CRATES}'},
}
pkg_coros = [get_package_sources(p, cargo_lock, git_repos) for p in cargo_lock['package']]
for pkg in await asyncio.gather(*pkg_coros):
if pkg is None:
continue
else:
pkg_sources, cargo_vendored_entry = pkg
package_sources.extend(pkg_sources)
cargo_vendored_sources.update(cargo_vendored_entry)
logging.debug('Adding collected git repos:\n%s', json.dumps(list(git_repos), indent=4))
git_repo_coros = []
for git_url, git_repo in git_repos.items():
for git_commit in git_repo['commits']:
git_repo_coros.append(get_git_repo_sources(git_url, git_commit, git_tarballs))
sources.extend(sum(await asyncio.gather(*git_repo_coros), []))
sources.extend(package_sources)
logging.debug('Vendored sources:\n%s', json.dumps(cargo_vendored_sources, indent=4))
sources.append({
'type': 'inline',
'contents': toml.dumps({
'source': cargo_vendored_sources,
}),
'dest': CARGO_HOME,
'dest-filename': 'config'
})
return sources
def main():
parser = argparse.ArgumentParser()
parser.add_argument('cargo_lock', help='Path to the Cargo.lock file')
parser.add_argument('-o', '--output', required=False, help='Where to write generated sources')
parser.add_argument('-t', '--git-tarballs', action='store_true', help='Download git repos as tarballs')
parser.add_argument('-d', '--debug', action='store_true')
args = parser.parse_args()
if args.output is not None:
outfile = args.output
else:
outfile = 'generated-sources.json'
if args.debug:
loglevel = logging.DEBUG
else:
loglevel = logging.INFO
logging.basicConfig(level=loglevel)
generated_sources = asyncio.run(generate_sources(load_toml(args.cargo_lock),
git_tarballs=args.git_tarballs))
with open(outfile, 'w') as out:
json.dump(generated_sources, out, indent=4, sort_keys=False)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,9 @@
[Desktop Entry]
Name=Verso
StartupWMClass=verso
Comment=A web browser that plays old world blues to build new world hope.
GenericName=Web Browser
Type=Application
Categories=Network
Icon=org.versotile.verso
Exec=verso

54
org.versotile.verso.yml Normal file
View File

@@ -0,0 +1,54 @@
id: org.versotile.verso
runtime: org.freedesktop.Platform
runtime-version: '23.08'
sdk: org.freedesktop.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
- org.freedesktop.Sdk.Extension.llvm18
finish-args:
- --share=ipc
- --socket=fallback-x11
- --socket=wayland
- --socket=pulseaudio
- --socket=system-bus
- --socket=session-bus
- --share=network
- --device=dri
- --filesystem=home
build-options:
append-path: /usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm18/bin
prepend-ld-library-path: /usr/lib/sdk/llvm18/lib
env:
CARGO_HOME: /run/build/verso/cargo
MOZJS_ARCHIVE: /run/build/verso/libmozjs-x86_64-unknown-linux-gnu.tar.gz
command: verso
modules:
- name: verso
buildsystem: simple
build-commands:
# Install pyhton packages required by script crates
- pip3 install --prefix=/app --no-deps ./third_party/ply
- pip3 install --prefix=/app --no-deps ./third_party/WebIDL/
# Desktop file
- install -d /app/share/applications
- install -Dm755 ./org.versotile.verso.desktop /app/share/applications/${FLATPAK_ID}.desktop
- install -Dm644 ./icons/icon256x256.png /app/share/icons/hicolor/256x256/apps/${FLATPAK_ID}.png
# Build Verso release profile
- cargo --offline fetch --manifest-path Cargo.toml
- cargo --offline build --release --features flatpak
# Install Verso
- install -Dm755 ./target/release/verso -t /app/bin/
- mkdir /app/resources
- cp -r ./resources/* /app/resources
sources:
- cargo-sources.json
- type: dir
path: .
- type: file
# Update the link whenever mozjs-sys is updated
url: https://github.com/servo/mozjs/releases/download/mozjs-sys-v0.115.13-2/libmozjs-x86_64-unknown-linux-gnu.tar.gz
sha256: 1df591ce943a5294cf709a3850a9ed19b6e809d582fabc9dc094f109ef576e55
only-arches:
- x86_64

View File

@@ -3,45 +3,44 @@ use std::ffi::c_void;
use std::rc::Rc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use base::id::{PipelineId, TopLevelBrowsingContextId};
use base::{Epoch, WebRenderEpochToU16};
use compositing_traits::{
CompositionPipeline, CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg,
ForwardedToCompositorMsg, SendableFrameTree,
};
use crossbeam_channel::Sender;
use embedder_traits::Cursor;
use euclid::{Point2D, Scale, Transform3D, Vector2D};
use gleam::gl;
use ipc_channel::ipc;
use log::{debug, error, trace, warn};
use servo::base::id::{PipelineId, TopLevelBrowsingContextId};
use servo::base::{Epoch, WebRenderEpochToU16};
use servo::embedder_traits::Cursor;
use servo::euclid::{Point2D, Scale, Transform3D, Vector2D};
use servo::profile_traits::time::{self as profile_time, profile, ProfilerCategory};
use servo::profile_traits::{mem, time};
use servo::script_traits::CompositorEvent::{
MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent,
};
use servo::script_traits::{
use profile_traits::time::{self as profile_time, profile, ProfilerCategory};
use profile_traits::{mem, time};
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent};
use script_traits::{
AnimationState, AnimationTickType, ConstellationControlMsg, MouseButton, MouseEventType,
ScrollState, TouchEventType, TouchId, WheelDelta, WindowSizeData, WindowSizeType,
};
use servo::servo_geometry::DeviceIndependentPixel;
use servo::style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
use servo::webrender_api::units::{
use servo_geometry::DeviceIndependentPixel;
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
use webrender::{RenderApi, Transaction};
use webrender_api::units::{
DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePoint, LayoutPoint, LayoutRect, LayoutSize,
LayoutVector2D, WorldPoint,
};
use servo::webrender_api::{
use webrender_api::{
BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch,
ExternalScrollId, FontInstanceOptions, HitTestFlags, PipelineId as WebRenderPipelineId,
PropertyBinding, ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation,
SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, TransformStyle,
};
use servo::webrender_traits::display_list::{HitTestInfo, ScrollTree};
use servo::webrender_traits::{
use webrender_traits::display_list::{HitTestInfo, ScrollTree};
use webrender_traits::{
CanvasToCompositorMsg, CompositorHitTestResult, FontToCompositorMsg, ImageUpdate,
NetToCompositorMsg, RenderingContext, ScriptToCompositorMsg, SerializedImageUpdate,
UntrustedNodeAddress,
};
use webrender::{RenderApi, Transaction};
use crate::touch::{TouchAction, TouchHandler};
use crate::webview::WebView;
@@ -68,7 +67,7 @@ pub struct InitialCompositorState {
/// Servo's rendering context
pub rendering_context: RenderingContext,
/// Webrender GL handle
pub webrender_gl: Rc<dyn servo::gl::Gl>,
pub webrender_gl: Rc<dyn gl::Gl>,
/// WebXR registry
pub webxr_main_thread: webxr::MainThreadRegistry,
}
@@ -168,7 +167,7 @@ pub struct IOCompositor {
rendering_context: RenderingContext,
/// The GL bindings for webrender
webrender_gl: Rc<dyn servo::gl::Gl>,
webrender_gl: Rc<dyn gl::Gl>,
/// Some XR devices want to run on the main thread.
pub webxr_main_thread: webxr::MainThreadRegistry,
@@ -1924,7 +1923,7 @@ impl IOCompositor {
.map(|info| info.framebuffer_object)
.unwrap_or(0);
self.webrender_gl
.bind_framebuffer(servo::gl::FRAMEBUFFER, framebuffer_object);
.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
self.assert_gl_framebuffer_complete();
profile(
@@ -2026,7 +2025,7 @@ impl IOCompositor {
self.assert_gl_framebuffer_complete();
// Set the viewport background based on prefs.
let color = servo::config::pref!(shell.background_color.rgba);
let color = servo_config::pref!(shell.background_color.rgba);
gl.clear_color(
color[0] as f32,
color[1] as f32,
@@ -2051,9 +2050,9 @@ impl IOCompositor {
rect.size().width,
rect.size().height,
);
gl.enable(servo::gl::SCISSOR_TEST);
gl.clear(servo::gl::COLOR_BUFFER_BIT);
gl.disable(servo::gl::SCISSOR_TEST);
gl.enable(gl::SCISSOR_TEST);
gl.clear(gl::COLOR_BUFFER_BIT);
gl.disable(gl::SCISSOR_TEST);
}
self.assert_gl_framebuffer_complete();
@@ -2061,7 +2060,7 @@ impl IOCompositor {
#[track_caller]
fn assert_no_gl_error(&self) {
debug_assert_eq!(self.webrender_gl.get_error(), servo::gl::NO_ERROR);
debug_assert_eq!(self.webrender_gl.get_error(), gl::NO_ERROR);
}
#[track_caller]
@@ -2069,10 +2068,9 @@ impl IOCompositor {
debug_assert_eq!(
(
self.webrender_gl.get_error(),
self.webrender_gl
.check_frame_buffer_status(servo::gl::FRAMEBUFFER)
self.webrender_gl.check_frame_buffer_status(gl::FRAMEBUFFER)
),
(servo::gl::NO_ERROR, servo::gl::FRAMEBUFFER_COMPLETE)
(gl::NO_ERROR, gl::FRAMEBUFFER_COMPLETE)
);
}

View File

@@ -1,9 +1,7 @@
use std::{fs, path::PathBuf};
use servo::{
config::opts::{default_opts, set_options, Opts},
embedder_traits::resources::{self, Resource, ResourceReaderMethods},
};
use embedder_traits::resources::{self, Resource, ResourceReaderMethods};
use servo_config::opts::{default_opts, set_options, Opts};
/// Configuration of Verso instance.
#[derive(Clone, Debug)]
@@ -32,21 +30,6 @@ impl Config {
}
}
impl Config {
/// Returns the path to the resources directory.
pub fn resources_dir_path() -> Option<std::path::PathBuf> {
#[cfg(feature = "packager")]
let root_dir = {
use cargo_packager_resource_resolver::{current_format, resources_dir};
current_format().and_then(|format| resources_dir(format))
};
#[cfg(not(feature = "packager"))]
let root_dir = std::env::current_dir();
root_dir.ok().map(|dir| dir.join("resources"))
}
}
struct ResourceReader(PathBuf);
impl ResourceReaderMethods for ResourceReader {

View File

@@ -1,5 +1,5 @@
use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers};
use log::info;
use servo::keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers};
use winit::event::{ElementState, KeyEvent};
use winit::keyboard::{Key as LogicalKey, KeyCode, ModifiersState, NamedKey, PhysicalKey};

View File

@@ -67,7 +67,12 @@ fn resources_dir_path() -> Option<std::path::PathBuf> {
use cargo_packager_resource_resolver::{current_format, resources_dir};
current_format().and_then(|format| resources_dir(format))
};
#[cfg(not(feature = "packager"))]
#[cfg(feature = "flatpak")]
let root_dir = {
use std::str::FromStr;
std::path::PathBuf::from_str("/app")
};
#[cfg(not(any(feature = "packager", feature = "flatpak")))]
let root_dir = std::env::current_dir();
root_dir.ok().map(|dir| dir.join("resources"))

View File

@@ -1,7 +1,7 @@
use euclid::{Point2D, Scale, Vector2D};
use log::warn;
use servo::euclid::{Point2D, Scale, Vector2D};
use servo::script_traits::{EventResult, TouchId};
use servo::style_traits::DevicePixel;
use script_traits::{EventResult, TouchId};
use webrender_api::units::DevicePixel;
use self::TouchState::*;

View File

@@ -5,40 +5,38 @@ use std::{
};
use arboard::Clipboard;
use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothRequest;
use canvas::{
canvas_paint_thread::{self, CanvasPaintThread},
WebGLComm,
};
use compositing_traits::{
CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg, ForwardedToCompositorMsg,
};
use constellation::{Constellation, FromCompositorLogger, InitialConstellationState};
use crossbeam_channel::{unbounded, Sender};
use devtools;
use embedder_traits::{EmbedderProxy, EmbedderReceiver, EventLoopWaker};
use euclid::Scale;
use fonts::FontCacheThread;
use gleam::gl;
use ipc_channel::ipc::{self, IpcSender};
use layout_thread_2020;
use log::{Log, Metadata, Record};
use servo::{
bluetooth::BluetoothThreadFactory,
bluetooth_traits::BluetoothRequest,
canvas::{
canvas_paint_thread::{self, CanvasPaintThread},
WebGLComm,
},
config::{opts, pref},
constellation::{Constellation, FromCompositorLogger, InitialConstellationState},
devtools,
embedder_traits::{EmbedderProxy, EmbedderReceiver, EventLoopWaker},
euclid::Scale,
fonts::FontCacheThread,
gl,
ipc_channel::ipc::{self, IpcSender},
layout_thread_2020,
media::{GlApi, GlContext, NativeDisplay, WindowGLContext},
net::resource_thread,
profile,
script::{self, JSEngineSetup},
script_traits::WindowSizeData,
style,
url::ServoUrl,
webgpu,
webrender_api::*,
webrender_traits::*,
};
use media::{GlApi, GlContext, NativeDisplay, WindowGLContext};
use net::resource_thread;
use profile;
use script::{self, JSEngineSetup};
use script_traits::WindowSizeData;
use servo_config::{opts, pref};
use servo_url::ServoUrl;
use style;
use surfman::GLApi;
use webgpu;
use webrender::{create_webrender_instance, ShaderPrecacheFlags, WebRenderOptions};
use webrender_api::*;
use webrender_traits::*;
use winit::{
event::{Event, StartCause},
event_loop::EventLoopProxy,
@@ -61,7 +59,8 @@ pub struct Verso {
/// and deinitialization of the JS Engine. Multiprocess Servo instances have their
/// own instance that exists in the content process instead.
_js_engine_setup: Option<JSEngineSetup>,
clipboard: Clipboard,
/// FIXME: It's None on wayland in Flatpak. Find a way to support this.
clipboard: Option<Clipboard>,
resource_dir: PathBuf,
}
@@ -363,8 +362,7 @@ impl Verso {
constellation_sender,
embedder_receiver,
_js_engine_setup: js_engine_setup,
clipboard: Clipboard::new()
.expect("Clipboard isn't supported in this platform or desktop environment."),
clipboard: Clipboard::new().ok(),
resource_dir,
};
@@ -435,7 +433,7 @@ impl Verso {
top_level_browsing_context,
msg,
&self.constellation_sender,
&mut self.clipboard,
self.clipboard.as_mut(),
);
}
ShutdownState::FinishedShuttingDown => {

View File

@@ -1,14 +1,11 @@
use arboard::Clipboard;
use base::id::{PipelineId, PipelineNamespace, PipelineNamespaceId, WebViewId};
use compositing_traits::ConstellationMsg;
use crossbeam_channel::Sender;
use servo::{
base::id::{PipelineId, PipelineNamespace, PipelineNamespaceId, WebViewId},
embedder_traits::{CompositorEventVariant, EmbedderMsg, PromptDefinition},
script_traits::TraversalDirection,
url::ServoUrl,
webrender_api::units::DeviceIntRect,
TopLevelBrowsingContextId,
};
use embedder_traits::{CompositorEventVariant, EmbedderMsg, PromptDefinition};
use script_traits::TraversalDirection;
use servo_url::ServoUrl;
use webrender_api::units::DeviceIntRect;
use crate::{verso::send_to_constellation, window::Window};
@@ -47,7 +44,7 @@ impl WebView {
pub fn new_panel(rect: DeviceIntRect) -> Self {
// Reserving a namespace to create TopLevelBrowsingContextId.
PipelineNamespace::install(PipelineNamespaceId(0));
let id = TopLevelBrowsingContextId::new();
let id = WebViewId::new();
Self {
webview_id: id,
pipeline_id: None,
@@ -63,7 +60,7 @@ impl Window {
webview_id: WebViewId,
message: EmbedderMsg,
sender: &Sender<ConstellationMsg>,
clipboard: &mut Clipboard,
clipboard: Option<&mut Clipboard>,
) {
log::trace!("Verso WebView {webview_id:?} is handling Embedder message: {message:?}",);
match message {
@@ -83,13 +80,17 @@ impl Window {
send_to_constellation(sender, ConstellationMsg::AllowNavigationResponse(id, true));
}
EmbedderMsg::GetClipboardContents(sender) => {
let contents = clipboard.get_text().unwrap_or_else(|e| {
log::warn!(
"Verso WebView {webview_id:?} failed to get clipboard content: {}",
e
);
String::new()
});
let contents = clipboard
.map(|c| {
c.get_text().unwrap_or_else(|e| {
log::warn!(
"Verso WebView {webview_id:?} failed to get clipboard content: {}",
e
);
String::new()
})
})
.unwrap_or_default();
if let Err(e) = sender.send(contents) {
log::warn!(
"Verso WebView {webview_id:?} failed to send clipboard content: {}",
@@ -98,12 +99,14 @@ impl Window {
}
}
EmbedderMsg::SetClipboardContents(text) => {
if let Err(e) = clipboard.set_text(text) {
log::warn!(
"Verso WebView {webview_id:?} failed to set clipboard contents: {}",
e
);
}
clipboard.map(|c| {
if let Err(e) = c.set_text(text) {
log::warn!(
"Verso WebView {webview_id:?} failed to set clipboard contents: {}",
e
);
}
});
}
EmbedderMsg::EventDelivered(event) => {
if let CompositorEventVariant::MouseButtonEvent = event {
@@ -122,7 +125,7 @@ impl Window {
panel_id: WebViewId,
message: EmbedderMsg,
sender: &Sender<ConstellationMsg>,
clipboard: &mut Clipboard,
clipboard: Option<&mut Clipboard>,
) {
log::trace!("Verso Panel {panel_id:?} is handling Embedder message: {message:?}",);
match message {
@@ -138,7 +141,7 @@ impl Window {
self.window.request_redraw();
// let demo_url = ServoUrl::parse("https://demo.versotile.org").unwrap();
let demo_url = ServoUrl::parse("https://keyboard-test.space").unwrap();
let demo_id = TopLevelBrowsingContextId::new();
let demo_id = WebViewId::new();
send_to_constellation(sender, ConstellationMsg::NewWebView(demo_url, demo_id));
}
EmbedderMsg::AllowNavigationRequest(id, _url) => {
@@ -203,18 +206,24 @@ impl Window {
}
}
EmbedderMsg::GetClipboardContents(sender) => {
let contents = clipboard.get_text().unwrap_or_else(|e| {
log::warn!("Verso Panel failed to get clipboard content: {}", e);
String::new()
});
let contents = clipboard
.map(|c| {
c.get_text().unwrap_or_else(|e| {
log::warn!("Verso Panel failed to get clipboard content: {}", e);
String::new()
})
})
.unwrap_or_default();
if let Err(e) = sender.send(contents) {
log::warn!("Verso Panel failed to send clipboard content: {}", e);
}
}
EmbedderMsg::SetClipboardContents(text) => {
if let Err(e) = clipboard.set_text(text) {
log::warn!("Verso Panel failed to set clipboard contents: {}", e);
}
clipboard.map(|c| {
if let Err(e) = c.set_text(text) {
log::warn!("Verso Panel failed to set clipboard contents: {}", e);
}
});
}
e => {
log::warn!("Verso Panel isn't supporting this message yet: {e:?}")

View File

@@ -1,23 +1,20 @@
use std::cell::Cell;
use base::id::{PipelineId, WebViewId};
use compositing_traits::ConstellationMsg;
use crossbeam_channel::Sender;
use embedder_traits::{Cursor, EmbedderMsg};
use euclid::{Point2D, Size2D};
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use servo::webrender_api::units::DeviceIntSize;
use servo::{
base::id::{PipelineId, WebViewId},
embedder_traits::{Cursor, EmbedderMsg},
euclid::{Point2D, Size2D},
script_traits::{TouchEventType, WheelDelta, WheelMode},
style_traits::DevicePixel,
webrender_api::{
units::{DeviceIntPoint, DeviceIntRect, DevicePoint, LayoutVector2D},
ScrollLocation,
},
webrender_traits::RenderingContext,
TopLevelBrowsingContextId,
};
use script_traits::{TouchEventType, WheelDelta, WheelMode};
use surfman::{Connection, SurfaceType};
use webrender_api::{
units::{
DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel, DevicePoint, LayoutVector2D,
},
ScrollLocation,
};
use webrender_traits::RenderingContext;
use winit::{
dpi::PhysicalPosition,
event::{ElementState, TouchPhase, WindowEvent},
@@ -102,10 +99,10 @@ impl Window {
compositor.on_mouse_window_move_event_class(cursor);
}
WindowEvent::MouseInput { state, button, .. } => {
let button: servo::script_traits::MouseButton = match button {
winit::event::MouseButton::Left => servo::script_traits::MouseButton::Left,
winit::event::MouseButton::Right => servo::script_traits::MouseButton::Right,
winit::event::MouseButton::Middle => servo::script_traits::MouseButton::Middle,
let button: script_traits::MouseButton = match button {
winit::event::MouseButton::Left => script_traits::MouseButton::Left,
winit::event::MouseButton::Right => script_traits::MouseButton::Right,
winit::event::MouseButton::Middle => script_traits::MouseButton::Middle,
_ => {
log::warn!(
"Verso Window isn't supporting this mouse button yet: {button:?}"
@@ -197,10 +194,10 @@ impl Window {
/// Handle servo messages.
pub fn handle_servo_message(
&mut self,
webview_id: Option<TopLevelBrowsingContextId>,
webview_id: Option<WebViewId>,
message: EmbedderMsg,
sender: &Sender<ConstellationMsg>,
clipboard: &mut Clipboard,
clipboard: Option<&mut Clipboard>,
) {
match webview_id {
// // Handle message in Verso Panel

1
third_party/WebIDL/README vendored Normal file
View File

@@ -0,0 +1 @@
A WebIDL parser written in Python to be used in Mozilla.

1
third_party/WebIDL/UPSTREAM vendored Normal file
View File

@@ -0,0 +1 @@
http://dev.w3.org/cvsweb/~checkout~/2006/webapi/WebIDL/Overview.html?rev=1.409;content-type=text%2Fhtml%3b+charset=utf-8

9529
third_party/WebIDL/WebIDL.py vendored Normal file

File diff suppressed because it is too large Load Diff

3
third_party/WebIDL/__init__.py vendored Normal file
View File

@@ -0,0 +1,3 @@
__version__ = '1.0'
__all__ = ['WebIDL']

Binary file not shown.

10
third_party/WebIDL/abstract.patch vendored Normal file
View File

@@ -0,0 +1,10 @@
--- WebIDL.py
+++ WebIDL.py
@@ -1987,6 +1987,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
or identifier == "RunConstructorInCallerCompartment"
or identifier == "WantsEventListenerHooks"
or identifier == "Serializable"
+ or identifier == "Abstract"
):
# Known extended attributes that do not take values
if not attr.noArguments():

49
third_party/WebIDL/builtin-array.patch vendored Normal file
View File

@@ -0,0 +1,49 @@
--- WebIDL.py
+++ WebIDL.py
@@ -2549,6 +2549,17 @@ class IDLType(IDLObject):
"object",
# Funny stuff
"interface",
+ "int8array",
+ "uint8array",
+ "int16array",
+ "uint16array",
+ "int32array",
+ "uint32array",
+ "float32array",
+ "float64array",
+ "arrayBuffer",
+ "arrayBufferView",
+ "uint8clampedarray",
"dictionary",
"enum",
"callback",
@@ -3842,17 +3853,17 @@ class IDLBuiltinType(IDLType):
Types.utf8string: IDLType.Tags.utf8string,
Types.jsstring: IDLType.Tags.jsstring,
Types.object: IDLType.Tags.object,
- Types.ArrayBuffer: IDLType.Tags.interface,
- Types.ArrayBufferView: IDLType.Tags.interface,
- Types.Int8Array: IDLType.Tags.interface,
- Types.Uint8Array: IDLType.Tags.interface,
- Types.Uint8ClampedArray: IDLType.Tags.interface,
- Types.Int16Array: IDLType.Tags.interface,
- Types.Uint16Array: IDLType.Tags.interface,
- Types.Int32Array: IDLType.Tags.interface,
- Types.Uint32Array: IDLType.Tags.interface,
- Types.Float32Array: IDLType.Tags.interface,
- Types.Float64Array: IDLType.Tags.interface,
+ Types.ArrayBuffer: IDLType.Tags.arrayBuffer,
+ Types.ArrayBufferView: IDLType.Tags.arrayBufferView,
+ Types.Int8Array: IDLType.Tags.int8array,
+ Types.Uint8Array: IDLType.Tags.uint8array,
+ Types.Uint8ClampedArray: IDLType.Tags.uint8clampedarray,
+ Types.Int16Array: IDLType.Tags.int16array,
+ Types.Uint16Array: IDLType.Tags.uint16array,
+ Types.Int32Array: IDLType.Tags.int32array,
+ Types.Uint32Array: IDLType.Tags.uint32array,
+ Types.Float32Array: IDLType.Tags.float32array,
+ Types.Float64Array: IDLType.Tags.float64array,
Types.ReadableStream: IDLType.Tags.interface,
}

View File

@@ -0,0 +1,20 @@
--- WebIDL.py
+++ WebIDL.py
@@ -2283,7 +2283,7 @@ class IDLUnresolvedType(IDLType):
return typedefType.complete(scope).withExtendedAttributes(self.extraTypeAttributes)
elif obj.isCallback() and not obj.isInterface():
assert self.name.name == obj.identifier.name
- return IDLCallbackType(self.location, obj)
+ return IDLCallbackType(obj.location, obj)
name = self.name.resolve(scope, None)
return IDLWrapperType(self.location, obj)
@@ -6854,7 +6854,7 @@ class Parser(Tokenizer):
type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
obj.identifier.name)
elif obj.isCallback() and not obj.isInterface():
- type = IDLCallbackType(self.getLocation(p, 1), obj)
+ type = IDLCallbackType(obj.location, obj)
else:
type = IDLWrapperType(self.getLocation(p, 1), p[1])
p[0] = self.handleNullable(type, p[2])

10
third_party/WebIDL/debug.patch vendored Normal file
View File

@@ -0,0 +1,10 @@
--- WebIDL.py
+++ WebIDL.py
@@ -8827,6 +8827,7 @@ class Parser(Tokenizer):
module=self,
outputdir=outputdir,
errorlog=logger,
+ debug=False,
write_tables=False,
# Pickling the grammar is a speedup in
# some cases (older Python?) but a

View File

@@ -0,0 +1,11 @@
--- WebIDL.py
+++ WebIDL.py
@@ -3490,7 +3490,7 @@ class IDLBuiltinType(IDLType):
[self.location, attribute.location])
assert not self.nullable()
if not attribute.hasValue():
- raise WebIDLError("[TreatNullAs] must take an identifier argument"
+ raise WebIDLError("[TreatNullAs] must take an identifier argument",
[attribute.location])
value = attribute.value()
if value != 'EmptyString':

10
third_party/WebIDL/inline.patch vendored Normal file
View File

@@ -0,0 +1,10 @@
--- WebIDL.py
+++ WebIDL.py
@@ -1988,6 +1988,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
or identifier == "WantsEventListenerHooks"
or identifier == "Serializable"
or identifier == "Abstract"
+ or identifier == "Inline"
):
# Known extended attributes that do not take values
if not attr.noArguments():

View File

@@ -0,0 +1,72 @@
diff --git a/third_party/WebIDL/WebIDL.py b/third_party/WebIDL/WebIDL.py
index 2366e3f702..e1d973f5fe 100644
--- a/third_party/WebIDL/WebIDL.py
+++ b/third_party/WebIDL/WebIDL.py
@@ -9022,6 +9022,67 @@ class Parser(Tokenizer):
itr_iface.asyncIterableInterface = iface
self._productions.append(itr_iface)
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
+ if not iterable:
+ # We haven't run finish() on the interface yet, so we don't know
+ # whether our interface is maplike/setlike/iterable or not. This
+ # means we have to loop through the members to see if we have an
+ # iterable member.
+ for m in iface.members:
+ if isinstance(m, IDLMaplikeOrSetlike):
+ iterable = m
+ break
+ if iterable and (iterable.isSetlike() or iterable.isMaplike()):
+
+ def simpleExtendedAttr(str):
+ return IDLExtendedAttribute(iface.location, (str,))
+
+ if isinstance(iterable, IDLAsyncIterable):
+ nextReturnType = IDLPromiseType(
+ iterable.location, BuiltinTypes[IDLBuiltinType.Types.any]
+ )
+ else:
+ nextReturnType = BuiltinTypes[IDLBuiltinType.Types.object]
+ nextMethod = IDLMethod(
+ iterable.location,
+ IDLUnresolvedIdentifier(iterable.location, "next"),
+ nextReturnType,
+ [],
+ )
+ nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
+
+ methods = [nextMethod]
+
+ if iterable.isSetlike():
+ itr_suffix = "Setlike"
+ else:
+ itr_suffix = "Maplike"
+ itr_ident = IDLUnresolvedIdentifier(
+ iface.location, iface.identifier.name + itr_suffix
+ )
+ classNameOverride = iface.identifier.name + " " + itr_suffix
+ itr_iface = IDLInterface(
+ iface.location,
+ self.globalScope(),
+ itr_ident,
+ None,
+ methods,
+ isKnownNonPartial=True,
+ classNameOverride=classNameOverride,
+ )
+ itr_iface.addExtendedAttributes(
+ [simpleExtendedAttr("LegacyNoInterfaceObject")]
+ )
+ # Make sure the exposure set for the iterator interface is the
+ # same as the exposure set for the iterable interface, because
+ # we're going to generate methods on the iterable that return
+ # instances of the iterator.
+ itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames)
+ # Always append generated iterable interfaces after the
+ # interface they're a member of, otherwise nativeType generation
+ # won't work correctly.
+ itr_iface.iterableInterface = iface
+ self._productions.append(itr_iface)
+ iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
# Make sure we finish IDLIncludesStatements before we finish the
# IDLInterfaces.

162
third_party/WebIDL/readable-stream.patch vendored Normal file
View File

@@ -0,0 +1,162 @@
--- WebIDL.py
+++ WebIDL.py
@@ -2498,6 +2498,9 @@ class IDLType(IDLObject):
def isRecord(self):
return False
+ def isReadableStream(self):
+ return False
+
def isArrayBuffer(self):
return False
@@ -2526,7 +2529,7 @@ class IDLType(IDLObject):
def isSpiderMonkeyInterface(self):
"""Returns a boolean indicating whether this type is an 'interface'
type that is implemented in SpiderMonkey."""
- return self.isInterface() and self.isBufferSource()
+ return self.isInterface() and (self.isBufferSource() or self.isReadableStream())
def isAny(self):
return self.tag() == IDLType.Tags.any
@@ -2743,6 +2746,9 @@ class IDLNullableType(IDLParametrizedType):
def isRecord(self):
return self.inner.isRecord()
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
+
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
@@ -3252,6 +3258,9 @@ class IDLTypedefType(IDLType):
def isRecord(self):
return self.inner.isRecord()
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
+
def isDictionary(self):
return self.inner.isDictionary()
@@ -3597,6 +3606,7 @@ class IDLBuiltinType(IDLType):
"Uint32Array",
"Float32Array",
"Float64Array",
+ "ReadableStream",
)
TagLookup = {
@@ -3632,6 +3642,7 @@ class IDLBuiltinType(IDLType):
Types.Uint32Array: IDLType.Tags.interface,
Types.Float32Array: IDLType.Tags.interface,
Types.Float64Array: IDLType.Tags.interface,
+ Types.ReadableStream: IDLType.Tags.interface,
}
PrettyNames = {
@@ -3667,6 +3678,7 @@ class IDLBuiltinType(IDLType):
Types.Uint32Array: "Uint32Array",
Types.Float32Array: "Float32Array",
Types.Float64Array: "Float64Array",
+ Types.ReadableStream: "ReadableStream",
}
def __init__(
@@ -3830,11 +3842,19 @@ class IDLBuiltinType(IDLType):
and self._typeTag <= IDLBuiltinType.Types.Float64Array
)
+ def isReadableStream(self):
+ return self._typeTag == IDLBuiltinType.Types.ReadableStream
+
def isInterface(self):
# TypedArray things are interface types per the TypedArray spec,
# but we handle them as builtins because SpiderMonkey implements
# all of it internally.
- return self.isArrayBuffer() or self.isArrayBufferView() or self.isTypedArray()
+ return (
+ self.isArrayBuffer()
+ or self.isArrayBufferView()
+ or self.isTypedArray()
+ or self.isReadableStream()
+ )
def isNonCallbackInterface(self):
# All the interfaces we can be are non-callback
@@ -3928,6 +3948,7 @@ class IDLBuiltinType(IDLType):
# ArrayBuffer is distinguishable from everything
# that's not an ArrayBuffer or a callback interface
(self.isArrayBuffer() and not other.isArrayBuffer())
+ or (self.isReadableStream() and not other.isReadableStream())
or
# ArrayBufferView is distinguishable from everything
# that's not an ArrayBufferView or typed array.
@@ -4134,6 +4155,11 @@ BuiltinTypes = {
"Float64Array",
IDLBuiltinType.Types.Float64Array,
),
+ IDLBuiltinType.Types.ReadableStream: IDLBuiltinType(
+ BuiltinLocation("<builtin type>"),
+ "ReadableStream",
+ IDLBuiltinType.Types.ReadableStream,
+ ),
}
@@ -6883,6 +6909,9 @@ class Tokenizer(object):
def t_IDENTIFIER(self, t):
r"[_-]?[A-Za-z][0-9A-Z_a-z-]*"
t.type = self.keywords.get(t.value, "IDENTIFIER")
+ # If Builtin readable streams are disabled, mark ReadableStream as an identifier.
+ if t.type == "READABLESTREAM" and not self._use_builtin_readable_streams:
+ t.type = "IDENTIFIER"
return t
def t_STRING(self, t):
@@ -6973,6 +7002,7 @@ class Tokenizer(object):
"setlike": "SETLIKE",
"iterable": "ITERABLE",
"namespace": "NAMESPACE",
+ "ReadableStream": "READABLESTREAM",
"constructor": "CONSTRUCTOR",
"symbol": "SYMBOL",
"async": "ASYNC",
@@ -6993,7 +7023,8 @@ class Tokenizer(object):
],
)
- def __init__(self, outputdir, lexer=None):
+ def __init__(self, outputdir, lexer=None, use_builtin_readable_streams=True):
+ self._use_builtin_readable_streams = use_builtin_readable_streams
if lexer:
self.lexer = lexer
else:
@@ -8482,6 +8513,7 @@ class Parser(Tokenizer):
"""
DistinguishableType : PrimitiveType Null
| ARRAYBUFFER Null
+ | READABLESTREAM Null
| OBJECT Null
| UNDEFINED Null
"""
@@ -8489,6 +8521,8 @@ class Parser(Tokenizer):
type = BuiltinTypes[IDLBuiltinType.Types.object]
elif p[1] == "ArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
+ elif p[1] == "ReadableStream":
+ type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
elif p[1] == "undefined":
type = BuiltinTypes[IDLBuiltinType.Types.undefined]
else:
@@ -8827,8 +8861,8 @@ class Parser(Tokenizer):
[Location(self.lexer, p.lineno, p.lexpos, self._filename)],
)
- def __init__(self, outputdir="", lexer=None):
- Tokenizer.__init__(self, outputdir, lexer)
+ def __init__(self, outputdir="", lexer=None, use_builtin_readable_stream=True):
+ Tokenizer.__init__(self, outputdir, lexer, use_builtin_readable_stream)
logger = SqueakyCleanLogger()
try:

79
third_party/WebIDL/runtests.py vendored Normal file
View File

@@ -0,0 +1,79 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
import os, sys
import glob
import optparse
import traceback
import WebIDL
class TestHarness(object):
def __init__(self, test, verbose):
self.test = test
self.verbose = verbose
self.printed_intro = False
def start(self):
if self.verbose:
self.maybe_print_intro()
def finish(self):
if self.verbose or self.printed_intro:
print("Finished test %s" % self.test)
def maybe_print_intro(self):
if not self.printed_intro:
print("Starting test %s" % self.test)
self.printed_intro = True
def test_pass(self, msg):
if self.verbose:
print("TEST-PASS | %s" % msg)
def test_fail(self, msg):
self.maybe_print_intro()
print("TEST-UNEXPECTED-FAIL | %s" % msg)
def ok(self, condition, msg):
if condition:
self.test_pass(msg)
else:
self.test_fail(msg)
def check(self, a, b, msg):
if a == b:
self.test_pass(msg)
else:
self.test_fail(msg)
print("\tGot %s expected %s" % (a, b))
def run_tests(tests, verbose):
testdir = os.path.join(os.path.dirname(__file__), 'tests')
if not tests:
tests = glob.iglob(os.path.join(testdir, "*.py"))
sys.path.append(testdir)
for test in tests:
(testpath, ext) = os.path.splitext(os.path.basename(test))
_test = __import__(testpath, globals(), locals(), ['WebIDLTest'])
harness = TestHarness(test, verbose)
harness.start()
try:
_test.WebIDLTest.__call__(WebIDL.Parser(), harness)
except Exception as ex:
print("TEST-UNEXPECTED-FAIL | Unhandled exception in test %s: %s" % (testpath, ex))
traceback.print_exc()
finally:
harness.finish()
if __name__ == '__main__':
usage = """%prog [OPTIONS] [TESTS]
Where TESTS are relative to the tests directory."""
parser = optparse.OptionParser(usage=usage)
parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
help="Don't print passing tests.")
options, tests = parser.parse_args()
run_tests(tests, verbose=options.verbose)

13
third_party/WebIDL/setup.py vendored Normal file
View File

@@ -0,0 +1,13 @@
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setup(name = "WebIDL",
description="A WebIDL parser written in Python to be used in Mozilla.",
version = "1.0.0",
packages = ['.'],
classifiers = [
'Programming Language :: Python :: 3',
]
)

View File

@@ -0,0 +1,19 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface DoubleNull {
attribute any? foo;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,19 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface ArgumentIdentifierConflict {
undefined foo(boolean arg1, boolean arg1);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,22 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
interface Foo {
undefined foo(object constructor);
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should have an interface")
iface = results[0]
harness.check(len(iface.members), 1, "Should have an operation")
operation = iface.members[0]
harness.check(len(operation.signatures()), 1, "Should have one signature")
(retval, args) = operation.signatures()[0]
harness.check(len(args), 1, "Should have an argument")
harness.check(
args[0].identifier.name,
"constructor",
"Should have an identifier named 'constructor'",
)

View File

@@ -0,0 +1,19 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface VoidArgument1 {
void foo(void arg2);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,95 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestArrayBuffer {
attribute ArrayBuffer bufferAttr;
undefined bufferMethod(ArrayBuffer arg1, ArrayBuffer? arg2, sequence<ArrayBuffer> arg3);
attribute ArrayBufferView viewAttr;
undefined viewMethod(ArrayBufferView arg1, ArrayBufferView? arg2, sequence<ArrayBufferView> arg3);
attribute Int8Array int8ArrayAttr;
undefined int8ArrayMethod(Int8Array arg1, Int8Array? arg2, sequence<Int8Array> arg3);
attribute Uint8Array uint8ArrayAttr;
undefined uint8ArrayMethod(Uint8Array arg1, Uint8Array? arg2, sequence<Uint8Array> arg3);
attribute Uint8ClampedArray uint8ClampedArrayAttr;
undefined uint8ClampedArrayMethod(Uint8ClampedArray arg1, Uint8ClampedArray? arg2, sequence<Uint8ClampedArray> arg3);
attribute Int16Array int16ArrayAttr;
undefined int16ArrayMethod(Int16Array arg1, Int16Array? arg2, sequence<Int16Array> arg3);
attribute Uint16Array uint16ArrayAttr;
undefined uint16ArrayMethod(Uint16Array arg1, Uint16Array? arg2, sequence<Uint16Array> arg3);
attribute Int32Array int32ArrayAttr;
undefined int32ArrayMethod(Int32Array arg1, Int32Array? arg2, sequence<Int32Array> arg3);
attribute Uint32Array uint32ArrayAttr;
undefined uint32ArrayMethod(Uint32Array arg1, Uint32Array? arg2, sequence<Uint32Array> arg3);
attribute Float32Array float32ArrayAttr;
undefined float32ArrayMethod(Float32Array arg1, Float32Array? arg2, sequence<Float32Array> arg3);
attribute Float64Array float64ArrayAttr;
undefined float64ArrayMethod(Float64Array arg1, Float64Array? arg2, sequence<Float64Array> arg3);
};
"""
)
results = parser.finish()
iface = results[0]
harness.ok(True, "TestArrayBuffer interface parsed without error")
harness.check(len(iface.members), 22, "Interface should have twenty two members")
members = iface.members
def checkStuff(attr, method, t):
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Expect an IDLAttribute")
harness.ok(isinstance(method, WebIDL.IDLMethod), "Expect an IDLMethod")
harness.check(str(attr.type), t, "Expect an ArrayBuffer type")
harness.ok(attr.type.isSpiderMonkeyInterface(), "Should test as a js interface")
(retType, arguments) = method.signatures()[0]
harness.ok(retType.isUndefined(), "Should have an undefined return type")
harness.check(len(arguments), 3, "Expect 3 arguments")
harness.check(str(arguments[0].type), t, "Expect an ArrayBuffer type")
harness.ok(
arguments[0].type.isSpiderMonkeyInterface(), "Should test as a js interface"
)
harness.check(
str(arguments[1].type), t + "OrNull", "Expect an ArrayBuffer type"
)
harness.ok(
arguments[1].type.inner.isSpiderMonkeyInterface(),
"Should test as a js interface",
)
harness.check(
str(arguments[2].type), t + "Sequence", "Expect an ArrayBuffer type"
)
harness.ok(
arguments[2].type.inner.isSpiderMonkeyInterface(),
"Should test as a js interface",
)
checkStuff(members[0], members[1], "ArrayBuffer")
checkStuff(members[2], members[3], "ArrayBufferView")
checkStuff(members[4], members[5], "Int8Array")
checkStuff(members[6], members[7], "Uint8Array")
checkStuff(members[8], members[9], "Uint8ClampedArray")
checkStuff(members[10], members[11], "Int16Array")
checkStuff(members[12], members[13], "Uint16Array")
checkStuff(members[14], members[15], "Int32Array")
checkStuff(members[16], members[17], "Uint32Array")
checkStuff(members[18], members[19], "Float32Array")
checkStuff(members[20], members[21], "Float64Array")

199
third_party/WebIDL/tests/test_attr.py vendored Normal file
View File

@@ -0,0 +1,199 @@
import WebIDL
def WebIDLTest(parser, harness):
testData = [
("::TestAttr%s::b", "b", "Byte%s", False),
("::TestAttr%s::rb", "rb", "Byte%s", True),
("::TestAttr%s::o", "o", "Octet%s", False),
("::TestAttr%s::ro", "ro", "Octet%s", True),
("::TestAttr%s::s", "s", "Short%s", False),
("::TestAttr%s::rs", "rs", "Short%s", True),
("::TestAttr%s::us", "us", "UnsignedShort%s", False),
("::TestAttr%s::rus", "rus", "UnsignedShort%s", True),
("::TestAttr%s::l", "l", "Long%s", False),
("::TestAttr%s::rl", "rl", "Long%s", True),
("::TestAttr%s::ul", "ul", "UnsignedLong%s", False),
("::TestAttr%s::rul", "rul", "UnsignedLong%s", True),
("::TestAttr%s::ll", "ll", "LongLong%s", False),
("::TestAttr%s::rll", "rll", "LongLong%s", True),
("::TestAttr%s::ull", "ull", "UnsignedLongLong%s", False),
("::TestAttr%s::rull", "rull", "UnsignedLongLong%s", True),
("::TestAttr%s::str", "str", "String%s", False),
("::TestAttr%s::rstr", "rstr", "String%s", True),
("::TestAttr%s::obj", "obj", "Object%s", False),
("::TestAttr%s::robj", "robj", "Object%s", True),
("::TestAttr%s::object", "object", "Object%s", False),
("::TestAttr%s::f", "f", "Float%s", False),
("::TestAttr%s::rf", "rf", "Float%s", True),
]
parser.parse(
"""
interface TestAttr {
attribute byte b;
readonly attribute byte rb;
attribute octet o;
readonly attribute octet ro;
attribute short s;
readonly attribute short rs;
attribute unsigned short us;
readonly attribute unsigned short rus;
attribute long l;
readonly attribute long rl;
attribute unsigned long ul;
readonly attribute unsigned long rul;
attribute long long ll;
readonly attribute long long rll;
attribute unsigned long long ull;
readonly attribute unsigned long long rull;
attribute DOMString str;
readonly attribute DOMString rstr;
attribute object obj;
readonly attribute object robj;
attribute object _object;
attribute float f;
readonly attribute float rf;
};
interface TestAttrNullable {
attribute byte? b;
readonly attribute byte? rb;
attribute octet? o;
readonly attribute octet? ro;
attribute short? s;
readonly attribute short? rs;
attribute unsigned short? us;
readonly attribute unsigned short? rus;
attribute long? l;
readonly attribute long? rl;
attribute unsigned long? ul;
readonly attribute unsigned long? rul;
attribute long long? ll;
readonly attribute long long? rll;
attribute unsigned long long? ull;
readonly attribute unsigned long long? rull;
attribute DOMString? str;
readonly attribute DOMString? rstr;
attribute object? obj;
readonly attribute object? robj;
attribute object? _object;
attribute float? f;
readonly attribute float? rf;
};
"""
)
results = parser.finish()
def checkAttr(attr, QName, name, type, readonly):
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
harness.ok(attr.isAttr(), "Attr is an Attr")
harness.ok(not attr.isMethod(), "Attr is not an method")
harness.ok(not attr.isConst(), "Attr is not a const")
harness.check(attr.identifier.QName(), QName, "Attr has the right QName")
harness.check(attr.identifier.name, name, "Attr has the right name")
harness.check(str(attr.type), type, "Attr has the right type")
harness.check(attr.readonly, readonly, "Attr's readonly state is correct")
harness.ok(True, "TestAttr interface parsed without error.")
harness.check(len(results), 2, "Should be two productions.")
iface = results[0]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(), "::TestAttr", "Interface has the right QName"
)
harness.check(iface.identifier.name, "TestAttr", "Interface has the right name")
harness.check(
len(iface.members), len(testData), "Expect %s members" % len(testData)
)
attrs = iface.members
for i in range(len(attrs)):
data = testData[i]
attr = attrs[i]
(QName, name, type, readonly) = data
checkAttr(attr, QName % "", name, type % "", readonly)
iface = results[1]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(), "::TestAttrNullable", "Interface has the right QName"
)
harness.check(
iface.identifier.name, "TestAttrNullable", "Interface has the right name"
)
harness.check(
len(iface.members), len(testData), "Expect %s members" % len(testData)
)
attrs = iface.members
for i in range(len(attrs)):
data = testData[i]
attr = attrs[i]
(QName, name, type, readonly) = data
checkAttr(attr, QName % "Nullable", name, type % "OrNull", readonly)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[SetterThrows] readonly attribute boolean foo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [SetterThrows] on readonly attributes")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[Throw] readonly attribute boolean foo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should spell [Throws] correctly")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[SameObject] readonly attribute boolean foo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow [SameObject] on attributes not of interface type"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[SameObject] readonly attribute A foo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should allow [SameObject] on attributes of interface type")

View File

@@ -0,0 +1,80 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface AttrSequenceType {
attribute sequence<object> foo;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Attribute type must not be a sequence type")
parser.reset()
threw = False
try:
parser.parse(
"""
interface AttrUnionWithSequenceType {
attribute (sequence<object> or DOMString) foo;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Attribute type must not be a union with a sequence member type")
parser.reset()
threw = False
try:
parser.parse(
"""
interface AttrNullableUnionWithSequenceType {
attribute (sequence<object>? or DOMString) foo;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Attribute type must not be a union with a nullable sequence " "member type",
)
parser.reset()
threw = False
try:
parser.parse(
"\n"
" interface AttrUnionWithUnionWithSequenceType {\n"
" attribute ((sequence<object> or DOMString) or "
"AttrUnionWithUnionWithSequenceType) foo;\n"
" };\n"
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Attribute type must not be a union type with a union member "
"type that has a sequence member type",
)

View File

@@ -0,0 +1,570 @@
import WebIDL
def WebIDLTest(parser, harness):
# Basic functionality
threw = False
try:
parser.parse(
"""
typedef [EnforceRange] long Foo;
typedef [Clamp] long Bar;
typedef [LegacyNullToEmptyString] DOMString Baz;
dictionary A {
required [EnforceRange] long a;
required [Clamp] long b;
[ChromeOnly, EnforceRange] long c;
Foo d;
};
interface B {
attribute Foo typedefFoo;
attribute [EnforceRange] long foo;
attribute [Clamp] long bar;
attribute [LegacyNullToEmptyString] DOMString baz;
undefined method([EnforceRange] long foo, [Clamp] long bar,
[LegacyNullToEmptyString] DOMString baz);
undefined method2(optional [EnforceRange] long foo, optional [Clamp] long bar,
optional [LegacyNullToEmptyString] DOMString baz);
undefined method3(optional [LegacyNullToEmptyString] UTF8String foo = "");
};
interface C {
attribute [EnforceRange] long? foo;
attribute [Clamp] long? bar;
undefined method([EnforceRange] long? foo, [Clamp] long? bar);
undefined method2(optional [EnforceRange] long? foo, optional [Clamp] long? bar);
};
interface Setlike {
setlike<[Clamp] long>;
};
interface Maplike {
maplike<[Clamp] long, [EnforceRange] long>;
};
interface Iterable {
iterable<[Clamp] long, [EnforceRange] long>;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should not have thrown on parsing normal")
if not threw:
harness.check(
results[0].innerType.hasEnforceRange(), True, "Foo is [EnforceRange]"
)
harness.check(results[1].innerType.hasClamp(), True, "Bar is [Clamp]")
harness.check(
results[2].innerType.legacyNullToEmptyString,
True,
"Baz is [LegacyNullToEmptyString]",
)
A = results[3]
harness.check(
A.members[0].type.hasEnforceRange(), True, "A.a is [EnforceRange]"
)
harness.check(A.members[1].type.hasClamp(), True, "A.b is [Clamp]")
harness.check(
A.members[2].type.hasEnforceRange(), True, "A.c is [EnforceRange]"
)
harness.check(
A.members[3].type.hasEnforceRange(), True, "A.d is [EnforceRange]"
)
B = results[4]
harness.check(
B.members[0].type.hasEnforceRange(), True, "B.typedefFoo is [EnforceRange]"
)
harness.check(
B.members[1].type.hasEnforceRange(), True, "B.foo is [EnforceRange]"
)
harness.check(B.members[2].type.hasClamp(), True, "B.bar is [Clamp]")
harness.check(
B.members[3].type.legacyNullToEmptyString,
True,
"B.baz is [LegacyNullToEmptyString]",
)
method = B.members[4].signatures()[0][1]
harness.check(
method[0].type.hasEnforceRange(),
True,
"foo argument of method is [EnforceRange]",
)
harness.check(
method[1].type.hasClamp(), True, "bar argument of method is [Clamp]"
)
harness.check(
method[2].type.legacyNullToEmptyString,
True,
"baz argument of method is [LegacyNullToEmptyString]",
)
method2 = B.members[5].signatures()[0][1]
harness.check(
method2[0].type.hasEnforceRange(),
True,
"foo argument of method2 is [EnforceRange]",
)
harness.check(
method2[1].type.hasClamp(), True, "bar argument of method2 is [Clamp]"
)
harness.check(
method2[2].type.legacyNullToEmptyString,
True,
"baz argument of method2 is [LegacyNullToEmptyString]",
)
method3 = B.members[6].signatures()[0][1]
harness.check(
method3[0].type.legacyNullToEmptyString,
True,
"bar argument of method2 is [LegacyNullToEmptyString]",
)
harness.check(
method3[0].defaultValue.type.isUTF8String(),
True,
"default value of bar argument of method2 is correctly coerced to UTF8String",
)
C = results[5]
harness.ok(C.members[0].type.nullable(), "C.foo is nullable")
harness.ok(C.members[0].type.hasEnforceRange(), "C.foo has [EnforceRange]")
harness.ok(C.members[1].type.nullable(), "C.bar is nullable")
harness.ok(C.members[1].type.hasClamp(), "C.bar has [Clamp]")
method = C.members[2].signatures()[0][1]
harness.ok(method[0].type.nullable(), "foo argument of method is nullable")
harness.ok(
method[0].type.hasEnforceRange(),
"foo argument of method has [EnforceRange]",
)
harness.ok(method[1].type.nullable(), "bar argument of method is nullable")
harness.ok(method[1].type.hasClamp(), "bar argument of method has [Clamp]")
method2 = C.members[3].signatures()[0][1]
harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable")
harness.ok(
method2[0].type.hasEnforceRange(),
"foo argument of method2 has [EnforceRange]",
)
harness.ok(method2[1].type.nullable(), "bar argument of method2 is nullable")
harness.ok(method2[1].type.hasClamp(), "bar argument of method2 has [Clamp]")
# Test [AllowShared]
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [AllowShared] ArrayBufferView Foo;
dictionary A {
required [AllowShared] ArrayBufferView a;
[ChromeOnly, AllowShared] ArrayBufferView b;
Foo c;
};
interface B {
attribute Foo typedefFoo;
attribute [AllowShared] ArrayBufferView foo;
undefined method([AllowShared] ArrayBufferView foo);
undefined method2(optional [AllowShared] ArrayBufferView foo);
};
interface C {
attribute [AllowShared] ArrayBufferView? foo;
undefined method([AllowShared] ArrayBufferView? foo);
undefined method2(optional [AllowShared] ArrayBufferView? foo);
};
interface Setlike {
setlike<[AllowShared] ArrayBufferView>;
};
interface Maplike {
maplike<[Clamp] long, [AllowShared] ArrayBufferView>;
};
interface Iterable {
iterable<[Clamp] long, [AllowShared] ArrayBufferView>;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should not have thrown on parsing normal")
if not threw:
harness.ok(results[0].innerType.hasAllowShared(), "Foo is [AllowShared]")
A = results[1]
harness.ok(A.members[0].type.hasAllowShared(), "A.a is [AllowShared]")
harness.ok(A.members[1].type.hasAllowShared(), "A.b is [AllowShared]")
harness.ok(A.members[2].type.hasAllowShared(), "A.c is [AllowShared]")
B = results[2]
harness.ok(B.members[0].type.hasAllowShared(), "B.typedefFoo is [AllowShared]")
harness.ok(B.members[1].type.hasAllowShared(), "B.foo is [AllowShared]")
method = B.members[2].signatures()[0][1]
harness.ok(
method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]"
)
method2 = B.members[3].signatures()[0][1]
harness.ok(
method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]"
)
C = results[3]
harness.ok(C.members[0].type.nullable(), "C.foo is nullable")
harness.ok(C.members[0].type.hasAllowShared(), "C.foo is [AllowShared]")
method = C.members[1].signatures()[0][1]
harness.ok(method[0].type.nullable(), "foo argument of method is nullable")
harness.ok(
method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]"
)
method2 = C.members[2].signatures()[0][1]
harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable")
harness.ok(
method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]"
)
ATTRIBUTES = [
("[Clamp]", "long"),
("[EnforceRange]", "long"),
("[LegacyNullToEmptyString]", "DOMString"),
("[AllowShared]", "ArrayBufferView"),
]
TEMPLATES = [
(
"required dictionary members",
"""
dictionary Foo {
%s required %s foo;
};
""",
),
(
"optional arguments",
"""
interface Foo {
undefined foo(%s optional %s foo);
};
""",
),
(
"typedefs",
"""
%s typedef %s foo;
""",
),
(
"attributes",
"""
interface Foo {
%s attribute %s foo;
};
""",
),
(
"readonly attributes",
"""
interface Foo {
readonly attribute %s %s foo;
};
""",
),
(
"readonly unresolved attributes",
"""
interface Foo {
readonly attribute Bar baz;
};
typedef %s %s Bar;
""",
),
(
"method",
"""
interface Foo {
%s %s foo();
};
""",
),
(
"interface",
"""
%s
interface Foo {
attribute %s foo;
};
""",
),
(
"partial interface",
"""
interface Foo {
undefined foo();
};
%s
partial interface Foo {
attribute %s bar;
};
""",
),
(
"interface mixin",
"""
%s
interface mixin Foo {
attribute %s foo;
};
""",
),
(
"namespace",
"""
%s
namespace Foo {
attribute %s foo;
};
""",
),
(
"partial namespace",
"""
namespace Foo {
undefined foo();
};
%s
partial namespace Foo {
attribute %s bar;
};
""",
),
(
"dictionary",
"""
%s
dictionary Foo {
%s foo;
};
""",
),
]
for name, template in TEMPLATES:
parser = parser.reset()
threw = False
try:
parser.parse(template % ("", "long"))
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Template for %s parses without attributes" % name)
for attribute, type in ATTRIBUTES:
parser = parser.reset()
threw = False
try:
parser.parse(template % (attribute, type))
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow %s on %s" % (attribute, name))
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [Clamp, EnforceRange] long Foo;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [EnforceRange, Clamp] long Foo;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [Clamp] long Foo;
typedef [EnforceRange] Foo bar;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [EnforceRange] long Foo;
typedef [Clamp] Foo bar;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs")
TYPES = [
"DOMString",
"unrestricted float",
"float",
"unrestricted double",
"double",
]
for type in TYPES:
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [Clamp] %s Foo;
"""
% type
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [Clamp] on %s" % type)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [EnforceRange] %s Foo;
"""
% type
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [EnforceRange] on %s" % type)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [LegacyNullToEmptyString] long Foo;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [LegacyNullToEmptyString] on long")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [LegacyNullToEmptyString] JSString Foo;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [LegacyNullToEmptyString] on JSString")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [LegacyNullToEmptyString] DOMString? Foo;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow [LegacyNullToEmptyString] on nullable DOMString"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [AllowShared] DOMString Foo;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[AllowShared] only allowed on buffer source types")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef [AllowShared=something] ArrayBufferView Foo;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[AllowShared] must take no arguments")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
undefined foo([Clamp] Bar arg);
};
typedef long Bar;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should allow type attributes on unresolved types")
harness.check(
results[0].members[0].signatures()[0][1][0].type.hasClamp(),
True,
"Unresolved types with type attributes should correctly resolve with attributes",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
undefined foo(Bar arg);
};
typedef [Clamp] long Bar;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should allow type attributes on typedefs")
harness.check(
results[0].members[0].signatures()[0][1][0].type.hasClamp(),
True,
"Unresolved types that resolve to typedefs with attributes should correctly resolve with "
"attributes",
)

View File

@@ -0,0 +1,11 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
interface Test {
attribute long b;
};
"""
)
attr = parser.finish()[0].members[0]
harness.check(attr.type.filename, "<builtin>", "Filename on builtin type")

View File

@@ -0,0 +1,59 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestBuiltins {
attribute boolean b;
attribute byte s8;
attribute octet u8;
attribute short s16;
attribute unsigned short u16;
attribute long s32;
attribute unsigned long u32;
attribute long long s64;
attribute unsigned long long u64;
};
"""
)
results = parser.finish()
harness.ok(True, "TestBuiltins interface parsed without error.")
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
iface = results[0]
harness.check(
iface.identifier.QName(), "::TestBuiltins", "Interface has the right QName"
)
harness.check(iface.identifier.name, "TestBuiltins", "Interface has the right name")
harness.check(iface.parent, None, "Interface has no parent")
members = iface.members
harness.check(len(members), 9, "Should be one production")
names = ["b", "s8", "u8", "s16", "u16", "s32", "u32", "s64", "u64", "ts"]
types = [
"Boolean",
"Byte",
"Octet",
"Short",
"UnsignedShort",
"Long",
"UnsignedLong",
"LongLong",
"UnsignedLongLong",
"UnsignedLongLong",
]
for i in range(9):
attr = members[i]
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
harness.check(
attr.identifier.QName(),
"::TestBuiltins::" + names[i],
"Attr has correct QName",
)
harness.check(attr.identifier.name, names[i], "Attr has correct name")
harness.check(str(attr.type), types[i], "Attr type is the correct name")
harness.ok(attr.type.isPrimitive(), "Should be a primitive type")

View File

@@ -0,0 +1,125 @@
# -*- coding: UTF-8 -*-
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestByteString {
attribute ByteString bs;
attribute DOMString ds;
};
"""
)
results = parser.finish()
harness.ok(True, "TestByteString interface parsed without error.")
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
iface = results[0]
harness.check(
iface.identifier.QName(), "::TestByteString", "Interface has the right QName"
)
harness.check(
iface.identifier.name, "TestByteString", "Interface has the right name"
)
harness.check(iface.parent, None, "Interface has no parent")
members = iface.members
harness.check(len(members), 2, "Should be two productions")
attr = members[0]
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
harness.check(
attr.identifier.QName(), "::TestByteString::bs", "Attr has correct QName"
)
harness.check(attr.identifier.name, "bs", "Attr has correct name")
harness.check(str(attr.type), "ByteString", "Attr type is the correct name")
harness.ok(attr.type.isByteString(), "Should be ByteString type")
harness.ok(attr.type.isString(), "Should be String collective type")
harness.ok(not attr.type.isDOMString(), "Should be not be DOMString type")
# now check we haven't broken DOMStrings in the process.
attr = members[1]
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
harness.check(
attr.identifier.QName(), "::TestByteString::ds", "Attr has correct QName"
)
harness.check(attr.identifier.name, "ds", "Attr has correct name")
harness.check(str(attr.type), "String", "Attr type is the correct name")
harness.ok(attr.type.isDOMString(), "Should be DOMString type")
harness.ok(attr.type.isString(), "Should be String collective type")
harness.ok(not attr.type.isByteString(), "Should be not be ByteString type")
# Cannot represent constant ByteString in IDL.
threw = False
try:
parser.parse(
"""
interface ConstByteString {
const ByteString foo = "hello"
};
"""
)
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should have thrown a WebIDL error for ByteString default in interface"
)
# Can have optional ByteStrings with default values
try:
parser.parse(
"""
interface OptionalByteString {
undefined passByteString(optional ByteString arg = "hello");
};
"""
)
parser.finish()
except WebIDL.WebIDLError as e:
harness.ok(
False,
"Should not have thrown a WebIDL error for ByteString "
"default in dictionary. " + str(e),
)
# Can have a default ByteString value in a dictionary
try:
parser.parse(
"""
dictionary OptionalByteStringDict {
ByteString item = "some string";
};
"""
)
parser.finish()
except WebIDL.WebIDLError as e:
harness.ok(
False,
"Should not have thrown a WebIDL error for ByteString "
"default in dictionary. " + str(e),
)
# Don't allow control characters in ByteString literals
threw = False
try:
parser.parse(
"""
dictionary OptionalByteStringDict2 {
ByteString item = "\x03";
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown a WebIDL error for invalid ByteString "
"default in dictionary",
)

View File

@@ -0,0 +1,42 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestCallback {
attribute CallbackType? listener;
};
callback CallbackType = boolean (unsigned long arg);
"""
)
results = parser.finish()
harness.ok(True, "TestCallback interface parsed without error.")
harness.check(len(results), 2, "Should be two productions.")
iface = results[0]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(), "::TestCallback", "Interface has the right QName"
)
harness.check(iface.identifier.name, "TestCallback", "Interface has the right name")
harness.check(len(iface.members), 1, "Expect %s members" % 1)
attr = iface.members[0]
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
harness.ok(attr.isAttr(), "Should be an attribute")
harness.ok(not attr.isMethod(), "Attr is not an method")
harness.ok(not attr.isConst(), "Attr is not a const")
harness.check(
attr.identifier.QName(), "::TestCallback::listener", "Attr has the right QName"
)
harness.check(attr.identifier.name, "listener", "Attr has the right name")
t = attr.type
harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
harness.ok(t.isCallback(), "Attr has the right type")
callback = results[1]
harness.ok(not callback.isConstructor(), "callback is not constructor")

View File

@@ -0,0 +1,84 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestCallbackConstructor {
attribute CallbackConstructorType? constructorAttribute;
};
callback constructor CallbackConstructorType = TestCallbackConstructor (unsigned long arg);
"""
)
results = parser.finish()
harness.ok(True, "TestCallbackConstructor interface parsed without error.")
harness.check(len(results), 2, "Should be two productions.")
iface = results[0]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(),
"::TestCallbackConstructor",
"Interface has the right QName",
)
harness.check(
iface.identifier.name, "TestCallbackConstructor", "Interface has the right name"
)
harness.check(len(iface.members), 1, "Expect %s members" % 1)
attr = iface.members[0]
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
harness.ok(attr.isAttr(), "Should be an attribute")
harness.ok(not attr.isMethod(), "Attr is not an method")
harness.ok(not attr.isConst(), "Attr is not a const")
harness.check(
attr.identifier.QName(),
"::TestCallbackConstructor::constructorAttribute",
"Attr has the right QName",
)
harness.check(
attr.identifier.name, "constructorAttribute", "Attr has the right name"
)
t = attr.type
harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
harness.ok(t.isCallback(), "Attr has the right type")
callback = results[1]
harness.ok(callback.isConstructor(), "Callback is constructor")
parser.reset()
threw = False
try:
parser.parse(
"""
[LegacyTreatNonObjectAsNull]
callback constructor CallbackConstructorType = object ();
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should throw on LegacyTreatNonObjectAsNull callback constructors"
)
parser.reset()
threw = False
try:
parser.parse(
"""
[MOZ_CAN_RUN_SCRIPT_BOUNDARY]
callback constructor CallbackConstructorType = object ();
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not permit MOZ_CAN_RUN_SCRIPT_BOUNDARY callback constructors"
)

View File

@@ -0,0 +1,106 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
callback interface TestCallbackInterface {
attribute boolean bool;
};
"""
)
results = parser.finish()
iface = results[0]
harness.ok(iface.isCallback(), "Interface should be a callback")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestInterface {
};
callback interface TestCallbackInterface : TestInterface {
attribute boolean bool;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow non-callback parent of callback interface")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestInterface : TestCallbackInterface {
};
callback interface TestCallbackInterface {
attribute boolean bool;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow callback parent of non-callback interface")
parser = parser.reset()
parser.parse(
"""
callback interface TestCallbackInterface1 {
undefined foo();
};
callback interface TestCallbackInterface2 {
undefined foo(DOMString arg);
undefined foo(TestCallbackInterface1 arg);
};
callback interface TestCallbackInterface3 {
undefined foo(DOMString arg);
undefined foo(TestCallbackInterface1 arg);
static undefined bar();
};
callback interface TestCallbackInterface4 {
undefined foo(DOMString arg);
undefined foo(TestCallbackInterface1 arg);
static undefined bar();
const long baz = 5;
};
callback interface TestCallbackInterface5 {
static attribute boolean bool;
undefined foo();
};
callback interface TestCallbackInterface6 {
undefined foo(DOMString arg);
undefined foo(TestCallbackInterface1 arg);
undefined bar();
};
callback interface TestCallbackInterface7 {
static attribute boolean bool;
};
callback interface TestCallbackInterface8 {
attribute boolean bool;
};
callback interface TestCallbackInterface9 : TestCallbackInterface1 {
undefined foo();
};
callback interface TestCallbackInterface10 : TestCallbackInterface1 {
undefined bar();
};
"""
)
results = parser.finish()
for i, iface in enumerate(results):
harness.check(
iface.isSingleOperationInterface(),
i < 4,
"Interface %s should be a single operation interface"
% iface.identifier.name,
)

View File

@@ -0,0 +1,160 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions(DOMString a)] undefined foo(boolean arg2);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions(DOMString b)] readonly attribute boolean bar;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions] attribute boolean bar;
};
"""
)
parser.finish()
except Exception as e:
harness.ok(
False,
"Shouldn't have thrown for [CEReactions] used on writable attribute. %s"
% e,
)
threw = True
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions] undefined foo(boolean arg2);
};
"""
)
parser.finish()
except Exception as e:
harness.ok(
False,
"Shouldn't have thrown for [CEReactions] used on regular operations. %s"
% e,
)
threw = True
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions] readonly attribute boolean A;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should have thrown for [CEReactions] used on a readonly attribute"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[CEReactions]
interface Foo {
}
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a interface")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions] getter any(DOMString name);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a named getter")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions] legacycaller double compute(double x);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a legacycaller")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
[CEReactions] stringifier DOMString ();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a stringifier")

View File

@@ -0,0 +1,128 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
dictionary Dict {
any foo;
[ChromeOnly] any bar;
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should have a dictionary")
members = results[0].members
harness.check(len(members), 2, "Should have two members")
# Note that members are ordered lexicographically, so "bar" comes
# before "foo".
harness.ok(
members[0].getExtendedAttribute("ChromeOnly"), "First member is not ChromeOnly"
)
harness.ok(
not members[1].getExtendedAttribute("ChromeOnly"), "Second member is ChromeOnly"
)
parser = parser.reset()
parser.parse(
"""
dictionary Dict {
any foo;
any bar;
};
interface Iface {
[Constant, Cached] readonly attribute Dict dict;
};
"""
)
results = parser.finish()
harness.check(len(results), 2, "Should have a dictionary and an interface")
parser = parser.reset()
exception = None
try:
parser.parse(
"""
dictionary Dict {
any foo;
[ChromeOnly] any bar;
};
interface Iface {
[Constant, Cached] readonly attribute Dict dict;
};
"""
)
results = parser.finish()
except Exception as e:
exception = e
harness.ok(exception, "Should have thrown.")
harness.check(
exception.message,
"[Cached] and [StoreInSlot] must not be used on an attribute "
"whose type contains a [ChromeOnly] dictionary member",
"Should have thrown the right exception",
)
parser = parser.reset()
exception = None
try:
parser.parse(
"""
dictionary ParentDict {
[ChromeOnly] any bar;
};
dictionary Dict : ParentDict {
any foo;
};
interface Iface {
[Constant, Cached] readonly attribute Dict dict;
};
"""
)
results = parser.finish()
except Exception as e:
exception = e
harness.ok(exception, "Should have thrown (2).")
harness.check(
exception.message,
"[Cached] and [StoreInSlot] must not be used on an attribute "
"whose type contains a [ChromeOnly] dictionary member",
"Should have thrown the right exception (2)",
)
parser = parser.reset()
exception = None
try:
parser.parse(
"""
dictionary GrandParentDict {
[ChromeOnly] any baz;
};
dictionary ParentDict : GrandParentDict {
any bar;
};
dictionary Dict : ParentDict {
any foo;
};
interface Iface {
[Constant, Cached] readonly attribute Dict dict;
};
"""
)
results = parser.finish()
except Exception as e:
exception = e
harness.ok(exception, "Should have thrown (3).")
harness.check(
exception.message,
"[Cached] and [StoreInSlot] must not be used on an attribute "
"whose type contains a [ChromeOnly] dictionary member",
"Should have thrown the right exception (3)",
)

96
third_party/WebIDL/tests/test_const.py vendored Normal file
View File

@@ -0,0 +1,96 @@
import WebIDL
expected = [
("::TestConsts::zero", "zero", "Byte", 0),
("::TestConsts::b", "b", "Byte", -1),
("::TestConsts::o", "o", "Octet", 2),
("::TestConsts::s", "s", "Short", -3),
("::TestConsts::us", "us", "UnsignedShort", 4),
("::TestConsts::l", "l", "Long", -5),
("::TestConsts::ul", "ul", "UnsignedLong", 6),
("::TestConsts::ull", "ull", "UnsignedLongLong", 7),
("::TestConsts::ll", "ll", "LongLong", -8),
("::TestConsts::t", "t", "Boolean", True),
("::TestConsts::f", "f", "Boolean", False),
("::TestConsts::fl", "fl", "Float", 0.2),
("::TestConsts::db", "db", "Double", 0.2),
("::TestConsts::ufl", "ufl", "UnrestrictedFloat", 0.2),
("::TestConsts::udb", "udb", "UnrestrictedDouble", 0.2),
("::TestConsts::fli", "fli", "Float", 2),
("::TestConsts::dbi", "dbi", "Double", 2),
("::TestConsts::ufli", "ufli", "UnrestrictedFloat", 2),
("::TestConsts::udbi", "udbi", "UnrestrictedDouble", 2),
]
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestConsts {
const byte zero = 0;
const byte b = -1;
const octet o = 2;
const short s = -3;
const unsigned short us = 0x4;
const long l = -0X5;
const unsigned long ul = 6;
const unsigned long long ull = 7;
const long long ll = -010;
const boolean t = true;
const boolean f = false;
const float fl = 0.2;
const double db = 0.2;
const unrestricted float ufl = 0.2;
const unrestricted double udb = 0.2;
const float fli = 2;
const double dbi = 2;
const unrestricted float ufli = 2;
const unrestricted double udbi = 2;
};
"""
)
results = parser.finish()
harness.ok(True, "TestConsts interface parsed without error.")
harness.check(len(results), 1, "Should be one production.")
iface = results[0]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(), "::TestConsts", "Interface has the right QName"
)
harness.check(iface.identifier.name, "TestConsts", "Interface has the right name")
harness.check(
len(iface.members), len(expected), "Expect %s members" % len(expected)
)
for const, (QName, name, type, value) in zip(iface.members, expected):
harness.ok(isinstance(const, WebIDL.IDLConst), "Should be an IDLConst")
harness.ok(const.isConst(), "Const is a const")
harness.ok(not const.isAttr(), "Const is not an attr")
harness.ok(not const.isMethod(), "Const is not a method")
harness.check(const.identifier.QName(), QName, "Const has the right QName")
harness.check(const.identifier.name, name, "Const has the right name")
harness.check(str(const.type), type, "Const has the right type")
harness.ok(const.type.isPrimitive(), "All consts should be primitive")
harness.check(
str(const.value.type),
str(const.type),
"Const's value has the same type as the type",
)
harness.check(const.value.value, value, "Const value has the right value.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestConsts {
const boolean? zero = 0;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Nullable types are not allowed for consts.")

View File

@@ -0,0 +1,596 @@
import WebIDL
def WebIDLTest(parser, harness):
def checkArgument(argument, QName, name, type, optional, variadic):
harness.ok(isinstance(argument, WebIDL.IDLArgument), "Should be an IDLArgument")
harness.check(
argument.identifier.QName(), QName, "Argument has the right QName"
)
harness.check(argument.identifier.name, name, "Argument has the right name")
harness.check(str(argument.type), type, "Argument has the right return type")
harness.check(
argument.optional, optional, "Argument has the right optional value"
)
harness.check(
argument.variadic, variadic, "Argument has the right variadic value"
)
def checkMethod(
method,
QName,
name,
signatures,
static=True,
getter=False,
setter=False,
deleter=False,
legacycaller=False,
stringifier=False,
chromeOnly=False,
htmlConstructor=False,
secureContext=False,
pref=None,
func=None,
):
harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
harness.ok(method.isMethod(), "Method is a method")
harness.ok(not method.isAttr(), "Method is not an attr")
harness.ok(not method.isConst(), "Method is not a const")
harness.check(method.identifier.QName(), QName, "Method has the right QName")
harness.check(method.identifier.name, name, "Method has the right name")
harness.check(method.isStatic(), static, "Method has the correct static value")
harness.check(method.isGetter(), getter, "Method has the correct getter value")
harness.check(method.isSetter(), setter, "Method has the correct setter value")
harness.check(
method.isDeleter(), deleter, "Method has the correct deleter value"
)
harness.check(
method.isLegacycaller(),
legacycaller,
"Method has the correct legacycaller value",
)
harness.check(
method.isStringifier(),
stringifier,
"Method has the correct stringifier value",
)
harness.check(
method.getExtendedAttribute("ChromeOnly") is not None,
chromeOnly,
"Method has the correct value for ChromeOnly",
)
harness.check(
method.isHTMLConstructor(),
htmlConstructor,
"Method has the correct htmlConstructor value",
)
harness.check(
len(method.signatures()),
len(signatures),
"Method has the correct number of signatures",
)
harness.check(
method.getExtendedAttribute("Pref"),
pref,
"Method has the correct pref value",
)
harness.check(
method.getExtendedAttribute("Func"),
func,
"Method has the correct func value",
)
harness.check(
method.getExtendedAttribute("SecureContext") is not None,
secureContext,
"Method has the correct SecureContext value",
)
sigpairs = zip(method.signatures(), signatures)
for gotSignature, expectedSignature in sigpairs:
(gotRetType, gotArgs) = gotSignature
(expectedRetType, expectedArgs) = expectedSignature
harness.check(
str(gotRetType), expectedRetType, "Method has the expected return type."
)
for i in range(0, len(gotArgs)):
(QName, name, type, optional, variadic) = expectedArgs[i]
checkArgument(gotArgs[i], QName, name, type, optional, variadic)
def checkResults(results):
harness.check(len(results), 3, "Should be three productions")
harness.ok(
isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface"
)
harness.ok(
isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface"
)
harness.ok(
isinstance(results[2], WebIDL.IDLInterface), "Should be an IDLInterface"
)
checkMethod(
results[0].ctor(),
"::TestConstructorNoArgs::constructor",
"constructor",
[("TestConstructorNoArgs (Wrapper)", [])],
)
harness.check(
len(results[0].members), 0, "TestConstructorNoArgs should not have members"
)
checkMethod(
results[1].ctor(),
"::TestConstructorWithArgs::constructor",
"constructor",
[
(
"TestConstructorWithArgs (Wrapper)",
[
(
"::TestConstructorWithArgs::constructor::name",
"name",
"String",
False,
False,
)
],
)
],
)
harness.check(
len(results[1].members),
0,
"TestConstructorWithArgs should not have members",
)
checkMethod(
results[2].ctor(),
"::TestConstructorOverloads::constructor",
"constructor",
[
(
"TestConstructorOverloads (Wrapper)",
[
(
"::TestConstructorOverloads::constructor::foo",
"foo",
"Object",
False,
False,
)
],
),
(
"TestConstructorOverloads (Wrapper)",
[
(
"::TestConstructorOverloads::constructor::bar",
"bar",
"Boolean",
False,
False,
)
],
),
],
)
harness.check(
len(results[2].members),
0,
"TestConstructorOverloads should not have members",
)
parser.parse(
"""
interface TestConstructorNoArgs {
constructor();
};
interface TestConstructorWithArgs {
constructor(DOMString name);
};
interface TestConstructorOverloads {
constructor(object foo);
constructor(boolean bar);
};
"""
)
results = parser.finish()
checkResults(results)
parser = parser.reset()
parser.parse(
"""
interface TestPrefConstructor {
[Pref="dom.webidl.test1"] constructor();
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
checkMethod(
results[0].ctor(),
"::TestPrefConstructor::constructor",
"constructor",
[("TestPrefConstructor (Wrapper)", [])],
pref=["dom.webidl.test1"],
)
parser = parser.reset()
parser.parse(
"""
interface TestChromeOnlyConstructor {
[ChromeOnly] constructor();
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
checkMethod(
results[0].ctor(),
"::TestChromeOnlyConstructor::constructor",
"constructor",
[("TestChromeOnlyConstructor (Wrapper)", [])],
chromeOnly=True,
)
parser = parser.reset()
parser.parse(
"""
interface TestSCConstructor {
[SecureContext] constructor();
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
checkMethod(
results[0].ctor(),
"::TestSCConstructor::constructor",
"constructor",
[("TestSCConstructor (Wrapper)", [])],
secureContext=True,
)
parser = parser.reset()
parser.parse(
"""
interface TestFuncConstructor {
[Func="IsNotUAWidget"] constructor();
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
checkMethod(
results[0].ctor(),
"::TestFuncConstructor::constructor",
"constructor",
[("TestFuncConstructor (Wrapper)", [])],
func=["IsNotUAWidget"],
)
parser = parser.reset()
parser.parse(
(
"\n"
" interface TestPrefChromeOnlySCFuncConstructor {\n"
' [ChromeOnly, Pref="dom.webidl.test1", SecureContext, '
'Func="IsNotUAWidget"]\n'
" constructor();\n"
" };\n"
)
)
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
checkMethod(
results[0].ctor(),
"::TestPrefChromeOnlySCFuncConstructor::constructor",
"constructor",
[("TestPrefChromeOnlySCFuncConstructor (Wrapper)", [])],
func=["IsNotUAWidget"],
pref=["dom.webidl.test1"],
chromeOnly=True,
secureContext=True,
)
parser = parser.reset()
parser.parse(
"""
interface TestHTMLConstructor {
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
checkMethod(
results[0].ctor(),
"::TestHTMLConstructor::constructor",
"constructor",
[("TestHTMLConstructor (Wrapper)", [])],
htmlConstructor=True,
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestChromeOnlyConstructor {
constructor()
[ChromeOnly] constructor(DOMString a);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Can't have both a constructor and a ChromeOnly constructor")
# Test HTMLConstructor with argument
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorWithArgs {
[HTMLConstructor] constructor(DOMString a);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "HTMLConstructor should take no argument")
# Test HTMLConstructor on a callback interface
parser = parser.reset()
threw = False
try:
parser.parse(
"""
callback interface TestHTMLConstructorOnCallbackInterface {
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
# Test HTMLConstructor and constructor operation
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
constructor();
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Can't have both a constructor and a HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
[Throws]
constructor();
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Can't have both a throwing constructor and a HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
constructor(DOMString a);
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a constructor operation")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
[Throws]
constructor(DOMString a);
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Can't have both a HTMLConstructor and a throwing constructor " "operation",
)
# Test HTMLConstructor and [ChromeOnly] constructor operation
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
[ChromeOnly]
constructor();
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Can't have both a ChromeOnly constructor and a HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
[Throws, ChromeOnly]
constructor();
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Can't have both a throwing chromeonly constructor and a " "HTMLConstructor",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
[ChromeOnly]
constructor(DOMString a);
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Can't have both a HTMLConstructor and a chromeonly constructor " "operation",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestHTMLConstructorAndConstructor {
[Throws, ChromeOnly]
constructor(DOMString a);
[HTMLConstructor] constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Can't have both a HTMLConstructor and a throwing chromeonly "
"constructor operation",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[LegacyNoInterfaceObject]
interface InterfaceWithoutInterfaceObject {
constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Can't have a constructor operation on a [LegacyNoInterfaceObject] "
"interface",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface InterfaceWithPartial {
};
partial interface InterfaceWithPartial {
constructor();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Can't have a constructor operation on a partial interface")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface InterfaceWithMixin {
};
interface mixin Mixin {
constructor();
};
InterfaceWithMixin includes Mixin
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Can't have a constructor operation on a mixin")

View File

@@ -0,0 +1,72 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
[Global=TestConstructorGlobal, Exposed=TestConstructorGlobal]
interface TestConstructorGlobal {
constructor();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=TestLegacyFactoryFunctionGlobal, Exposed=TestLegacyFactoryFunctionGlobal,
LegacyFactoryFunction=FooBar]
interface TestLegacyFactoryFunctionGlobal {
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[LegacyFactoryFunction=FooBar, Global=TestLegacyFactoryFunctionGlobal,
Exposed=TestLegacyFactoryFunctionGlobal]
interface TestLegacyFactoryFunctionGlobal {
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=TestHTMLConstructorGlobal, Exposed=TestHTMLConstructorGlobal]
interface TestHTMLConstructorGlobal {
[HTMLConstructor] constructor();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,50 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
[LegacyNoInterfaceObject]
interface TestConstructorLegacyNoInterfaceObject {
constructor();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
parser.parse(
"""
[LegacyNoInterfaceObject, LegacyFactoryFunction=FooBar]
interface TestLegacyFactoryFunctionLegacyNoInterfaceObject {
};
"""
)
# Test HTMLConstructor and LegacyNoInterfaceObject
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[LegacyNoInterfaceObject]
interface TestHTMLConstructorLegacyNoInterfaceObject {
[HTMLConstructor] constructor();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,17 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
interface Foo;
interface Bar;
interface Foo;
"""
)
results = parser.finish()
# There should be no duplicate interfaces in the result.
expectedNames = sorted(["Foo", "Bar"])
actualNames = sorted(map(lambda iface: iface.identifier.name, results))
harness.check(
actualNames, expectedNames, "Parser shouldn't output duplicate names."
)

View File

@@ -0,0 +1,878 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
dictionary Dict2 : Dict1 {
long child = 5;
Dict1 aaandAnother;
};
dictionary Dict1 {
long parent;
double otherParent;
};
"""
)
results = parser.finish()
dict1 = results[1]
dict2 = results[0]
harness.check(len(dict1.members), 2, "Dict1 has two members")
harness.check(len(dict2.members), 2, "Dict2 has four members")
harness.check(
dict1.members[0].identifier.name, "otherParent", "'o' comes before 'p'"
)
harness.check(
dict1.members[1].identifier.name, "parent", "'o' really comes before 'p'"
)
harness.check(
dict2.members[0].identifier.name, "aaandAnother", "'a' comes before 'c'"
)
harness.check(
dict2.members[1].identifier.name, "child", "'a' really comes before 'c'"
)
# Test partial dictionary.
parser = parser.reset()
parser.parse(
"""
dictionary A {
long c;
long g;
};
partial dictionary A {
long h;
long d;
};
"""
)
results = parser.finish()
dict1 = results[0]
harness.check(len(dict1.members), 4, "Dict1 has four members")
harness.check(dict1.members[0].identifier.name, "c", "c should be first")
harness.check(dict1.members[1].identifier.name, "d", "d should come after c")
harness.check(dict1.members[2].identifier.name, "g", "g should come after d")
harness.check(dict1.members[3].identifier.name, "h", "h should be last")
# Now reset our parser
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Dict {
long prop = 5;
long prop;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow name duplication in a dictionary")
# Test no name duplication across normal and partial dictionary.
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
long prop = 5;
};
partial dictionary A {
long prop;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow name duplication across normal and partial dictionary"
)
# Now reset our parser again
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Dict1 : Dict2 {
long prop = 5;
};
dictionary Dict2 : Dict3 {
long prop2;
};
dictionary Dict3 {
double prop;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow name duplication in a dictionary and " "its ancestor"
)
# More reset
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Iface {};
dictionary Dict : Iface {
long prop;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow non-dictionary parents for dictionaries")
# Even more reset
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A : B {};
dictionary B : A {};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow cycles in dictionary inheritance chains")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
[LegacyNullToEmptyString] DOMString foo;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow [LegacyNullToEmptyString] on dictionary members"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(A arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Trailing dictionary arg must be optional")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional A arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Trailing dictionary arg must have a default value")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo((A or DOMString) arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Trailing union arg containing a dictionary must be optional")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional (A or DOMString) arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Trailing union arg containing a dictionary must have a default value"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(A arg1, optional long arg2);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Dictionary arg followed by optional arg must be optional")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional A arg1, optional long arg2);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Dictionary arg followed by optional arg must have default value")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(A arg1, optional long arg2, long arg3);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
not threw,
"Dictionary arg followed by non-optional arg doesn't have to be optional",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo((A or DOMString) arg1, optional long arg2);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Union arg containing dictionary followed by optional arg must " "be optional",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional (A or DOMString) arg1, optional long arg2);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Union arg containing dictionary followed by optional arg must "
"have a default value",
)
parser = parser.reset()
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(A arg1, long arg2);
};
"""
)
parser.finish()
harness.ok(True, "Dictionary arg followed by required arg can be required")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional A? arg1 = {});
};
"""
)
parser.finish()
except Exception as x:
threw = x
harness.ok(threw, "Optional dictionary arg must not be nullable")
harness.ok(
"nullable" in str(threw),
"Must have the expected exception for optional nullable dictionary arg",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
required long x;
};
interface X {
undefined doFoo(A? arg1);
};
"""
)
parser.finish()
except Exception as x:
threw = x
harness.ok(threw, "Required dictionary arg must not be nullable")
harness.ok(
"nullable" in str(threw),
"Must have the expected exception for required nullable " "dictionary arg",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional (A or long)? arg1 = {});
};
"""
)
parser.finish()
except Exception as x:
threw = x
harness.ok(threw, "Dictionary arg must not be in an optional nullable union")
harness.ok(
"nullable" in str(threw),
"Must have the expected exception for optional nullable union "
"arg containing dictionary",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
required long x;
};
interface X {
undefined doFoo((A or long)? arg1);
};
"""
)
parser.finish()
except Exception as x:
threw = x
harness.ok(threw, "Dictionary arg must not be in a required nullable union")
harness.ok(
"nullable" in str(threw),
"Must have the expected exception for required nullable union "
"arg containing dictionary",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(sequence<A?> arg1);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Nullable union should be allowed in a sequence argument")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional (A or long?) arg1);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Dictionary must not be in a union with a nullable type")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional (long? or A) arg1);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "A nullable type must not be in a union with a dictionary")
parser = parser.reset()
parser.parse(
"""
dictionary A {
};
interface X {
A? doFoo();
};
"""
)
parser.finish()
harness.ok(True, "Dictionary return value can be nullable")
parser = parser.reset()
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional A arg = {});
};
"""
)
parser.finish()
harness.ok(True, "Dictionary arg should actually parse")
parser = parser.reset()
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional (A or DOMString) arg = {});
};
"""
)
parser.finish()
harness.ok(True, "Union arg containing a dictionary should actually parse")
parser = parser.reset()
parser.parse(
"""
dictionary A {
};
interface X {
undefined doFoo(optional (A or DOMString) arg = "abc");
};
"""
)
parser.finish()
harness.ok(
True,
"Union arg containing a dictionary with string default should actually parse",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
Foo foo;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Member type must not be its Dictionary.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo3 : Foo {
short d;
};
dictionary Foo2 : Foo3 {
boolean c;
};
dictionary Foo1 : Foo2 {
long a;
};
dictionary Foo {
Foo1 b;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Member type must not be a Dictionary that " "inherits from its Dictionary.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
(Foo or DOMString)[]? b;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Member type must not be a Nullable type "
"whose inner type includes its Dictionary.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
(DOMString or Foo) b;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Member type must not be a Union type, one of "
"whose member types includes its Dictionary.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
sequence<sequence<sequence<Foo>>> c;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Member type must not be a Sequence type "
"whose element type includes its Dictionary.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
(DOMString or Foo)[] d;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Member type must not be an Array type "
"whose element type includes its Dictionary.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
Foo1 b;
};
dictionary Foo3 {
Foo d;
};
dictionary Foo2 : Foo3 {
short c;
};
dictionary Foo1 : Foo2 {
long a;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Member type must not be a Dictionary, one of whose "
"members or inherited members has a type that includes "
"its Dictionary.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
};
dictionary Bar {
Foo? d;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Member type must not be a nullable dictionary")
parser = parser.reset()
parser.parse(
"""
dictionary Foo {
unrestricted float urFloat = 0;
unrestricted float urFloat2 = 1.1;
unrestricted float urFloat3 = -1.1;
unrestricted float? urFloat4 = null;
unrestricted float infUrFloat = Infinity;
unrestricted float negativeInfUrFloat = -Infinity;
unrestricted float nanUrFloat = NaN;
unrestricted double urDouble = 0;
unrestricted double urDouble2 = 1.1;
unrestricted double urDouble3 = -1.1;
unrestricted double? urDouble4 = null;
unrestricted double infUrDouble = Infinity;
unrestricted double negativeInfUrDouble = -Infinity;
unrestricted double nanUrDouble = NaN;
};
"""
)
parser.finish()
harness.ok(True, "Parsing default values for unrestricted types succeeded.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
double f = Infinity;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
double f = -Infinity;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
double f = NaN;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Only unrestricted values can be initialized to NaN")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
float f = Infinity;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
float f = -Infinity;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
float f = NaN;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Only unrestricted values can be initialized to NaN")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Foo {
long module;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should be able to use 'module' as a dictionary member name")

View File

@@ -0,0 +1,465 @@
import WebIDL
def firstArgType(method):
return method.signatures()[0][1][0].type
def WebIDLTest(parser, harness):
parser.parse(
"""
// Give our dictionary a required member so we don't need to
// mess with optional and default values.
dictionary Dict {
required long member;
};
callback interface Foo {
};
interface Bar {
// Bit of a pain to get things that have dictionary types
undefined passDict(Dict arg);
undefined passFoo(Foo arg);
undefined passNullableUnion((object? or DOMString) arg);
undefined passNullable(Foo? arg);
};
"""
)
results = parser.finish()
iface = results[2]
harness.ok(iface.isInterface(), "Should have interface")
dictMethod = iface.members[0]
ifaceMethod = iface.members[1]
nullableUnionMethod = iface.members[2]
nullableIfaceMethod = iface.members[3]
dictType = firstArgType(dictMethod)
ifaceType = firstArgType(ifaceMethod)
harness.ok(dictType.isDictionary(), "Should have dictionary type")
harness.ok(ifaceType.isInterface(), "Should have interface type")
harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type")
harness.ok(
not dictType.isDistinguishableFrom(ifaceType),
"Dictionary not distinguishable from callback interface",
)
harness.ok(
not ifaceType.isDistinguishableFrom(dictType),
"Callback interface not distinguishable from dictionary",
)
nullableUnionType = firstArgType(nullableUnionMethod)
nullableIfaceType = firstArgType(nullableIfaceMethod)
harness.ok(nullableUnionType.isUnion(), "Should have union type")
harness.ok(nullableIfaceType.isInterface(), "Should have interface type")
harness.ok(nullableIfaceType.nullable(), "Should have nullable type")
harness.ok(
not nullableUnionType.isDistinguishableFrom(nullableIfaceType),
"Nullable type not distinguishable from union with nullable " "member type",
)
harness.ok(
not nullableIfaceType.isDistinguishableFrom(nullableUnionType),
"Union with nullable member type not distinguishable from " "nullable type",
)
parser = parser.reset()
parser.parse(
"""
interface TestIface {
undefined passKid(Kid arg);
undefined passParent(Parent arg);
undefined passGrandparent(Grandparent arg);
undefined passUnrelated1(Unrelated1 arg);
undefined passUnrelated2(Unrelated2 arg);
undefined passArrayBuffer(ArrayBuffer arg);
undefined passArrayBuffer(ArrayBufferView arg);
};
interface Kid : Parent {};
interface Parent : Grandparent {};
interface Grandparent {};
interface Unrelated1 {};
interface Unrelated2 {};
"""
)
results = parser.finish()
iface = results[0]
harness.ok(iface.isInterface(), "Should have interface")
argTypes = [firstArgType(method) for method in iface.members]
unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]]
for type1 in argTypes:
for type2 in argTypes:
distinguishable = type1 is not type2 and (
type1 in unrelatedTypes or type2 in unrelatedTypes
)
harness.check(
type1.isDistinguishableFrom(type2),
distinguishable,
"Type %s should %sbe distinguishable from type %s"
% (type1, "" if distinguishable else "not ", type2),
)
harness.check(
type2.isDistinguishableFrom(type1),
distinguishable,
"Type %s should %sbe distinguishable from type %s"
% (type2, "" if distinguishable else "not ", type1),
)
parser = parser.reset()
parser.parse(
"""
interface Dummy {};
interface TestIface {
undefined method(long arg1, TestIface arg2);
undefined method(long arg1, long arg2);
undefined method(long arg1, Dummy arg2);
undefined method(DOMString arg1, DOMString arg2, DOMString arg3);
};
"""
)
results = parser.finish()
harness.check(len(results[1].members), 1, "Should look like we have one method")
harness.check(
len(results[1].members[0].signatures()), 4, "Should have four signatures"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Dummy {};
interface TestIface {
undefined method(long arg1, TestIface arg2);
undefined method(long arg1, long arg2);
undefined method(any arg1, Dummy arg2);
undefined method(DOMString arg1, DOMString arg2, DOMString arg3);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should throw when args before the distinguishing arg are not "
"all the same type",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Dummy {};
interface TestIface {
undefined method(long arg1, TestIface arg2);
undefined method(long arg1, long arg2);
undefined method(any arg1, DOMString arg2);
undefined method(DOMString arg1, DOMString arg2, DOMString arg3);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should throw when there is no distinguishing index")
# Now let's test our whole distinguishability table
argTypes = [
"long",
"short",
"long?",
"short?",
"boolean",
"boolean?",
"undefined",
"undefined?",
"DOMString",
"ByteString",
"UTF8String",
"Enum",
"Enum2",
"Interface",
"Interface?",
"AncestorInterface",
"UnrelatedInterface",
"CallbackInterface",
"CallbackInterface?",
"CallbackInterface2",
"object",
"Callback",
"Callback2",
"Dict",
"Dict2",
"sequence<long>",
"sequence<short>",
"record<DOMString, object>",
"record<USVString, Dict>",
"record<ByteString, long>",
"record<UTF8String, long>",
"any",
"Promise<any>",
"Promise<any>?",
"USVString",
"JSString",
"ArrayBuffer",
"ArrayBufferView",
"Uint8Array",
"Uint16Array",
"(long or Callback)",
"(long or Dict)",
]
# Try to categorize things a bit to keep list lengths down
def allBut(list1, list2):
return [
a
for a in list1
if a not in list2
and (a != "any" and a != "Promise<any>" and a != "Promise<any>?")
]
unionsWithCallback = ["(long or Callback)"]
unionsNoCallback = ["(long or Dict)"]
unions = unionsWithCallback + unionsNoCallback
numerics = ["long", "short", "long?", "short?"]
booleans = ["boolean", "boolean?"]
undefineds = ["undefined", "undefined?"]
primitives = numerics + booleans
nonNumerics = allBut(argTypes, numerics + unions)
nonBooleans = allBut(argTypes, booleans)
strings = [
"DOMString",
"ByteString",
"Enum",
"Enum2",
"USVString",
"JSString",
"UTF8String",
]
nonStrings = allBut(argTypes, strings)
nonObjects = undefineds + primitives + strings
bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"]
interfaces = [
"Interface",
"Interface?",
"AncestorInterface",
"UnrelatedInterface",
] + bufferSourceTypes
nullables = [
"long?",
"short?",
"boolean?",
"undefined?",
"Interface?",
"CallbackInterface?",
"Dict",
"Dict2",
"Date?",
"any",
"Promise<any>?",
] + unionsNoCallback
sequences = ["sequence<long>", "sequence<short>"]
nonUserObjects = nonObjects + interfaces + sequences
otherObjects = allBut(argTypes, nonUserObjects + ["object"])
notRelatedInterfaces = (
nonObjects
+ ["UnrelatedInterface"]
+ otherObjects
+ sequences
+ bufferSourceTypes
)
records = [
"record<DOMString, object>",
"record<USVString, Dict>",
"record<ByteString, long>",
"record<UTF8String, long>",
] # JSString not supported in records
dicts = ["Dict", "Dict2"]
callbacks = ["Callback", "Callback2"]
callbackInterfaces = [
"CallbackInterface",
"CallbackInterface?",
"CallbackInterface2",
]
dictionaryLike = dicts + callbackInterfaces + records + unionsNoCallback
# Build a representation of the distinguishability table as a dict
# of dicts, holding True values where needed, holes elsewhere.
data = dict()
for type in argTypes:
data[type] = dict()
def setDistinguishable(type, types):
for other in types:
data[type][other] = True
setDistinguishable("long", nonNumerics)
setDistinguishable("short", nonNumerics)
setDistinguishable("long?", allBut(nonNumerics, nullables))
setDistinguishable("short?", allBut(nonNumerics, nullables))
setDistinguishable("boolean", nonBooleans)
setDistinguishable("boolean?", allBut(nonBooleans, nullables))
setDistinguishable("undefined", allBut(argTypes, undefineds + dictionaryLike))
setDistinguishable(
"undefined?", allBut(argTypes, undefineds + dictionaryLike + nullables)
)
setDistinguishable("DOMString", nonStrings)
setDistinguishable("ByteString", nonStrings)
setDistinguishable("UTF8String", nonStrings)
setDistinguishable("USVString", nonStrings)
setDistinguishable("JSString", nonStrings)
setDistinguishable("Enum", nonStrings)
setDistinguishable("Enum2", nonStrings)
setDistinguishable("Interface", notRelatedInterfaces)
setDistinguishable("Interface?", allBut(notRelatedInterfaces, nullables))
setDistinguishable("AncestorInterface", notRelatedInterfaces)
setDistinguishable(
"UnrelatedInterface", allBut(argTypes, ["object", "UnrelatedInterface"])
)
setDistinguishable(
"CallbackInterface",
allBut(nonUserObjects + callbacks + unionsWithCallback, undefineds),
)
setDistinguishable(
"CallbackInterface?",
allBut(nonUserObjects + callbacks + unionsWithCallback, nullables + undefineds),
)
setDistinguishable(
"CallbackInterface2",
allBut(nonUserObjects + callbacks + unionsWithCallback, undefineds),
)
setDistinguishable("object", nonObjects)
setDistinguishable(
"Callback",
nonUserObjects + unionsNoCallback + dicts + records + callbackInterfaces,
)
setDistinguishable(
"Callback2",
nonUserObjects + unionsNoCallback + dicts + records + callbackInterfaces,
)
setDistinguishable(
"Dict",
allBut(nonUserObjects + unionsWithCallback + callbacks, nullables + undefineds),
)
setDistinguishable(
"Dict2",
allBut(nonUserObjects + unionsWithCallback + callbacks, nullables + undefineds),
)
setDistinguishable(
"sequence<long>",
allBut(argTypes, sequences + ["object"]),
)
setDistinguishable(
"sequence<short>",
allBut(argTypes, sequences + ["object"]),
)
setDistinguishable(
"record<DOMString, object>",
allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds),
)
setDistinguishable(
"record<USVString, Dict>",
allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds),
)
# JSString not supported in records
setDistinguishable(
"record<ByteString, long>",
allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds),
)
setDistinguishable(
"record<UTF8String, long>",
allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds),
)
setDistinguishable("any", [])
setDistinguishable("Promise<any>", [])
setDistinguishable("Promise<any>?", [])
setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"]))
setDistinguishable(
"ArrayBufferView",
allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]),
)
setDistinguishable(
"Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"])
)
setDistinguishable(
"Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"])
)
setDistinguishable(
"(long or Callback)",
allBut(nonUserObjects + dicts + records + callbackInterfaces, numerics),
)
setDistinguishable(
"(long or Dict)",
allBut(nonUserObjects + callbacks, numerics + nullables + undefineds),
)
def areDistinguishable(type1, type2):
return data[type1].get(type2, False)
def checkDistinguishability(parser, type1, type2):
idlTemplate = """
enum Enum { "a", "b" };
enum Enum2 { "c", "d" };
interface Interface : AncestorInterface {};
interface AncestorInterface {};
interface UnrelatedInterface {};
callback interface CallbackInterface {};
callback interface CallbackInterface2 {};
callback Callback = any();
callback Callback2 = long(short arg);
[LegacyTreatNonObjectAsNull] callback LegacyCallback1 = any();
// Give our dictionaries required members so we don't need to
// mess with optional and default values.
dictionary Dict { required long member; };
dictionary Dict2 { required long member; };
interface TestInterface {%s
};
"""
if type1 in undefineds or type2 in undefineds:
methods = """
(%s or %s) myMethod();""" % (
type1,
type2,
)
else:
methodTemplate = """
undefined myMethod(%s arg);"""
methods = (methodTemplate % type1) + (methodTemplate % type2)
idl = idlTemplate % methods
parser = parser.reset()
threw = False
try:
parser.parse(idl)
parser.finish()
except WebIDL.WebIDLError:
threw = True
if areDistinguishable(type1, type2):
harness.ok(
not threw,
"Should not throw for '%s' and '%s' because they are distinguishable"
% (type1, type2),
)
else:
harness.ok(
threw,
"Should throw for '%s' and '%s' because they are not distinguishable"
% (type1, type2),
)
# Enumerate over everything in both orders, since order matters in
# terms of our implementation of distinguishability checks
for type1 in argTypes:
for type2 in argTypes:
checkDistinguishability(parser, type1, type2)

View File

@@ -0,0 +1,19 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface DoubleNull {
attribute byte?? foo;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,67 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface DuplicateQualifiers1 {
getter getter byte foo(unsigned long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface DuplicateQualifiers2 {
setter setter byte foo(unsigned long index, byte value);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface DuplicateQualifiers4 {
deleter deleter byte foo(unsigned long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface DuplicateQualifiers5 {
getter deleter getter byte foo(unsigned long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,17 @@
import WebIDL
def WebIDLTest(parser, harness):
try:
parser.parse(
"""
enum TestEmptyEnum {
};
"""
)
harness.ok(False, "Should have thrown!")
except WebIDL.WebIDLError:
harness.ok(True, "Parsing TestEmptyEnum enum should fail")
parser.finish()

View File

@@ -0,0 +1,54 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface X {
const sequence<long> foo = [];
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Constant cannot have [] as a default value")
parser = parser.reset()
parser.parse(
"""
interface X {
undefined foo(optional sequence<long> arg = []);
};
"""
)
results = parser.finish()
harness.ok(
isinstance(
results[0].members[0].signatures()[0][1][0].defaultValue,
WebIDL.IDLEmptySequenceValue,
),
"Should have IDLEmptySequenceValue as default value of argument",
)
parser = parser.reset()
parser.parse(
"""
dictionary X {
sequence<long> foo = [];
};
"""
)
results = parser.finish()
harness.ok(
isinstance(results[0].members[0].defaultValue, WebIDL.IDLEmptySequenceValue),
"Should have IDLEmptySequenceValue as default value of " "dictionary member",
)

107
third_party/WebIDL/tests/test_enum.py vendored Normal file
View File

@@ -0,0 +1,107 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
enum TestEnum {
"",
"foo",
"bar"
};
interface TestEnumInterface {
TestEnum doFoo(boolean arg);
readonly attribute TestEnum foo;
};
"""
)
results = parser.finish()
harness.ok(True, "TestEnumInterfaces interface parsed without error.")
harness.check(len(results), 2, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLEnum), "Should be an IDLEnum")
harness.ok(isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface")
enum = results[0]
harness.check(enum.identifier.QName(), "::TestEnum", "Enum has the right QName")
harness.check(enum.identifier.name, "TestEnum", "Enum has the right name")
harness.check(enum.values(), ["", "foo", "bar"], "Enum has the right values")
iface = results[1]
harness.check(
iface.identifier.QName(), "::TestEnumInterface", "Interface has the right QName"
)
harness.check(
iface.identifier.name, "TestEnumInterface", "Interface has the right name"
)
harness.check(iface.parent, None, "Interface has no parent")
members = iface.members
harness.check(len(members), 2, "Should be one production")
harness.ok(isinstance(members[0], WebIDL.IDLMethod), "Should be an IDLMethod")
method = members[0]
harness.check(
method.identifier.QName(),
"::TestEnumInterface::doFoo",
"Method has correct QName",
)
harness.check(method.identifier.name, "doFoo", "Method has correct name")
signatures = method.signatures()
harness.check(len(signatures), 1, "Expect one signature")
(returnType, arguments) = signatures[0]
harness.check(
str(returnType), "TestEnum (Wrapper)", "Method type is the correct name"
)
harness.check(len(arguments), 1, "Method has the right number of arguments")
arg = arguments[0]
harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
harness.check(str(arg.type), "Boolean", "Argument has the right type")
attr = members[1]
harness.check(
attr.identifier.QName(), "::TestEnumInterface::foo", "Attr has correct QName"
)
harness.check(attr.identifier.name, "foo", "Attr has correct name")
harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")
# Now reset our parser
parser = parser.reset()
threw = False
try:
parser.parse(
"""
enum Enum {
"a",
"b",
"c"
};
interface TestInterface {
undefined foo(optional Enum e = "d");
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow a bogus default value for an enum")
# Now reset our parser
parser = parser.reset()
parser.parse(
"""
enum Enum {
"a",
"b",
"c",
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should allow trailing comma in enum")

View File

@@ -0,0 +1,16 @@
import WebIDL
def WebIDLTest(parser, harness):
try:
parser.parse(
"""
enum TestEnumDuplicateValue {
"",
""
};
"""
)
harness.ok(False, "Should have thrown!")
except WebIDL.WebIDLError:
harness.ok(True, "Enum TestEnumDuplicateValue should throw")

View File

@@ -0,0 +1,24 @@
import WebIDL
def WebIDLTest(parser, harness):
# Check that error messages put the '^' in the right place.
threw = False
input = "interface ?"
try:
parser.parse(input)
parser.finish()
except WebIDL.WebIDLError as e:
threw = True
lines = str(e).split("\n")
harness.check(len(lines), 3, "Expected number of lines in error message")
harness.check(lines[1], input, "Second line shows error")
harness.check(
lines[2],
" " * (len(input) - 1) + "^",
"Correct column pointer in error message",
)
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,38 @@
import WebIDL
def WebIDLTest(parser, harness):
# Check that error messages put the '^' in the right place.
threw = False
input = """\
// This is a comment.
interface Foo {
};
/* This is also a comment. */
interface ?"""
try:
parser.parse(input)
parser.finish()
except WebIDL.WebIDLError as e:
threw = True
lines = str(e).split("\n")
harness.check(len(lines), 3, "Expected number of lines in error message")
harness.ok(
lines[0].endswith("line 6:10"),
'First line of error should end with "line 6:10", but was "%s".' % lines[0],
)
harness.check(
lines[1],
"interface ?",
"Second line of error message is the line which caused the error.",
)
harness.check(
lines[2],
" " * (len("interface ?") - 1) + "^",
"Correct column pointer in error message.",
)
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,383 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=(Bar, Bar1,Bar2), Exposed=Bar] interface Bar {};
[Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
[Exposed=(Foo,Bar1)]
interface Iface {
undefined method1();
[Exposed=Bar1]
readonly attribute any attr;
};
[Exposed=Foo]
partial interface Iface {
undefined method2();
};
"""
)
results = parser.finish()
harness.check(len(results), 5, "Should know about five things")
iface = results[3]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
members = iface.members
harness.check(len(members), 3, "Should have three members")
harness.ok(
members[0].exposureSet == set(["Foo", "Bar"]),
"method1 should have the right exposure set",
)
harness.ok(
members[0]._exposureGlobalNames == set(["Foo", "Bar1"]),
"method1 should have the right exposure global names",
)
harness.ok(
members[1].exposureSet == set(["Bar"]),
"attr should have the right exposure set",
)
harness.ok(
members[1]._exposureGlobalNames == set(["Bar1"]),
"attr should have the right exposure global names",
)
harness.ok(
members[2].exposureSet == set(["Foo"]),
"method2 should have the right exposure set",
)
harness.ok(
members[2]._exposureGlobalNames == set(["Foo"]),
"method2 should have the right exposure global names",
)
harness.ok(
iface.exposureSet == set(["Foo", "Bar"]),
"Iface should have the right exposure set",
)
harness.ok(
iface._exposureGlobalNames == set(["Foo", "Bar1"]),
"Iface should have the right exposure global names",
)
parser = parser.reset()
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {};
[Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
[Exposed=Foo]
interface Iface2 {
undefined method3();
};
"""
)
results = parser.finish()
harness.check(len(results), 4, "Should know about four things")
iface = results[3]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
members = iface.members
harness.check(len(members), 1, "Should have one member")
harness.ok(
members[0].exposureSet == set(["Foo"]),
"method3 should have the right exposure set",
)
harness.ok(
members[0]._exposureGlobalNames == set(["Foo"]),
"method3 should have the right exposure global names",
)
harness.ok(
iface.exposureSet == set(["Foo"]), "Iface2 should have the right exposure set"
)
harness.ok(
iface._exposureGlobalNames == set(["Foo"]),
"Iface2 should have the right exposure global names",
)
parser = parser.reset()
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {};
[Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
[Exposed=Foo]
interface Iface3 {
undefined method4();
};
[Exposed=(Foo,Bar1)]
interface mixin Mixin {
undefined method5();
};
Iface3 includes Mixin;
"""
)
results = parser.finish()
harness.check(len(results), 6, "Should know about six things")
iface = results[3]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
members = iface.members
harness.check(len(members), 2, "Should have two members")
harness.ok(
members[0].exposureSet == set(["Foo"]),
"method4 should have the right exposure set",
)
harness.ok(
members[0]._exposureGlobalNames == set(["Foo"]),
"method4 should have the right exposure global names",
)
harness.ok(
members[1].exposureSet == set(["Foo", "Bar"]),
"method5 should have the right exposure set",
)
harness.ok(
members[1]._exposureGlobalNames == set(["Foo", "Bar1"]),
"method5 should have the right exposure global names",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Exposed=Foo]
interface Bar {
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on interface.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Bar {
[Exposed=Foo]
readonly attribute bool attr;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on attribute.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Bar {
[Exposed=Foo]
undefined operation();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on operation.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Bar {
[Exposed=Foo]
const long constant = 5;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on constant.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=Bar, Exposed=Bar] interface Bar {};
[Exposed=Foo]
interface Baz {
[Exposed=Bar]
undefined method();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should have thrown on member exposed where its interface is not."
)
parser = parser.reset()
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=Bar, Exposed=Bar] interface Bar {};
[Exposed=Foo]
interface Baz {
undefined method();
};
[Exposed=Bar]
interface mixin Mixin {
undefined otherMethod();
};
Baz includes Mixin;
"""
)
results = parser.finish()
harness.check(len(results), 5, "Should know about five things")
iface = results[2]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
members = iface.members
harness.check(len(members), 2, "Should have two members")
harness.ok(
members[0].exposureSet == set(["Foo"]),
"method should have the right exposure set",
)
harness.ok(
members[0]._exposureGlobalNames == set(["Foo"]),
"method should have the right exposure global names",
)
harness.ok(
members[1].exposureSet == set(["Bar"]),
"otherMethod should have the right exposure set",
)
harness.ok(
members[1]._exposureGlobalNames == set(["Bar"]),
"otherMethod should have the right exposure global names",
)
parser = parser.reset()
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=Bar, Exposed=Bar] interface Bar {};
[Exposed=*]
interface Baz {
undefined methodWild();
};
[Exposed=Bar]
interface mixin Mixin {
undefined methodNotWild();
};
Baz includes Mixin;
"""
)
results = parser.finish()
harness.check(len(results), 5, "Should know about five things")
iface = results[2]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
members = iface.members
harness.check(len(members), 2, "Should have two members")
harness.ok(
members[0].exposureSet == set(["Foo", "Bar"]),
"methodWild should have the right exposure set",
)
harness.ok(
members[0]._exposureGlobalNames == set(["Foo", "Bar"]),
"methodWild should have the right exposure global names",
)
harness.ok(
members[1].exposureSet == set(["Bar"]),
"methodNotWild should have the right exposure set",
)
harness.ok(
members[1]._exposureGlobalNames == set(["Bar"]),
"methodNotWild should have the right exposure global names",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=Bar, Exposed=Bar] interface Bar {};
[Exposed=Foo]
interface Baz {
[Exposed=*]
undefined method();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should have thrown on member exposed where its interface is not."
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, Exposed=Foo] interface Foo {};
[Global=Bar, Exposed=Bar] interface Bar {};
[Exposed=(Foo,*)]
interface Baz {
undefined method();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on a wildcard in an identifier list.")

View File

@@ -0,0 +1,131 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
[LegacyNoInterfaceObject]
interface TestExtendedAttr {
[LegacyUnforgeable] readonly attribute byte b;
};
"""
)
parser.finish()
parser = parser.reset()
parser.parse(
"""
[Pref="foo.bar",Pref=flop]
interface TestExtendedAttr {
[Pref="foo.bar"] attribute byte b;
};
"""
)
parser.finish()
parser = parser.reset()
parser.parse(
"""
interface TestLegacyLenientThis {
[LegacyLenientThis] attribute byte b;
};
"""
)
results = parser.finish()
harness.ok(
results[0].members[0].hasLegacyLenientThis(), "Should have a lenient this"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestLegacyLenientThis2 {
[LegacyLenientThis=something] attribute byte b;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[LegacyLenientThis] must take no arguments")
parser = parser.reset()
parser.parse(
"""
interface TestClamp {
undefined testClamp([Clamp] long foo);
undefined testNotClamp(long foo);
};
"""
)
results = parser.finish()
# Pull out the first argument out of the arglist of the first (and
# only) signature.
harness.ok(
results[0].members[0].signatures()[0][1][0].type.hasClamp(), "Should be clamped"
)
harness.ok(
not results[0].members[1].signatures()[0][1][0].type.hasClamp(),
"Should not be clamped",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestClamp2 {
undefined testClamp([Clamp=something] long foo);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[Clamp] must take no arguments")
parser = parser.reset()
parser.parse(
"""
interface TestEnforceRange {
undefined testEnforceRange([EnforceRange] long foo);
undefined testNotEnforceRange(long foo);
};
"""
)
results = parser.finish()
# Pull out the first argument out of the arglist of the first (and
# only) signature.
harness.ok(
results[0].members[0].signatures()[0][1][0].type.hasEnforceRange(),
"Should be enforceRange",
)
harness.ok(
not results[0].members[1].signatures()[0][1][0].type.hasEnforceRange(),
"Should not be enforceRange",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestEnforceRange2 {
undefined testEnforceRange([EnforceRange=something] long foo);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[EnforceRange] must take no arguments")

View File

@@ -0,0 +1,145 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
typedef float myFloat;
typedef unrestricted float myUnrestrictedFloat;
interface FloatTypes {
attribute float f;
attribute unrestricted float uf;
attribute double d;
attribute unrestricted double ud;
[LenientFloat]
attribute float lf;
[LenientFloat]
attribute double ld;
undefined m1(float arg1, double arg2, float? arg3, double? arg4,
myFloat arg5, unrestricted float arg6,
unrestricted double arg7, unrestricted float? arg8,
unrestricted double? arg9, myUnrestrictedFloat arg10);
[LenientFloat]
undefined m2(float arg1, double arg2, float? arg3, double? arg4,
myFloat arg5, unrestricted float arg6,
unrestricted double arg7, unrestricted float? arg8,
unrestricted double? arg9, myUnrestrictedFloat arg10);
[LenientFloat]
undefined m3(float arg);
[LenientFloat]
undefined m4(double arg);
[LenientFloat]
undefined m5((float or FloatTypes) arg);
[LenientFloat]
undefined m6(sequence<float> arg);
};
"""
)
results = parser.finish()
harness.check(len(results), 3, "Should be two typedefs and one interface.")
iface = results[2]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
types = [a.type for a in iface.members if a.isAttr()]
harness.ok(types[0].isFloat(), "'float' is a float")
harness.ok(not types[0].isUnrestricted(), "'float' is not unrestricted")
harness.ok(types[1].isFloat(), "'unrestricted float' is a float")
harness.ok(types[1].isUnrestricted(), "'unrestricted float' is unrestricted")
harness.ok(types[2].isFloat(), "'double' is a float")
harness.ok(not types[2].isUnrestricted(), "'double' is not unrestricted")
harness.ok(types[3].isFloat(), "'unrestricted double' is a float")
harness.ok(types[3].isUnrestricted(), "'unrestricted double' is unrestricted")
method = iface.members[6]
harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
argtypes = [a.type for a in method.signatures()[0][1]]
for idx, type in enumerate(argtypes):
harness.ok(type.isFloat(), "Type %d should be float" % idx)
harness.check(
type.isUnrestricted(),
idx >= 5,
"Type %d should %sbe unrestricted" % (idx, "" if idx >= 4 else "not "),
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface FloatTypes {
[LenientFloat]
long m(float arg);
};
"""
)
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[LenientFloat] only allowed on methods returning undefined")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface FloatTypes {
[LenientFloat]
undefined m(unrestricted float arg);
};
"""
)
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "[LenientFloat] only allowed on methods with unrestricted float args"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface FloatTypes {
[LenientFloat]
undefined m(sequence<unrestricted float> arg);
};
"""
)
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "[LenientFloat] only allowed on methods with unrestricted float args (2)"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface FloatTypes {
[LenientFloat]
undefined m((unrestricted float or FloatTypes) arg);
};
"""
)
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "[LenientFloat] only allowed on methods with unrestricted float args (3)"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface FloatTypes {
[LenientFloat]
readonly attribute float foo;
};
"""
)
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[LenientFloat] only allowed on writable attributes")

View File

@@ -0,0 +1,15 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
interface ForwardDeclared;
interface ForwardDeclared;
interface TestForwardDecl {
attribute ForwardDeclared foo;
};
"""
)
parser.finish()
harness.ok(True, "TestForwardDeclared interface parsed without error.")

View File

@@ -0,0 +1,151 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
[Global=Foo, Exposed=Foo]
interface Foo : Bar {
getter any(DOMString name);
};
[Exposed=Foo]
interface Bar {};
"""
)
results = parser.finish()
harness.ok(
results[0].isOnGlobalProtoChain(),
"[Global] interface should be on global's proto chain",
)
harness.ok(
results[1].isOnGlobalProtoChain(),
"[Global] interface should be on global's proto chain",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, Exposed=Foo]
interface Foo {
getter any(DOMString name);
setter undefined(DOMString name, any arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown for [Global] used on an interface with a " "named setter",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, Exposed=Foo]
interface Foo {
getter any(DOMString name);
deleter undefined(DOMString name);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown for [Global] used on an interface with a " "named deleter",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, LegacyOverrideBuiltIns, Exposed=Foo]
interface Foo {
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown for [Global] used on an interface with a "
"[LegacyOverrideBuiltIns]",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, Exposed=Foo]
interface Foo : Bar {
};
[LegacyOverrideBuiltIns, Exposed=Foo]
interface Bar {
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown for [Global] used on an interface with an "
"[LegacyOverrideBuiltIns] ancestor",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Foo, Exposed=Foo]
interface Foo {
};
[Exposed=Foo]
interface Bar : Foo {
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown for [Global] used on an interface with a " "descendant",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global, Exposed=Foo]
interface Foo {
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown for [Global] without a right hand side value",
)

View File

@@ -0,0 +1,45 @@
def WebIDLTest(parser, harness):
try:
parser.parse(
"""
enum Foo { "a" };
interface Foo;
"""
)
parser.finish()
harness.ok(False, "Should fail to parse")
except Exception as e:
harness.ok(
"Name collision" in str(e), "Should have name collision for interface"
)
parser = parser.reset()
try:
parser.parse(
"""
dictionary Foo { long x; };
enum Foo { "a" };
"""
)
parser.finish()
harness.ok(False, "Should fail to parse")
except Exception as e:
harness.ok(
"Name collision" in str(e), "Should have name collision for dictionary"
)
parser = parser.reset()
try:
parser.parse(
"""
enum Foo { "a" };
enum Foo { "b" };
"""
)
parser.finish()
harness.ok(False, "Should fail to parse")
except Exception as e:
harness.ok(
"Multiple unresolvable definitions" in str(e),
"Should have name collision for dictionary",
)

View File

@@ -0,0 +1,18 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestIncompleteParent : NotYetDefined {
undefined foo();
};
interface NotYetDefined : EvenHigherOnTheChain {
};
interface EvenHigherOnTheChain {
};
"""
)
parser.finish()
harness.ok(True, "TestIncompleteParent interface parsed without error.")

View File

@@ -0,0 +1,61 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestIncompleteTypes {
attribute FooInterface attr1;
FooInterface method1(FooInterface arg);
};
interface FooInterface {
};
"""
)
results = parser.finish()
harness.ok(True, "TestIncompleteTypes interface parsed without error.")
harness.check(len(results), 2, "Should be two productions.")
iface = results[0]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(),
"::TestIncompleteTypes",
"Interface has the right QName",
)
harness.check(
iface.identifier.name, "TestIncompleteTypes", "Interface has the right name"
)
harness.check(len(iface.members), 2, "Expect 2 members")
attr = iface.members[0]
harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
method = iface.members[1]
harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
harness.check(
attr.identifier.QName(),
"::TestIncompleteTypes::attr1",
"Attribute has the right QName",
)
harness.check(
attr.type.name, "FooInterface", "Previously unresolved type has the right name"
)
harness.check(
method.identifier.QName(),
"::TestIncompleteTypes::method1",
"Attribute has the right QName",
)
(returnType, args) = method.signatures()[0]
harness.check(
returnType.name, "FooInterface", "Previously unresolved type has the right name"
)
harness.check(
args[0].type.name,
"FooInterface",
"Previously unresolved type has the right name",
)

View File

@@ -0,0 +1,459 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse("interface Foo { };")
results = parser.finish()
harness.ok(True, "Empty interface parsed without error.")
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
iface = results[0]
harness.check(iface.identifier.QName(), "::Foo", "Interface has the right QName")
harness.check(iface.identifier.name, "Foo", "Interface has the right name")
harness.check(iface.parent, None, "Interface has no parent")
parser.parse("interface Bar : Foo { };")
results = parser.finish()
harness.ok(True, "Empty interface parsed without error.")
harness.check(len(results), 2, "Should be two productions")
harness.ok(isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface")
iface = results[1]
harness.check(iface.identifier.QName(), "::Bar", "Interface has the right QName")
harness.check(iface.identifier.name, "Bar", "Interface has the right name")
harness.ok(isinstance(iface.parent, WebIDL.IDLInterface), "Interface has a parent")
parser = parser.reset()
parser.parse(
"""
interface QNameBase {
attribute long foo;
};
interface QNameDerived : QNameBase {
attribute long long foo;
attribute byte bar;
};
"""
)
results = parser.finish()
harness.check(len(results), 2, "Should be two productions")
harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
harness.ok(isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(results[1].parent, results[0], "Inheritance chain is right")
harness.check(len(results[0].members), 1, "Expect 1 productions")
harness.check(len(results[1].members), 2, "Expect 2 productions")
base = results[0]
derived = results[1]
harness.check(
base.members[0].identifier.QName(),
"::QNameBase::foo",
"Member has the right QName",
)
harness.check(
derived.members[0].identifier.QName(),
"::QNameDerived::foo",
"Member has the right QName",
)
harness.check(
derived.members[1].identifier.QName(),
"::QNameDerived::bar",
"Member has the right QName",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A : B {};
interface B : A {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow cycles in interface inheritance chains")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A : C {};
interface C : B {};
interface B : A {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow indirect cycles in interface inheritance chains"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A;
interface B : A {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow inheriting from an interface that is only forward declared",
)
parser = parser.reset()
parser.parse(
"""
interface A {
constructor();
constructor(long arg);
readonly attribute boolean x;
undefined foo();
};
partial interface A {
readonly attribute boolean y;
undefined foo(long arg);
};
"""
)
results = parser.finish()
harness.check(len(results), 2, "Should have two results with partial interface")
iface = results[0]
harness.check(
len(iface.members), 3, "Should have three members with partial interface"
)
harness.check(
iface.members[0].identifier.name,
"x",
"First member should be x with partial interface",
)
harness.check(
iface.members[1].identifier.name,
"foo",
"Second member should be foo with partial interface",
)
harness.check(
len(iface.members[1].signatures()),
2,
"Should have two foo signatures with partial interface",
)
harness.check(
iface.members[2].identifier.name,
"y",
"Third member should be y with partial interface",
)
harness.check(
len(iface.ctor().signatures()),
2,
"Should have two constructors with partial interface",
)
parser = parser.reset()
parser.parse(
"""
partial interface A {
readonly attribute boolean y;
undefined foo(long arg);
};
interface A {
constructor();
constructor(long arg);
readonly attribute boolean x;
undefined foo();
};
"""
)
results = parser.finish()
harness.check(
len(results), 2, "Should have two results with reversed partial interface"
)
iface = results[1]
harness.check(
len(iface.members),
3,
"Should have three members with reversed partial interface",
)
harness.check(
iface.members[0].identifier.name,
"x",
"First member should be x with reversed partial interface",
)
harness.check(
iface.members[1].identifier.name,
"foo",
"Second member should be foo with reversed partial interface",
)
harness.check(
len(iface.members[1].signatures()),
2,
"Should have two foo signatures with reversed partial interface",
)
harness.check(
iface.members[2].identifier.name,
"y",
"Third member should be y with reversed partial interface",
)
harness.check(
len(iface.ctor().signatures()),
2,
"Should have two constructors with reversed partial interface",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
readonly attribute boolean x;
};
interface A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow two non-partial interfaces with the same name")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
partial interface A {
readonly attribute boolean x;
};
partial interface A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Must have a non-partial interface for a given name")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
boolean x;
};
partial interface A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow a name collision between partial interface "
"and other object",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
boolean x;
};
interface A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow a name collision between interface " "and other object"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
boolean x;
};
interface A;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow a name collision between external interface "
"and other object",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
readonly attribute boolean x;
};
interface A;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow a name collision between external interface " "and interface",
)
parser = parser.reset()
parser.parse(
"""
interface A;
interface A;
"""
)
results = parser.finish()
harness.ok(
len(results) == 1 and isinstance(results[0], WebIDL.IDLExternalInterface),
"Should allow name collisions between external interface " "declarations",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[SomeRandomAnnotation]
interface A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow unknown extended attributes on interfaces")
parser = parser.reset()
parser.parse(
"""
[Global=Window, Exposed=Window] interface Window {};
[Exposed=Window, LegacyWindowAlias=A]
interface B {};
[Exposed=Window, LegacyWindowAlias=(C, D)]
interface E {};
"""
)
results = parser.finish()
harness.check(
results[1].legacyWindowAliases, ["A"], "Should support a single identifier"
)
harness.check(
results[2].legacyWindowAliases, ["C", "D"], "Should support an identifier list"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[LegacyWindowAlias]
interface A {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [LegacyWindowAlias] with no value")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Exposed=Worker, LegacyWindowAlias=B]
interface A {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [LegacyWindowAlias] without Window exposure")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Window, Exposed=Window] interface Window {};
[Exposed=Window]
interface A {};
[Exposed=Window, LegacyWindowAlias=A]
interface B {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow [LegacyWindowAlias] to conflict with other identifiers"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Window, Exposed=Window] interface Window {};
[Exposed=Window, LegacyWindowAlias=A]
interface B {};
[Exposed=Window]
interface A {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow [LegacyWindowAlias] to conflict with other identifiers"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[Global=Window, Exposed=Window] interface Window {};
[Exposed=Window, LegacyWindowAlias=A]
interface B {};
[Exposed=Window, LegacyWindowAlias=A]
interface C {};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow [LegacyWindowAlias] to conflict with other identifiers"
)

View File

@@ -0,0 +1,20 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface IdentifierConflict {
const byte thing1 = 1;
const unsigned long thing1 = 1;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,171 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers1 {
const byte thing1 = 1;
readonly attribute long thing1;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers1.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers2 {
readonly attribute long thing1;
const byte thing1 = 1;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers2.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers3 {
getter boolean thing1(DOMString name);
readonly attribute long thing1;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers3.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers4 {
const byte thing1 = 1;
long thing1();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers4.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers5 {
static long thing1();
undefined thing1();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
not threw, "Should not have thrown for IdentifierConflictAcrossMembers5."
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin IdentifierConflictAcrossMembers6Mixin {
undefined thing1();
};
interface IdentifierConflictAcrossMembers6 {
static long thing1();
};
IdentifierConflictAcrossMembers6 includes IdentifierConflictAcrossMembers6Mixin;
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
not threw, "Should not have thrown for IdentifierConflictAcrossMembers6."
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers7 {
const byte thing1 = 1;
static readonly attribute long thing1;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers7.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers8 {
readonly attribute long thing1 = 1;
static readonly attribute long thing1;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers8.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IdentifierConflictAcrossMembers9 {
void thing1();
static readonly attribute long thing1;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers9.")

View File

@@ -0,0 +1,935 @@
import WebIDL
def WebIDLTest(parser, harness):
def shouldPass(prefix, iface, expectedMembers, numProductions=1):
p = parser.reset()
p.parse(iface)
results = p.finish()
harness.check(
len(results),
numProductions,
"%s - Should have production count %d" % (prefix, numProductions),
)
harness.ok(
isinstance(results[0], WebIDL.IDLInterface),
"%s - Should be an IDLInterface" % (prefix),
)
# Make a copy, since we plan to modify it
expectedMembers = list(expectedMembers)
for m in results[0].members:
name = m.identifier.name
if m.isMethod() and m.isStatic():
# None of the expected members are static methods, so ignore those.
harness.ok(True, "%s - %s - Should be a %s" % (prefix, name, type(m)))
elif (name, type(m)) in expectedMembers:
harness.ok(True, "%s - %s - Should be a %s" % (prefix, name, type(m)))
expectedMembers.remove((name, type(m)))
else:
harness.ok(
False,
"%s - %s - Unknown symbol of type %s" % (prefix, name, type(m)),
)
# A bit of a hoop because we can't generate the error string if we pass
if len(expectedMembers) == 0:
harness.ok(True, "Found all the members")
else:
harness.ok(
False,
"Expected member not found: %s of type %s"
% (expectedMembers[0][0], expectedMembers[0][1]),
)
return results
def shouldFail(prefix, iface):
try:
p = parser.reset()
p.parse(iface)
p.finish()
harness.ok(False, prefix + " - Interface passed when should've failed")
except WebIDL.WebIDLError:
harness.ok(True, prefix + " - Interface failed as expected")
except Exception as e:
harness.ok(
False,
prefix
+ " - Interface failed but not as a WebIDLError exception: %s" % e,
)
iterableMembers = [
(x, WebIDL.IDLMethod) for x in ["entries", "keys", "values", "forEach"]
]
setROMembers = (
[(x, WebIDL.IDLMethod) for x in ["has"]]
+ [("__setlike", WebIDL.IDLMaplikeOrSetlike)]
+ iterableMembers
)
setROMembers.extend([("size", WebIDL.IDLAttribute)])
setRWMembers = [
(x, WebIDL.IDLMethod) for x in ["add", "clear", "delete"]
] + setROMembers
mapROMembers = (
[(x, WebIDL.IDLMethod) for x in ["get", "has"]]
+ [("__maplike", WebIDL.IDLMaplikeOrSetlike)]
+ iterableMembers
)
mapROMembers.extend([("size", WebIDL.IDLAttribute)])
mapRWMembers = [
(x, WebIDL.IDLMethod) for x in ["set", "clear", "delete"]
] + mapROMembers
# OK, now that we've used iterableMembers to set up the above, append
# __iterable to it for the iterable<> case.
iterableMembers.append(("__iterable", WebIDL.IDLIterable))
asyncIterableMembers = [
(x, WebIDL.IDLMethod) for x in ["entries", "keys", "values"]
]
asyncIterableMembers.append(("__iterable", WebIDL.IDLAsyncIterable))
valueIterableMembers = [("__iterable", WebIDL.IDLIterable)]
valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod))
valueIterableMembers.append(("length", WebIDL.IDLAttribute))
valueAsyncIterableMembers = [("__iterable", WebIDL.IDLAsyncIterable)]
valueAsyncIterableMembers.append(("values", WebIDL.IDLMethod))
disallowedIterableNames = [
("keys", WebIDL.IDLMethod),
("entries", WebIDL.IDLMethod),
("values", WebIDL.IDLMethod),
]
disallowedMemberNames = [
("forEach", WebIDL.IDLMethod),
("has", WebIDL.IDLMethod),
("size", WebIDL.IDLAttribute),
] + disallowedIterableNames
mapDisallowedMemberNames = [("get", WebIDL.IDLMethod)] + disallowedMemberNames
disallowedNonMethodNames = [
("clear", WebIDL.IDLMethod),
("delete", WebIDL.IDLMethod),
]
mapDisallowedNonMethodNames = [("set", WebIDL.IDLMethod)] + disallowedNonMethodNames
setDisallowedNonMethodNames = [("add", WebIDL.IDLMethod)] + disallowedNonMethodNames
unrelatedMembers = [
("unrelatedAttribute", WebIDL.IDLAttribute),
("unrelatedMethod", WebIDL.IDLMethod),
]
#
# Simple Usage Tests
#
shouldPass(
"Iterable (key only)",
"""
interface Foo1 {
iterable<long>;
readonly attribute unsigned long length;
getter long(unsigned long index);
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
valueIterableMembers + unrelatedMembers,
)
shouldPass(
"Iterable (key only) inheriting from parent",
"""
interface Foo1 : Foo2 {
iterable<long>;
readonly attribute unsigned long length;
getter long(unsigned long index);
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
valueIterableMembers,
numProductions=2,
)
shouldPass(
"Iterable (key and value)",
"""
interface Foo1 {
iterable<long, long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
iterableMembers + unrelatedMembers,
# numProductions == 2 because of the generated iterator iface,
numProductions=2,
)
shouldPass(
"Iterable (key and value) inheriting from parent",
"""
interface Foo1 : Foo2 {
iterable<long, long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
iterableMembers,
# numProductions == 3 because of the generated iterator iface,
numProductions=3,
)
shouldPass(
"Async iterable (key only)",
"""
interface Foo1 {
async iterable<long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
valueAsyncIterableMembers + unrelatedMembers,
# numProductions == 2 because of the generated iterator iface,
numProductions=2,
)
shouldPass(
"Async iterable (key only) inheriting from parent",
"""
interface Foo1 : Foo2 {
async iterable<long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
valueAsyncIterableMembers,
# numProductions == 3 because of the generated iterator iface,
numProductions=3,
)
shouldPass(
"Async iterable with argument (key only)",
"""
interface Foo1 {
async iterable<long>(optional long foo);
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
valueAsyncIterableMembers + unrelatedMembers,
# numProductions == 2 because of the generated iterator iface,
numProductions=2,
)
shouldPass(
"Async iterable (key and value)",
"""
interface Foo1 {
async iterable<long, long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
asyncIterableMembers + unrelatedMembers,
# numProductions == 2 because of the generated iterator iface,
numProductions=2,
)
shouldPass(
"Async iterable (key and value) inheriting from parent",
"""
interface Foo1 : Foo2 {
async iterable<long, long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
asyncIterableMembers,
# numProductions == 3 because of the generated iterator iface,
numProductions=3,
)
shouldPass(
"Async iterable with argument (key and value)",
"""
interface Foo1 {
async iterable<long, long>(optional long foo);
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
asyncIterableMembers + unrelatedMembers,
# numProductions == 2 because of the generated iterator iface,
numProductions=2,
)
shouldPass(
"Maplike (readwrite)",
"""
interface Foo1 {
maplike<long, long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
mapRWMembers + unrelatedMembers,
)
shouldPass(
"Maplike (readwrite) inheriting from parent",
"""
interface Foo1 : Foo2 {
maplike<long, long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
mapRWMembers,
numProductions=2,
)
shouldPass(
"Maplike (readwrite)",
"""
interface Foo1 {
maplike<long, long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
mapRWMembers + unrelatedMembers,
)
shouldPass(
"Maplike (readwrite) inheriting from parent",
"""
interface Foo1 : Foo2 {
maplike<long, long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
mapRWMembers,
numProductions=2,
)
shouldPass(
"Maplike (readonly)",
"""
interface Foo1 {
readonly maplike<long, long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
mapROMembers + unrelatedMembers,
)
shouldPass(
"Maplike (readonly) inheriting from parent",
"""
interface Foo1 : Foo2 {
readonly maplike<long, long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
mapROMembers,
numProductions=2,
)
shouldPass(
"Setlike (readwrite)",
"""
interface Foo1 {
setlike<long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
setRWMembers + unrelatedMembers,
)
shouldPass(
"Setlike (readwrite) inheriting from parent",
"""
interface Foo1 : Foo2 {
setlike<long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
setRWMembers,
numProductions=2,
)
shouldPass(
"Setlike (readonly)",
"""
interface Foo1 {
readonly setlike<long>;
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
setROMembers + unrelatedMembers,
)
shouldPass(
"Setlike (readonly) inheriting from parent",
"""
interface Foo1 : Foo2 {
readonly setlike<long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""",
setROMembers,
numProductions=2,
)
shouldPass(
"Inheritance of maplike/setlike",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
};
""",
mapRWMembers,
numProductions=2,
)
shouldFail(
"JS Implemented maplike interface",
"""
[JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
interface Foo1 {
constructor();
setlike<long>;
};
""",
)
shouldFail(
"JS Implemented maplike interface",
"""
[JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
interface Foo1 {
constructor();
maplike<long, long>;
};
""",
)
#
# Multiple maplike/setlike tests
#
shouldFail(
"Two maplike/setlikes on same interface",
"""
interface Foo1 {
setlike<long>;
maplike<long, long>;
};
""",
)
shouldFail(
"Two iterable/setlikes on same interface",
"""
interface Foo1 {
iterable<long>;
maplike<long, long>;
};
""",
)
shouldFail(
"Two iterables on same interface",
"""
interface Foo1 {
iterable<long>;
iterable<long, long>;
};
""",
)
shouldFail(
"Two iterables on same interface",
"""
interface Foo1 {
iterable<long>;
async iterable<long>;
};
""",
)
shouldFail(
"Two iterables on same interface",
"""
interface Foo1 {
async iterable<long>;
async iterable<long, long>;
};
""",
)
shouldFail(
"Async iterable with non-optional arguments",
"""
interface Foo1 {
async iterable<long>(long foo);
};
""",
)
shouldFail(
"Async iterable with non-optional arguments",
"""
interface Foo1 {
async iterable<long>(optional long foo, long bar);
};
""",
)
shouldFail(
"Async iterable with non-optional arguments",
"""
interface Foo1 {
async iterable<long, long>(long foo);
};
""",
)
shouldFail(
"Two maplike/setlikes in partials",
"""
interface Foo1 {
maplike<long, long>;
};
partial interface Foo1 {
setlike<long>;
};
""",
)
shouldFail(
"Conflicting maplike/setlikes across inheritance",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
setlike<long>;
};
""",
)
shouldFail(
"Conflicting maplike/iterable across inheritance",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
iterable<long>;
};
""",
)
shouldFail(
"Conflicting maplike/setlikes across multistep inheritance",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
};
interface Foo3 : Foo2 {
setlike<long>;
};
""",
)
#
# Member name collision tests
#
def testConflictingMembers(
likeMember, conflict, expectedMembers, methodPasses, numProductions=1
):
"""
Tests for maplike/setlike member generation against conflicting member
names. If methodPasses is True, this means we expect the interface to
pass in the case of method shadowing, and expectedMembers should be the
list of interface members to check against on the passing interface.
"""
(conflictName, conflictType) = conflict
if methodPasses:
shouldPass(
"Conflicting method: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
%s;
[Throws]
undefined %s(long test1, double test2, double test3);
};
"""
% (likeMember, conflictName),
expectedMembers,
)
else:
shouldFail(
"Conflicting method: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
%s;
[Throws]
undefined %s(long test1, double test2, double test3);
};
"""
% (likeMember, conflictName),
)
# Inherited conflicting methods should ALWAYS fail
shouldFail(
"Conflicting inherited method: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
undefined %s(long test1, double test2, double test3);
};
interface Foo2 : Foo1 {
%s;
};
"""
% (conflictName, likeMember),
)
if conflictType == WebIDL.IDLAttribute:
shouldFail(
"Conflicting static method: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
%s;
static undefined %s(long test1, double test2, double test3);
};
"""
% (likeMember, conflictName),
)
else:
shouldPass(
"Conflicting static method: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
%s;
static undefined %s(long test1, double test2, double test3);
};
"""
% (likeMember, conflictName),
expectedMembers,
numProductions=numProductions,
)
shouldFail(
"Conflicting attribute: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
%s
attribute double %s;
};
"""
% (likeMember, conflictName),
)
shouldFail(
"Conflicting const: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
%s;
const double %s = 0;
};
"""
% (likeMember, conflictName),
)
shouldFail(
"Conflicting static attribute: %s and %s" % (likeMember, conflictName),
"""
interface Foo1 {
%s;
static attribute long %s;
};
"""
% (likeMember, conflictName),
)
for member in disallowedIterableNames:
testConflictingMembers(
"iterable<long, long>", member, iterableMembers, False, numProductions=2
)
for member in mapDisallowedMemberNames:
testConflictingMembers("maplike<long, long>", member, mapRWMembers, False)
for member in disallowedMemberNames:
testConflictingMembers("setlike<long>", member, setRWMembers, False)
for member in mapDisallowedNonMethodNames:
testConflictingMembers("maplike<long, long>", member, mapRWMembers, True)
for member in setDisallowedNonMethodNames:
testConflictingMembers("setlike<long>", member, setRWMembers, True)
shouldPass(
"Inheritance of maplike/setlike with child member collision",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
undefined entries();
};
""",
mapRWMembers,
numProductions=2,
)
shouldPass(
"Inheritance of multi-level maplike/setlike with child member collision",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
};
interface Foo3 : Foo2 {
undefined entries();
};
""",
mapRWMembers,
numProductions=3,
)
shouldFail(
"Maplike interface with mixin member collision",
"""
interface Foo1 {
maplike<long, long>;
};
interface mixin Foo2 {
undefined entries();
};
Foo1 includes Foo2;
""",
)
shouldPass(
"Inherited Maplike interface with consequential interface member collision",
"""
interface Foo1 {
maplike<long, long>;
};
interface mixin Foo2 {
undefined entries();
};
interface Foo3 : Foo1 {
};
Foo3 includes Foo2;
""",
mapRWMembers,
numProductions=4,
)
shouldFail(
"Inheritance of name collision with child maplike/setlike",
"""
interface Foo1 {
undefined entries();
};
interface Foo2 : Foo1 {
maplike<long, long>;
};
""",
)
shouldFail(
"Inheritance of multi-level name collision with child maplike/setlike",
"""
interface Foo1 {
undefined entries();
};
interface Foo2 : Foo1 {
};
interface Foo3 : Foo2 {
maplike<long, long>;
};
""",
)
shouldPass(
"Inheritance of attribute collision with parent maplike/setlike",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
attribute double size;
};
""",
mapRWMembers,
numProductions=2,
)
shouldPass(
"Inheritance of multi-level attribute collision with parent maplike/setlike",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
};
interface Foo3 : Foo2 {
attribute double size;
};
""",
mapRWMembers,
numProductions=3,
)
shouldFail(
"Inheritance of attribute collision with child maplike/setlike",
"""
interface Foo1 {
attribute double size;
};
interface Foo2 : Foo1 {
maplike<long, long>;
};
""",
)
shouldFail(
"Inheritance of multi-level attribute collision with child maplike/setlike",
"""
interface Foo1 {
attribute double size;
};
interface Foo2 : Foo1 {
};
interface Foo3 : Foo2 {
maplike<long, long>;
};
""",
)
shouldFail(
"Inheritance of attribute/rw function collision with child maplike/setlike",
"""
interface Foo1 {
attribute double set;
};
interface Foo2 : Foo1 {
maplike<long, long>;
};
""",
)
shouldFail(
"Inheritance of const/rw function collision with child maplike/setlike",
"""
interface Foo1 {
const double set = 0;
};
interface Foo2 : Foo1 {
maplike<long, long>;
};
""",
)
shouldPass(
"Inheritance of rw function with same name in child maplike/setlike",
"""
interface Foo1 {
maplike<long, long>;
};
interface Foo2 : Foo1 {
undefined clear();
};
""",
mapRWMembers,
numProductions=2,
)
shouldFail(
"Inheritance of unforgeable attribute collision with child maplike/setlike",
"""
interface Foo1 {
[LegacyUnforgeable]
attribute double size;
};
interface Foo2 : Foo1 {
maplike<long, long>;
};
""",
)
shouldFail(
"Inheritance of multi-level unforgeable attribute collision with child maplike/setlike",
"""
interface Foo1 {
[LegacyUnforgeable]
attribute double size;
};
interface Foo2 : Foo1 {
};
interface Foo3 : Foo2 {
maplike<long, long>;
};
""",
)
shouldPass(
"Interface with readonly allowable overrides",
"""
interface Foo1 {
readonly setlike<long>;
readonly attribute boolean clear;
};
""",
setROMembers + [("clear", WebIDL.IDLAttribute)],
)
r = shouldPass(
"Check proper override of clear/delete/set",
"""
interface Foo1 {
maplike<long, long>;
long clear(long a, long b, double c, double d);
long set(long a, long b, double c, double d);
long delete(long a, long b, double c, double d);
};
""",
mapRWMembers,
)
for m in r[0].members:
if m.identifier.name in ["clear", "set", "delete"]:
harness.ok(m.isMethod(), "%s should be a method" % m.identifier.name)
harness.check(
m.maxArgCount, 4, "%s should have 4 arguments" % m.identifier.name
)
harness.ok(
not m.isMaplikeOrSetlikeOrIterableMethod(),
"%s should not be a maplike/setlike function" % m.identifier.name,
)

View File

@@ -0,0 +1,534 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse("interface mixin Foo { };")
results = parser.finish()
harness.ok(True, "Empty interface mixin parsed without error.")
harness.check(len(results), 1, "Should be one production")
harness.ok(
isinstance(results[0], WebIDL.IDLInterfaceMixin),
"Should be an IDLInterfaceMixin",
)
mixin = results[0]
harness.check(
mixin.identifier.QName(), "::Foo", "Interface mixin has the right QName"
)
harness.check(mixin.identifier.name, "Foo", "Interface mixin has the right name")
parser = parser.reset()
parser.parse(
"""
interface mixin QNameBase {
const long foo = 3;
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should be one productions")
harness.ok(
isinstance(results[0], WebIDL.IDLInterfaceMixin),
"Should be an IDLInterfaceMixin",
)
harness.check(len(results[0].members), 1, "Expect 1 productions")
mixin = results[0]
harness.check(
mixin.members[0].identifier.QName(),
"::QNameBase::foo",
"Member has the right QName",
)
parser = parser.reset()
parser.parse(
"""
interface mixin A {
readonly attribute boolean x;
undefined foo();
};
partial interface mixin A {
readonly attribute boolean y;
undefined foo(long arg);
};
"""
)
results = parser.finish()
harness.check(
len(results), 2, "Should have two results with partial interface mixin"
)
mixin = results[0]
harness.check(
len(mixin.members), 3, "Should have three members with partial interface mixin"
)
harness.check(
mixin.members[0].identifier.name,
"x",
"First member should be x with partial interface mixin",
)
harness.check(
mixin.members[1].identifier.name,
"foo",
"Second member should be foo with partial interface mixin",
)
harness.check(
len(mixin.members[1].signatures()),
2,
"Should have two foo signatures with partial interface mixin",
)
harness.check(
mixin.members[2].identifier.name,
"y",
"Third member should be y with partial interface mixin",
)
parser = parser.reset()
parser.parse(
"""
partial interface mixin A {
readonly attribute boolean y;
undefined foo(long arg);
};
interface mixin A {
readonly attribute boolean x;
undefined foo();
};
"""
)
results = parser.finish()
harness.check(
len(results), 2, "Should have two results with reversed partial interface mixin"
)
mixin = results[1]
harness.check(
len(mixin.members),
3,
"Should have three members with reversed partial interface mixin",
)
harness.check(
mixin.members[0].identifier.name,
"x",
"First member should be x with reversed partial interface mixin",
)
harness.check(
mixin.members[1].identifier.name,
"foo",
"Second member should be foo with reversed partial interface mixin",
)
harness.check(
len(mixin.members[1].signatures()),
2,
"Should have two foo signatures with reversed partial interface mixin",
)
harness.check(
mixin.members[2].identifier.name,
"y",
"Third member should be y with reversed partial interface mixin",
)
parser = parser.reset()
parser.parse(
"""
interface Interface {};
interface mixin Mixin {
attribute short x;
};
Interface includes Mixin;
"""
)
results = parser.finish()
iface = results[0]
harness.check(len(iface.members), 1, "Should merge members from mixins")
harness.check(
iface.members[0].identifier.name, "x", "Should merge members from mixins"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin A {
readonly attribute boolean x;
};
interface mixin A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow two non-partial interface mixins with the same name"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
partial interface mixin A {
readonly attribute boolean x;
};
partial interface mixin A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Must have a non-partial interface mixin for a given name")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
boolean x;
};
partial interface mixin A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow a name collision between partial interface "
"mixin and other object",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary A {
boolean x;
};
interface mixin A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow a name collision between interface mixin " "and other object",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin A {
readonly attribute boolean x;
};
interface A;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow a name collision between external interface "
"and interface mixin",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[SomeRandomAnnotation]
interface mixin A {
readonly attribute boolean y;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow unknown extended attributes on interface mixins"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin A {
getter double (DOMString propertyName);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow getters on interface mixins")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin A {
setter undefined (DOMString propertyName, double propertyValue);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow setters on interface mixins")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin A {
deleter undefined (DOMString propertyName);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow deleters on interface mixins")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin A {
legacycaller double compute(double x);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow legacycallers on interface mixins")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin A {
inherit attribute x;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow inherited attribute on interface mixins")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Interface {};
interface NotMixin {
attribute short x;
};
Interface includes NotMixin;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should fail if the right side does not point an interface mixin")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin NotInterface {};
interface mixin Mixin {
attribute short x;
};
NotInterface includes Mixin;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should fail if the left side does not point an interface")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin Mixin {
iterable<DOMString>;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should fail if an interface mixin includes iterable")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin Mixin {
setlike<DOMString>;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should fail if an interface mixin includes setlike")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface mixin Mixin {
maplike<DOMString, DOMString>;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should fail if an interface mixin includes maplike")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Interface {
attribute short attr;
};
interface mixin Mixin {
attribute short attr;
};
Interface includes Mixin;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should fail if the included mixin interface has duplicated member"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Interface {};
interface mixin Mixin1 {
attribute short attr;
};
interface mixin Mixin2 {
attribute short attr;
};
Interface includes Mixin1;
Interface includes Mixin2;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should fail if the included mixin interfaces have duplicated member"
)
parser = parser.reset()
parser.parse(
"""
[Global=Window, Exposed=Window] interface Window {};
[Global=Worker, Exposed=Worker] interface Worker {};
[Exposed=Window]
interface Base {};
interface mixin Mixin {
Base returnSelf();
};
Base includes Mixin;
"""
)
results = parser.finish()
base = results[2]
attr = base.members[0]
harness.check(
attr.exposureSet,
set(["Window"]),
"Should expose on globals where the base interfaces are exposed",
)
parser = parser.reset()
parser.parse(
"""
[Global=Window, Exposed=Window] interface Window {};
[Global=Worker, Exposed=Worker] interface Worker {};
[Exposed=Window]
interface Base {};
[Exposed=Window]
interface mixin Mixin {
attribute short a;
};
Base includes Mixin;
"""
)
results = parser.finish()
base = results[2]
attr = base.members[0]
harness.check(
attr.exposureSet, set(["Window"]), "Should follow [Exposed] on interface mixin"
)
parser = parser.reset()
parser.parse(
"""
[Global=Window, Exposed=Window] interface Window {};
[Global=Worker, Exposed=Worker] interface Worker {};
[Exposed=Window]
interface Base1 {};
[Exposed=Worker]
interface Base2 {};
interface mixin Mixin {
attribute short a;
};
Base1 includes Mixin;
Base2 includes Mixin;
"""
)
results = parser.finish()
base = results[2]
attr = base.members[0]
harness.check(
attr.exposureSet,
set(["Window", "Worker"]),
"Should expose on all globals where including interfaces are " "exposed",
)
base = results[3]
attr = base.members[0]
harness.check(
attr.exposureSet,
set(["Window", "Worker"]),
"Should expose on all globals where including interfaces are " "exposed",
)

View File

@@ -0,0 +1,11 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
[LegacyTreatNonObjectAsNull] callback Function = any(any... arguments);
"""
)
results = parser.finish()
callback = results[0]
harness.check(callback._treatNonObjectAsNull, True, "Got the expected value")

View File

@@ -0,0 +1,86 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import WebIDL
def should_throw(parser, harness, message, code):
parser = parser.reset()
threw = False
try:
parser.parse(code)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown: %s" % message)
def WebIDLTest(parser, harness):
# The [LegacyLenientSetter] extended attribute MUST take no arguments.
should_throw(
parser,
harness,
"no arguments",
"""
interface I {
[LegacyLenientSetter=X] readonly attribute long A;
};
""",
)
# An attribute with the [LegacyLenientSetter] extended attribute MUST NOT
# also be declared with the [PutForwards] extended attribute.
should_throw(
parser,
harness,
"PutForwards",
"""
interface I {
[PutForwards=B, LegacyLenientSetter] readonly attribute J A;
};
interface J {
attribute long B;
};
""",
)
# An attribute with the [LegacyLenientSetter] extended attribute MUST NOT
# also be declared with the [Replaceable] extended attribute.
should_throw(
parser,
harness,
"Replaceable",
"""
interface I {
[Replaceable, LegacyLenientSetter] readonly attribute J A;
};
""",
)
# The [LegacyLenientSetter] extended attribute MUST NOT be used on an
# attribute that is not read only.
should_throw(
parser,
harness,
"writable attribute",
"""
interface I {
[LegacyLenientSetter] attribute long A;
};
""",
)
# The [LegacyLenientSetter] extended attribute MUST NOT be used on a
# static attribute.
should_throw(
parser,
harness,
"static attribute",
"""
interface I {
[LegacyLenientSetter] static readonly attribute long A;
};
""",
)

430
third_party/WebIDL/tests/test_method.py vendored Normal file
View File

@@ -0,0 +1,430 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestMethods {
undefined basic();
static undefined basicStatic();
undefined basicWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
boolean basicBoolean();
static boolean basicStaticBoolean();
boolean basicBooleanWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
undefined optionalArg(optional byte? arg1, optional sequence<byte> arg2);
undefined variadicArg(byte?... arg1);
object getObject();
undefined setObject(object arg1);
undefined setAny(any arg1);
float doFloats(float arg1);
};
"""
)
results = parser.finish()
harness.ok(True, "TestMethods interface parsed without error.")
harness.check(len(results), 1, "Should be one production.")
iface = results[0]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(), "::TestMethods", "Interface has the right QName"
)
harness.check(iface.identifier.name, "TestMethods", "Interface has the right name")
harness.check(len(iface.members), 12, "Expect 12 members")
methods = iface.members
def checkArgument(argument, QName, name, type, optional, variadic):
harness.ok(isinstance(argument, WebIDL.IDLArgument), "Should be an IDLArgument")
harness.check(
argument.identifier.QName(), QName, "Argument has the right QName"
)
harness.check(argument.identifier.name, name, "Argument has the right name")
harness.check(str(argument.type), type, "Argument has the right return type")
harness.check(
argument.optional, optional, "Argument has the right optional value"
)
harness.check(
argument.variadic, variadic, "Argument has the right variadic value"
)
def checkMethod(
method,
QName,
name,
signatures,
static=False,
getter=False,
setter=False,
deleter=False,
legacycaller=False,
stringifier=False,
):
harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
harness.ok(method.isMethod(), "Method is a method")
harness.ok(not method.isAttr(), "Method is not an attr")
harness.ok(not method.isConst(), "Method is not a const")
harness.check(method.identifier.QName(), QName, "Method has the right QName")
harness.check(method.identifier.name, name, "Method has the right name")
harness.check(method.isStatic(), static, "Method has the correct static value")
harness.check(method.isGetter(), getter, "Method has the correct getter value")
harness.check(method.isSetter(), setter, "Method has the correct setter value")
harness.check(
method.isDeleter(), deleter, "Method has the correct deleter value"
)
harness.check(
method.isLegacycaller(),
legacycaller,
"Method has the correct legacycaller value",
)
harness.check(
method.isStringifier(),
stringifier,
"Method has the correct stringifier value",
)
harness.check(
len(method.signatures()),
len(signatures),
"Method has the correct number of signatures",
)
sigpairs = zip(method.signatures(), signatures)
for gotSignature, expectedSignature in sigpairs:
(gotRetType, gotArgs) = gotSignature
(expectedRetType, expectedArgs) = expectedSignature
harness.check(
str(gotRetType), expectedRetType, "Method has the expected return type."
)
for i in range(0, len(gotArgs)):
(QName, name, type, optional, variadic) = expectedArgs[i]
checkArgument(gotArgs[i], QName, name, type, optional, variadic)
checkMethod(methods[0], "::TestMethods::basic", "basic", [("Undefined", [])])
checkMethod(
methods[1],
"::TestMethods::basicStatic",
"basicStatic",
[("Undefined", [])],
static=True,
)
checkMethod(
methods[2],
"::TestMethods::basicWithSimpleArgs",
"basicWithSimpleArgs",
[
(
"Undefined",
[
(
"::TestMethods::basicWithSimpleArgs::arg1",
"arg1",
"Boolean",
False,
False,
),
(
"::TestMethods::basicWithSimpleArgs::arg2",
"arg2",
"Byte",
False,
False,
),
(
"::TestMethods::basicWithSimpleArgs::arg3",
"arg3",
"UnsignedLong",
False,
False,
),
],
)
],
)
checkMethod(
methods[3], "::TestMethods::basicBoolean", "basicBoolean", [("Boolean", [])]
)
checkMethod(
methods[4],
"::TestMethods::basicStaticBoolean",
"basicStaticBoolean",
[("Boolean", [])],
static=True,
)
checkMethod(
methods[5],
"::TestMethods::basicBooleanWithSimpleArgs",
"basicBooleanWithSimpleArgs",
[
(
"Boolean",
[
(
"::TestMethods::basicBooleanWithSimpleArgs::arg1",
"arg1",
"Boolean",
False,
False,
),
(
"::TestMethods::basicBooleanWithSimpleArgs::arg2",
"arg2",
"Byte",
False,
False,
),
(
"::TestMethods::basicBooleanWithSimpleArgs::arg3",
"arg3",
"UnsignedLong",
False,
False,
),
],
)
],
)
checkMethod(
methods[6],
"::TestMethods::optionalArg",
"optionalArg",
[
(
"Undefined",
[
(
"::TestMethods::optionalArg::arg1",
"arg1",
"ByteOrNull",
True,
False,
),
(
"::TestMethods::optionalArg::arg2",
"arg2",
"ByteSequence",
True,
False,
),
],
)
],
)
checkMethod(
methods[7],
"::TestMethods::variadicArg",
"variadicArg",
[
(
"Undefined",
[
(
"::TestMethods::variadicArg::arg1",
"arg1",
"ByteOrNull",
True,
True,
)
],
)
],
)
checkMethod(methods[8], "::TestMethods::getObject", "getObject", [("Object", [])])
checkMethod(
methods[9],
"::TestMethods::setObject",
"setObject",
[
(
"Undefined",
[("::TestMethods::setObject::arg1", "arg1", "Object", False, False)],
)
],
)
checkMethod(
methods[10],
"::TestMethods::setAny",
"setAny",
[("Undefined", [("::TestMethods::setAny::arg1", "arg1", "Any", False, False)])],
)
checkMethod(
methods[11],
"::TestMethods::doFloats",
"doFloats",
[("Float", [("::TestMethods::doFloats::arg1", "arg1", "Float", False, False)])],
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
undefined foo(optional float bar = 1);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should allow integer to float type corecion")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[GetterThrows] undefined foo();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [GetterThrows] on methods")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[SetterThrows] undefined foo();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [SetterThrows] on methods")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[Throw] undefined foo();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should spell [Throws] correctly on methods")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
undefined __noSuchMethod__();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow __noSuchMethod__ methods")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[Throws, LenientFloat]
undefined foo(float myFloat);
[Throws]
undefined foo();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should allow LenientFloat to be only in a specific overload")
parser = parser.reset()
parser.parse(
"""
interface A {
[Throws]
undefined foo();
[Throws, LenientFloat]
undefined foo(float myFloat);
};
"""
)
results = parser.finish()
iface = results[0]
methods = iface.members
lenientFloat = methods[0].getExtendedAttribute("LenientFloat")
harness.ok(
lenientFloat is not None,
"LenientFloat in overloads must be added to the method",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[Throws, LenientFloat]
undefined foo(float myFloat);
[Throws]
undefined foo(float myFloat, float yourFloat);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should prevent overloads from getting different restricted float behavior",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[Throws]
undefined foo(float myFloat, float yourFloat);
[Throws, LenientFloat]
undefined foo(float myFloat);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should prevent overloads from getting different restricted float behavior (2)",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[Throws, LenientFloat]
undefined foo(float myFloat);
[Throws, LenientFloat]
undefined foo(short myShort);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should prevent overloads from getting redundant [LenientFloat]")

View File

@@ -0,0 +1,235 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
namespace MyNamespace {
attribute any foo;
any bar();
};
"""
)
results = parser.finish()
harness.check(len(results), 1, "Should have a thing.")
harness.ok(results[0].isNamespace(), "Our thing should be a namespace")
harness.check(len(results[0].members), 2, "Should have two things in our namespace")
harness.ok(results[0].members[0].isAttr(), "First member is attribute")
harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
harness.ok(results[0].members[1].isMethod(), "Second member is method")
harness.ok(results[0].members[1].isStatic(), "Operation should be static")
parser = parser.reset()
parser.parse(
"""
namespace MyNamespace {
attribute any foo;
};
partial namespace MyNamespace {
any bar();
};
"""
)
results = parser.finish()
harness.check(len(results), 2, "Should have things.")
harness.ok(results[0].isNamespace(), "Our thing should be a namespace")
harness.check(len(results[0].members), 2, "Should have two things in our namespace")
harness.ok(results[0].members[0].isAttr(), "First member is attribute")
harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
harness.ok(results[0].members[1].isMethod(), "Second member is method")
harness.ok(results[0].members[1].isStatic(), "Operation should be static")
parser = parser.reset()
parser.parse(
"""
partial namespace MyNamespace {
any bar();
};
namespace MyNamespace {
attribute any foo;
};
"""
)
results = parser.finish()
harness.check(len(results), 2, "Should have things.")
harness.ok(results[1].isNamespace(), "Our thing should be a namespace")
harness.check(len(results[1].members), 2, "Should have two things in our namespace")
harness.ok(results[1].members[0].isAttr(), "First member is attribute")
harness.ok(results[1].members[0].isStatic(), "Attribute should be static")
harness.ok(results[1].members[1].isMethod(), "Second member is method")
harness.ok(results[1].members[1].isStatic(), "Operation should be static")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
namespace MyNamespace {
static attribute any foo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
namespace MyNamespace {
static any bar();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
namespace MyNamespace {
any bar();
};
interface MyNamespace {
any baz();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface MyNamespace {
any baz();
};
namespace MyNamespace {
any bar();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
namespace MyNamespace {
any baz();
};
namespace MyNamespace {
any bar();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
partial namespace MyNamespace {
any baz();
};
interface MyNamespace {
any bar();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
namespace MyNamespace {
any bar();
};
partial interface MyNamespace {
any baz();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
partial interface MyNamespace {
any baz();
};
namespace MyNamespace {
any bar();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface MyNamespace {
any bar();
};
partial namespace MyNamespace {
any baz();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,76 @@
import WebIDL
# Import the WebIDL module, so we can do isinstance checks and whatnot
def WebIDLTest(parser, harness):
# Basic functionality
parser.parse(
"""
interface Iface {
[NewObject] readonly attribute Iface attr;
[NewObject] Iface method();
};
"""
)
results = parser.finish()
harness.ok(results, "Should not have thrown on basic [NewObject] usage")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Iface {
[Pure, NewObject] readonly attribute Iface attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[NewObject] attributes must depend on something")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Iface {
[Pure, NewObject] Iface method();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[NewObject] methods must depend on something")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Iface {
[Cached, NewObject, Affects=Nothing] readonly attribute Iface attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[NewObject] attributes must not be [Cached]")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Iface {
[StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]")

View File

@@ -0,0 +1,145 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestNullableEquivalency1 {
attribute long a;
attribute long? b;
};
interface TestNullableEquivalency2 {
attribute ArrayBuffer a;
attribute ArrayBuffer? b;
};
/* Can't have dictionary-valued attributes, so can't test that here */
enum TestNullableEquivalency4Enum {
"Foo",
"Bar"
};
interface TestNullableEquivalency4 {
attribute TestNullableEquivalency4Enum a;
attribute TestNullableEquivalency4Enum? b;
};
interface TestNullableEquivalency5 {
attribute TestNullableEquivalency4 a;
attribute TestNullableEquivalency4? b;
};
interface TestNullableEquivalency6 {
attribute boolean a;
attribute boolean? b;
};
interface TestNullableEquivalency7 {
attribute DOMString a;
attribute DOMString? b;
};
interface TestNullableEquivalency8 {
attribute float a;
attribute float? b;
};
interface TestNullableEquivalency9 {
attribute double a;
attribute double? b;
};
interface TestNullableEquivalency10 {
attribute object a;
attribute object? b;
};
"""
)
for decl in parser.finish():
if decl.isInterface():
checkEquivalent(decl, harness)
def checkEquivalent(iface, harness):
type1 = iface.members[0].type
type2 = iface.members[1].type
harness.check(type1.nullable(), False, "attr1 should not be nullable")
harness.check(type2.nullable(), True, "attr2 should be nullable")
# We don't know about type1, but type2, the nullable type, definitely
# shouldn't be builtin.
harness.check(type2.builtin, False, "attr2 should not be builtin")
# Ensure that all attributes of type2 match those in type1, except for:
# - names on an ignore list,
# - names beginning with '_',
# - functions which throw when called with no args, and
# - class-level non-callables ("static variables").
#
# Yes, this is an ugly, fragile hack. But it finds bugs...
for attr in dir(type1):
if (
attr.startswith("_")
or attr
in [
"nullable",
"builtin",
"filename",
"location",
"inner",
"QName",
"getDeps",
"name",
"prettyName",
]
or (hasattr(type(type1), attr) and not callable(getattr(type1, attr)))
):
continue
a1 = getattr(type1, attr)
if callable(a1):
try:
v1 = a1()
except AssertionError:
# Various methods assert that they're called on objects of
# the right type, skip them if the assert fails.
continue
except TypeError:
# a1 requires positional arguments, so skip this attribute.
continue
try:
a2 = getattr(type2, attr)
except WebIDL.WebIDLError:
harness.ok(
False,
"Missing %s attribute on type %s in %s" % (attr, type2, iface),
)
continue
if not callable(a2):
harness.ok(
False,
"%s attribute on type %s in %s wasn't callable"
% (attr, type2, iface),
)
continue
v2 = a2()
harness.check(v2, v1, "%s method return value" % attr)
else:
try:
a2 = getattr(type2, attr)
except WebIDL.WebIDLError:
harness.ok(
False,
"Missing %s attribute on type %s in %s" % (attr, type2, iface),
)
continue
harness.check(a2, a1, "%s attribute should match" % attr)

View File

@@ -0,0 +1,19 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface NullableVoid {
void? foo();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,287 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
def WebIDLTest(parser, harness):
# Test dictionary as inner type
harness.should_throw(
parser,
"""
dictionary A {
boolean member;
};
interface B {
attribute ObservableArray<A> foo;
};
""",
"use dictionary as inner type",
)
# Test sequence as inner type
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<sequence<boolean>> foo;
};
""",
"use sequence as inner type",
)
# Test sequence<dictionary> as inner type
harness.should_throw(
parser,
"""
dictionary A {
boolean member;
};
interface B {
attribute ObservableArray<sequence<A>> foo;
};
""",
"use sequence<dictionary> as inner type",
)
# Test record as inner type
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<record<DOMString, boolean>> foo;
};
""",
"use record as inner type",
)
# Test record<dictionary> as inner type
harness.should_throw(
parser,
"""
dictionary A {
boolean member;
};
interface B {
attribute ObservableArray<record<DOMString, A>> foo;
};
""",
"use record<dictionary> as inner type",
)
# Test observable array as inner type
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<ObservableArray<boolean>> foo;
};
""",
"use ObservableArray as inner type",
)
# Test nullable attribute
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<boolean>? foo;
};
""",
"nullable",
)
# Test sequence
harness.should_throw(
parser,
"""
interface A {
undefined foo(sequence<ObservableArray<boolean>> foo);
};
""",
"used in sequence",
)
# Test record
harness.should_throw(
parser,
"""
interface A {
undefined foo(record<DOMString, ObservableArray<boolean>> foo);
};
""",
"used in record",
)
# Test promise
harness.should_throw(
parser,
"""
interface A {
Promise<ObservableArray<boolean>> foo();
};
""",
"used in promise",
)
# Test union
harness.should_throw(
parser,
"""
interface A {
attribute (DOMString or ObservableArray<boolean>>) foo;
};
""",
"used in union",
)
# Test dictionary member
harness.should_throw(
parser,
"""
dictionary A {
ObservableArray<boolean> foo;
};
""",
"used on dictionary member type",
)
# Test argument
harness.should_throw(
parser,
"""
interface A {
undefined foo(ObservableArray<boolean> foo);
};
""",
"used on argument",
)
# Test static attribute
harness.should_throw(
parser,
"""
interface A {
static attribute ObservableArray<boolean> foo;
};
""",
"used on static attribute type",
)
# Test iterable
harness.should_throw(
parser,
"""
interface A {
iterable<ObservableArray<boolean>>;
};
""",
"used in iterable",
)
# Test maplike
harness.should_throw(
parser,
"""
interface A {
maplike<long, ObservableArray<boolean>>;
};
""",
"used in maplike",
)
# Test setlike
harness.should_throw(
parser,
"""
interface A {
setlike<ObservableArray<boolean>>;
};
""",
"used in setlike",
)
# Test JS implemented interface
harness.should_throw(
parser,
"""
[JSImplementation="@mozilla.org/dom/test-interface-js;1"]
interface A {
readonly attribute ObservableArray<boolean> foo;
};
""",
"used in JS implemented interface",
)
# Test namespace
harness.should_throw(
parser,
"""
namespace A {
readonly attribute ObservableArray<boolean> foo;
};
""",
"used in namespaces",
)
# Test [Cached] extended attribute
harness.should_throw(
parser,
"""
interface A {
[Cached, Pure]
readonly attribute ObservableArray<boolean> foo;
};
""",
"have Cached extended attribute",
)
# Test [StoreInSlot] extended attribute
harness.should_throw(
parser,
"""
interface A {
[StoreInSlot, Pure]
readonly attribute ObservableArray<boolean> foo;
};
""",
"have StoreInSlot extended attribute",
)
# Test regular attribute
parser = parser.reset()
parser.parse(
"""
interface A {
readonly attribute ObservableArray<boolean> foo;
attribute ObservableArray<[Clamp] octet> bar;
attribute ObservableArray<long?> baz;
attribute ObservableArray<(boolean or long)> qux;
};
"""
)
results = parser.finish()
A = results[0]
foo = A.members[0]
harness.ok(foo.readonly, "A.foo is readonly attribute")
harness.ok(foo.type.isObservableArray(), "A.foo is ObservableArray type")
harness.check(
foo.slotIndices[A.identifier.name], 0, "A.foo should be stored in slot"
)
bar = A.members[1]
harness.ok(bar.type.isObservableArray(), "A.bar is ObservableArray type")
harness.check(
bar.slotIndices[A.identifier.name], 1, "A.bar should be stored in slot"
)
harness.ok(bar.type.inner.hasClamp(), "A.bar's inner type should be clamped")
baz = A.members[2]
harness.ok(baz.type.isObservableArray(), "A.baz is ObservableArray type")
harness.check(
baz.slotIndices[A.identifier.name], 2, "A.baz should be stored in slot"
)
harness.ok(baz.type.inner.nullable(), "A.baz's inner type should be nullable")
qux = A.members[3]
harness.ok(qux.type.isObservableArray(), "A.qux is ObservableArray type")
harness.check(
qux.slotIndices[A.identifier.name], 3, "A.qux should be stored in slot"
)
harness.ok(qux.type.inner.isUnion(), "A.qux's inner type should be union")

View File

@@ -0,0 +1,38 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface OptionalConstraints1 {
undefined foo(optional byte arg1, byte arg2);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
not threw,
"Should not have thrown on non-optional argument following "
"optional argument.",
)
parser = parser.reset()
parser.parse(
"""
interface OptionalConstraints2 {
undefined foo(optional byte arg1 = 1, optional byte arg2 = 2,
optional byte arg3, optional byte arg4 = 4,
optional byte arg5, optional byte arg6 = 9);
};
"""
)
results = parser.finish()
args = results[0].members[0].signatures()[0][1]
harness.check(len(args), 6, "Should have 6 arguments")
harness.check(args[5].defaultValue.value, 9, "Should have correct default value")

View File

@@ -0,0 +1,74 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestOverloads {
undefined basic();
undefined basic(long arg1);
boolean abitharder(TestOverloads foo);
boolean abitharder(boolean foo);
undefined abitharder(ArrayBuffer? foo);
undefined withVariadics(long... numbers);
undefined withVariadics(TestOverloads iface);
undefined withVariadics(long num, TestOverloads iface);
undefined optionalTest();
undefined optionalTest(optional long num1, long num2);
};
"""
)
results = parser.finish()
harness.ok(True, "TestOverloads interface parsed without error.")
harness.check(len(results), 1, "Should be one production.")
iface = results[0]
harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
harness.check(
iface.identifier.QName(), "::TestOverloads", "Interface has the right QName"
)
harness.check(
iface.identifier.name, "TestOverloads", "Interface has the right name"
)
harness.check(len(iface.members), 4, "Expect %s members" % 4)
member = iface.members[0]
harness.check(
member.identifier.QName(),
"::TestOverloads::basic",
"Method has the right QName",
)
harness.check(member.identifier.name, "basic", "Method has the right name")
harness.check(member.hasOverloads(), True, "Method has overloads")
signatures = member.signatures()
harness.check(len(signatures), 2, "Method should have 2 signatures")
(retval, argumentSet) = signatures[0]
harness.check(str(retval), "Undefined", "Expect an undefined retval")
harness.check(len(argumentSet), 0, "Expect an empty argument set")
(retval, argumentSet) = signatures[1]
harness.check(str(retval), "Undefined", "Expect an undefined retval")
harness.check(len(argumentSet), 1, "Expect an argument set with one argument")
argument = argumentSet[0]
harness.ok(isinstance(argument, WebIDL.IDLArgument), "Should be an IDLArgument")
harness.check(
argument.identifier.QName(),
"::TestOverloads::basic::arg1",
"Argument has the right QName",
)
harness.check(argument.identifier.name, "arg1", "Argument has the right name")
harness.check(str(argument.type), "Long", "Argument has the right type")
member = iface.members[3]
harness.check(
len(member.overloadsForArgCount(0)), 1, "Only one overload for no args"
)
harness.check(len(member.overloadsForArgCount(1)), 0, "No overloads for one arg")
harness.check(
len(member.overloadsForArgCount(2)), 1, "Only one overload for two args"
)

180
third_party/WebIDL/tests/test_promise.py vendored Normal file
View File

@@ -0,0 +1,180 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface A {
legacycaller Promise<any> foo();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow Promise return values for legacycaller.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
Promise<any> foo();
long foo(long arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow overloads which have both Promise and "
"non-Promise return types.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
long foo(long arg);
Promise<any> foo();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow overloads which have both Promise and "
"non-Promise return types.",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
Promise<any>? foo();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow nullable Promise return values.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
undefined foo(Promise<any>? arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow nullable Promise arguments.")
parser = parser.reset()
parser.parse(
"""
interface A {
Promise<any> foo();
Promise<any> foo(long arg);
};
"""
)
parser.finish()
harness.ok(
True, "Should allow overloads which only have Promise and return " "types."
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
attribute Promise<any> attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow writable Promise-typed attributes.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[LegacyLenientSetter] readonly attribute Promise<any> attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should not allow [LegacyLenientSetter] Promise-typed attributes."
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[PutForwards=bar] readonly attribute Promise<any> attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [PutForwards] Promise-typed attributes.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[Replaceable] readonly attribute Promise<any> attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [Replaceable] Promise-typed attributes.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface A {
[SameObject] readonly attribute Promise<any> attr;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow [SameObject] Promise-typed attributes.")

View File

@@ -0,0 +1,110 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface TestIface {
static attribute boolean prototype;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "The identifier of a static attribute must not be 'prototype'")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestIface {
static boolean prototype();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "The identifier of a static operation must not be 'prototype'")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestIface {
const boolean prototype = true;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "The identifier of a constant must not be 'prototype'")
# Make sure that we can parse non-static attributes with 'prototype' as identifier.
parser = parser.reset()
parser.parse(
"""
interface TestIface {
attribute boolean prototype;
};
"""
)
results = parser.finish()
testIface = results[0]
harness.check(
testIface.members[0].isStatic(), False, "Attribute should not be static"
)
harness.check(
testIface.members[0].identifier.name,
"prototype",
"Attribute identifier should be 'prototype'",
)
# Make sure that we can parse non-static operations with 'prototype' as identifier.
parser = parser.reset()
parser.parse(
"""
interface TestIface {
boolean prototype();
};
"""
)
results = parser.finish()
testIface = results[0]
harness.check(
testIface.members[0].isStatic(), False, "Operation should not be static"
)
harness.check(
testIface.members[0].identifier.name,
"prototype",
"Operation identifier should be 'prototype'",
)
# Make sure that we can parse dictionary members with 'prototype' as identifier.
parser = parser.reset()
parser.parse(
"""
dictionary TestDict {
boolean prototype;
};
"""
)
results = parser.finish()
testDict = results[0]
harness.check(
testDict.members[0].identifier.name,
"prototype",
"Dictionary member should be 'prototype'",
)

View File

@@ -0,0 +1,122 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface I {
[PutForwards=B] readonly attribute long A;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface I {
[PutForwards=B] readonly attribute J A;
};
interface J {
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface I {
[PutForwards=B] attribute J A;
};
interface J {
attribute long B;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface I {
[PutForwards=B] static readonly attribute J A;
};
interface J {
attribute long B;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
callback interface I {
[PutForwards=B] readonly attribute J A;
};
interface J {
attribute long B;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface I {
[PutForwards=C] readonly attribute J A;
[PutForwards=C] readonly attribute J B;
};
interface J {
[PutForwards=D] readonly attribute K C;
};
interface K {
[PutForwards=A] readonly attribute I D;
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

61
third_party/WebIDL/tests/test_record.py vendored Normal file
View File

@@ -0,0 +1,61 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
dictionary Dict {};
interface RecordArg {
undefined foo(record<DOMString, Dict> arg);
};
"""
)
results = parser.finish()
harness.check(len(results), 2, "Should know about two things")
harness.ok(
isinstance(results[1], WebIDL.IDLInterface), "Should have an interface here"
)
members = results[1].members
harness.check(len(members), 1, "Should have one member")
harness.ok(members[0].isMethod(), "Should have method")
signature = members[0].signatures()[0]
args = signature[1]
harness.check(len(args), 1, "Should have one arg")
harness.ok(args[0].type.isRecord(), "Should have a record type here")
harness.ok(args[0].type.inner.isDictionary(), "Should have a dictionary inner type")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface RecordUndefinedArg {
undefined foo(record<DOMString, undefined> arg);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "Should have thrown because record can't have undefined as value type."
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
dictionary Dict {
record<DOMString, Dict> val;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on dictionary containing itself via record.")

View File

@@ -0,0 +1,86 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import WebIDL
def should_throw(parser, harness, message, code):
parser = parser.reset()
threw = False
try:
parser.parse(code)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown: %s" % message)
def WebIDLTest(parser, harness):
# The [Replaceable] extended attribute MUST take no arguments.
should_throw(
parser,
harness,
"no arguments",
"""
interface I {
[Replaceable=X] readonly attribute long A;
};
""",
)
# An attribute with the [Replaceable] extended attribute MUST NOT also be
# declared with the [PutForwards] extended attribute.
should_throw(
parser,
harness,
"PutForwards",
"""
interface I {
[PutForwards=B, Replaceable] readonly attribute J A;
};
interface J {
attribute long B;
};
""",
)
# The [Replaceable] extended attribute MUST NOT be used on an attribute
# that is not read only.
should_throw(
parser,
harness,
"writable attribute",
"""
interface I {
[Replaceable] attribute long A;
};
""",
)
# The [Replaceable] extended attribute MUST NOT be used on a static
# attribute.
should_throw(
parser,
harness,
"static attribute",
"""
interface I {
[Replaceable] static readonly attribute long A;
};
""",
)
# The [Replaceable] extended attribute MUST NOT be used on an attribute
# declared on a callback interface.
should_throw(
parser,
harness,
"callback interface",
"""
callback interface I {
[Replaceable] readonly attribute long A;
};
""",
)

View File

@@ -0,0 +1,7 @@
def WebIDLTest(parser, harness):
parser.parse("")
parser.finish()
harness.ok(True, "Parsing nothing doesn't throw.")
parser.parse("interface Foo {};")
parser.finish()
harness.ok(True, "Parsing a silly interface doesn't throw.")

View File

@@ -0,0 +1,541 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
[SecureContext]
interface TestSecureContextOnInterface {
const octet TEST_CONSTANT = 0;
readonly attribute byte testAttribute;
undefined testMethod(byte foo);
};
partial interface TestSecureContextOnInterface {
const octet TEST_CONSTANT_2 = 0;
readonly attribute byte testAttribute2;
undefined testMethod2(byte foo);
};
"""
)
results = parser.finish()
harness.check(
len(results[0].members),
6,
"TestSecureContextOnInterface should have six members",
)
harness.ok(
results[0].getExtendedAttribute("SecureContext"),
"Interface should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[0].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to constant members",
)
harness.ok(
results[0].members[1].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to attribute members",
)
harness.ok(
results[0].members[2].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to method members",
)
harness.ok(
results[0].members[3].getExtendedAttribute("SecureContext"),
(
"[SecureContext] should propagate from interface to "
"constant members from partial interface"
),
)
harness.ok(
results[0].members[4].getExtendedAttribute("SecureContext"),
(
"[SecureContext] should propagate from interface to "
"attribute members from partial interface"
),
)
harness.ok(
results[0].members[5].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to method members from partial interface",
)
# Same thing, but with the partial interface specified first:
parser = parser.reset()
parser.parse(
"""
partial interface TestSecureContextOnInterfaceAfterPartialInterface {
const octet TEST_CONSTANT_2 = 0;
readonly attribute byte testAttribute2;
undefined testMethod2(byte foo);
};
[SecureContext]
interface TestSecureContextOnInterfaceAfterPartialInterface {
const octet TEST_CONSTANT = 0;
readonly attribute byte testAttribute;
undefined testMethod(byte foo);
};
"""
)
results = parser.finish()
harness.check(
len(results[1].members),
6,
"TestSecureContextOnInterfaceAfterPartialInterface should have six members",
)
harness.ok(
results[1].getExtendedAttribute("SecureContext"),
"Interface should have [SecureContext] extended attribute",
)
harness.ok(
results[1].members[0].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to constant members",
)
harness.ok(
results[1].members[1].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to attribute members",
)
harness.ok(
results[1].members[2].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to method members",
)
harness.ok(
results[1].members[3].getExtendedAttribute("SecureContext"),
(
"[SecureContext] should propagate from interface to constant members from "
"partial interface"
),
)
harness.ok(
results[1].members[4].getExtendedAttribute("SecureContext"),
(
"[SecureContext] should propagate from interface to attribute members from "
"partial interface"
),
)
harness.ok(
results[1].members[5].getExtendedAttribute("SecureContext"),
(
"[SecureContext] should propagate from interface to method members from partial "
"interface"
),
)
parser = parser.reset()
parser.parse(
"""
interface TestSecureContextOnPartialInterface {
const octet TEST_CONSTANT = 0;
readonly attribute byte testAttribute;
undefined testMethod(byte foo);
};
[SecureContext]
partial interface TestSecureContextOnPartialInterface {
const octet TEST_CONSTANT_2 = 0;
readonly attribute byte testAttribute2;
undefined testMethod2(byte foo);
};
"""
)
results = parser.finish()
harness.check(
len(results[0].members),
6,
"TestSecureContextOnPartialInterface should have six members",
)
harness.ok(
results[0].getExtendedAttribute("SecureContext") is None,
"[SecureContext] should not propagate from a partial interface to the interface",
)
harness.ok(
results[0].members[0].getExtendedAttribute("SecureContext") is None,
(
"[SecureContext] should not propagate from a partial interface to the interface's "
"constant members"
),
)
harness.ok(
results[0].members[1].getExtendedAttribute("SecureContext") is None,
(
"[SecureContext] should not propagate from a partial interface to the interface's "
"attribute members"
),
)
harness.ok(
results[0].members[2].getExtendedAttribute("SecureContext") is None,
(
"[SecureContext] should not propagate from a partial interface to the interface's "
"method members"
),
)
harness.ok(
results[0].members[3].getExtendedAttribute("SecureContext"),
"Constant members from [SecureContext] partial interface should be [SecureContext]",
)
harness.ok(
results[0].members[4].getExtendedAttribute("SecureContext"),
"Attribute members from [SecureContext] partial interface should be [SecureContext]",
)
harness.ok(
results[0].members[5].getExtendedAttribute("SecureContext"),
"Method members from [SecureContext] partial interface should be [SecureContext]",
)
parser = parser.reset()
parser.parse(
"""
interface TestSecureContextOnInterfaceMembers {
const octet TEST_NON_SECURE_CONSTANT_1 = 0;
[SecureContext]
const octet TEST_SECURE_CONSTANT = 1;
const octet TEST_NON_SECURE_CONSTANT_2 = 2;
readonly attribute byte testNonSecureAttribute1;
[SecureContext]
readonly attribute byte testSecureAttribute;
readonly attribute byte testNonSecureAttribute2;
undefined testNonSecureMethod1(byte foo);
[SecureContext]
undefined testSecureMethod(byte foo);
undefined testNonSecureMethod2(byte foo);
};
"""
)
results = parser.finish()
harness.check(
len(results[0].members),
9,
"TestSecureContextOnInterfaceMembers should have nine members",
)
harness.ok(
results[0].getExtendedAttribute("SecureContext") is None,
"[SecureContext] on members should not propagate up to the interface",
)
harness.ok(
results[0].members[0].getExtendedAttribute("SecureContext") is None,
"Constant should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[1].getExtendedAttribute("SecureContext"),
"Constant should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[2].getExtendedAttribute("SecureContext") is None,
"Constant should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[3].getExtendedAttribute("SecureContext") is None,
"Attribute should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[4].getExtendedAttribute("SecureContext"),
"Attribute should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[5].getExtendedAttribute("SecureContext") is None,
"Attribute should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[6].getExtendedAttribute("SecureContext") is None,
"Method should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[7].getExtendedAttribute("SecureContext"),
"Method should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[8].getExtendedAttribute("SecureContext") is None,
"Method should not have [SecureContext] extended attribute",
)
parser = parser.reset()
parser.parse(
"""
interface TestSecureContextOnPartialInterfaceMembers {
};
partial interface TestSecureContextOnPartialInterfaceMembers {
const octet TEST_NON_SECURE_CONSTANT_1 = 0;
[SecureContext]
const octet TEST_SECURE_CONSTANT = 1;
const octet TEST_NON_SECURE_CONSTANT_2 = 2;
readonly attribute byte testNonSecureAttribute1;
[SecureContext]
readonly attribute byte testSecureAttribute;
readonly attribute byte testNonSecureAttribute2;
undefined testNonSecureMethod1(byte foo);
[SecureContext]
undefined testSecureMethod(byte foo);
undefined testNonSecureMethod2(byte foo);
};
"""
)
results = parser.finish()
harness.check(
len(results[0].members),
9,
"TestSecureContextOnPartialInterfaceMembers should have nine members",
)
harness.ok(
results[0].members[0].getExtendedAttribute("SecureContext") is None,
"Constant from partial interface should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[1].getExtendedAttribute("SecureContext"),
"Constant from partial interface should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[2].getExtendedAttribute("SecureContext") is None,
"Constant from partial interface should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[3].getExtendedAttribute("SecureContext") is None,
"Attribute from partial interface should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[4].getExtendedAttribute("SecureContext"),
"Attribute from partial interface should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[5].getExtendedAttribute("SecureContext") is None,
"Attribute from partial interface should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[6].getExtendedAttribute("SecureContext") is None,
"Method from partial interface should not have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[7].getExtendedAttribute("SecureContext"),
"Method from partial interface should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[8].getExtendedAttribute("SecureContext") is None,
"Method from partial interface should not have [SecureContext] extended attribute",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[SecureContext=something]
interface TestSecureContextTakesNoValue1 {
const octet TEST_SECURE_CONSTANT = 0;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "[SecureContext] must take no arguments (testing on interface)")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestSecureContextForOverloads1 {
[SecureContext]
undefined testSecureMethod(byte foo);
};
partial interface TestSecureContextForOverloads1 {
undefined testSecureMethod(byte foo, byte bar);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
(
"If [SecureContext] appears on an overloaded operation, then it MUST appear on all "
"overloads"
),
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestSecureContextForOverloads2 {
[SecureContext]
undefined testSecureMethod(byte foo);
};
partial interface TestSecureContextForOverloads2 {
[SecureContext]
undefined testSecureMethod(byte foo, byte bar);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
not threw,
"[SecureContext] can appear on an overloaded operation if it appears on all overloads",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[SecureContext]
interface TestSecureContextOnInterfaceAndMember {
[SecureContext]
undefined testSecureMethod(byte foo);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw, "[SecureContext] must not appear on an interface and interface member"
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestSecureContextOnPartialInterfaceAndMember {
};
[SecureContext]
partial interface TestSecureContextOnPartialInterfaceAndMember {
[SecureContext]
undefined testSecureMethod(byte foo);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
(
"[SecureContext] must not appear on a partial interface and one of the partial "
"interface's member's"
),
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[SecureContext]
interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
};
partial interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
[SecureContext]
undefined testSecureMethod(byte foo);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
(
"[SecureContext] must not appear on an interface and one of its partial interface's "
"member's"
),
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[SecureContext]
interface TestSecureContextOnInheritedInterface {
};
interface TestSecureContextNotOnInheritingInterface : TestSecureContextOnInheritedInterface {
undefined testSecureMethod(byte foo);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
(
"[SecureContext] must appear on interfaces that inherit from another [SecureContext] "
"interface"
),
)
# Test 'includes'.
parser = parser.reset()
parser.parse(
"""
[SecureContext]
interface TestSecureContextInterfaceThatIncludesNonSecureContextMixin {
const octet TEST_CONSTANT = 0;
};
interface mixin TestNonSecureContextMixin {
const octet TEST_CONSTANT_2 = 0;
readonly attribute byte testAttribute2;
undefined testMethod2(byte foo);
};
TestSecureContextInterfaceThatIncludesNonSecureContextMixin includes TestNonSecureContextMixin;
"""
)
results = parser.finish()
harness.check(
len(results[0].members),
4,
(
"TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have four "
"members"
),
)
harness.ok(
results[0].getExtendedAttribute("SecureContext"),
"Interface should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[0].getExtendedAttribute("SecureContext"),
(
"[SecureContext] should propagate from interface to constant members even when other "
"members are copied from a non-[SecureContext] interface"
),
)
harness.ok(
results[0].members[1].getExtendedAttribute("SecureContext") is None,
"Constants copied from non-[SecureContext] mixin should not be [SecureContext]",
)
harness.ok(
results[0].members[2].getExtendedAttribute("SecureContext") is None,
"Attributes copied from non-[SecureContext] mixin should not be [SecureContext]",
)
harness.ok(
results[0].members[3].getExtendedAttribute("SecureContext") is None,
"Methods copied from non-[SecureContext] mixin should not be [SecureContext]",
)
# Test SecureContext and LegacyNoInterfaceObject
parser = parser.reset()
parser.parse(
"""
[LegacyNoInterfaceObject, SecureContext]
interface TestSecureContextLegacyNoInterfaceObject {
undefined testSecureMethod(byte foo);
};
"""
)
results = parser.finish()
harness.check(
len(results[0].members),
1,
"TestSecureContextLegacyNoInterfaceObject should have only one member",
)
harness.ok(
results[0].getExtendedAttribute("SecureContext"),
"Interface should have [SecureContext] extended attribute",
)
harness.ok(
results[0].members[0].getExtendedAttribute("SecureContext"),
"Interface member should have [SecureContext] extended attribute",
)

View File

@@ -0,0 +1,259 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch1 {
getter long long foo(long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch2 {
getter undefined foo(unsigned long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch3 {
getter boolean foo(unsigned long index, boolean extraArg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch4 {
getter boolean foo(unsigned long... index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch5 {
getter boolean foo(optional unsigned long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch6 {
getter boolean foo();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch7 {
deleter long long foo(long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch9 {
deleter boolean foo(unsigned long index, boolean extraArg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch10 {
deleter boolean foo(unsigned long... index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch11 {
deleter boolean foo(optional unsigned long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch12 {
deleter boolean foo();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch13 {
setter long long foo(long index, long long value);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch15 {
setter boolean foo(unsigned long index, boolean value, long long extraArg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch16 {
setter boolean foo(unsigned long index, boolean... value);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch17 {
setter boolean foo(unsigned long index, optional boolean value);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodSignatureMismatch18 {
setter boolean foo();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,117 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface SpecialMethods {
getter long long (unsigned long index);
setter long long (unsigned long index, long long value);
getter boolean (DOMString name);
setter boolean (DOMString name, boolean value);
deleter boolean (DOMString name);
readonly attribute unsigned long length;
};
interface SpecialMethodsCombination {
getter deleter boolean (DOMString name);
};
"""
)
results = parser.finish()
def checkMethod(
method,
QName,
name,
static=False,
getter=False,
setter=False,
deleter=False,
legacycaller=False,
stringifier=False,
):
harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
harness.check(method.identifier.QName(), QName, "Method has the right QName")
harness.check(method.identifier.name, name, "Method has the right name")
harness.check(method.isStatic(), static, "Method has the correct static value")
harness.check(method.isGetter(), getter, "Method has the correct getter value")
harness.check(method.isSetter(), setter, "Method has the correct setter value")
harness.check(
method.isDeleter(), deleter, "Method has the correct deleter value"
)
harness.check(
method.isLegacycaller(),
legacycaller,
"Method has the correct legacycaller value",
)
harness.check(
method.isStringifier(),
stringifier,
"Method has the correct stringifier value",
)
harness.check(len(results), 2, "Expect 2 interfaces")
iface = results[0]
harness.check(len(iface.members), 6, "Expect 6 members")
checkMethod(
iface.members[0],
"::SpecialMethods::__indexedgetter",
"__indexedgetter",
getter=True,
)
checkMethod(
iface.members[1],
"::SpecialMethods::__indexedsetter",
"__indexedsetter",
setter=True,
)
checkMethod(
iface.members[2],
"::SpecialMethods::__namedgetter",
"__namedgetter",
getter=True,
)
checkMethod(
iface.members[3],
"::SpecialMethods::__namedsetter",
"__namedsetter",
setter=True,
)
checkMethod(
iface.members[4],
"::SpecialMethods::__nameddeleter",
"__nameddeleter",
deleter=True,
)
iface = results[1]
harness.check(len(iface.members), 1, "Expect 1 member")
checkMethod(
iface.members[0],
"::SpecialMethodsCombination::__namedgetterdeleter",
"__namedgetterdeleter",
getter=True,
deleter=True,
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface IndexedDeleter {
deleter undefined(unsigned long index);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "There are no indexed deleters")

View File

@@ -0,0 +1,54 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface SpecialMethodUniqueness1 {
getter deleter boolean (DOMString name);
getter boolean (DOMString name);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodUniqueness1 {
deleter boolean (DOMString name);
getter deleter boolean (DOMString name);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
threw = False
try:
parser.parse(
"""
interface SpecialMethodUniqueness1 {
setter boolean (DOMString name);
setter boolean (DOMString name);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,196 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
interface TestStringifier {
stringifier;
};
"""
)
results = parser.finish()
harness.ok(
isinstance(results[0].members[0], WebIDL.IDLMethod),
"Stringifer should be method",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestStringifier {
stringifier;
stringifier;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow two 'stringifier;'")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestStringifier {
stringifier;
stringifier DOMString foo();
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow a 'stringifier;' and a 'stringifier()'")
parser = parser.reset()
parser.parse(
"""
interface TestStringifier {
stringifier attribute DOMString foo;
};
"""
)
results = parser.finish()
harness.ok(
isinstance(results[0].members[0], WebIDL.IDLAttribute),
"Stringifier attribute should be an attribute",
)
stringifier = results[0].members[1]
harness.ok(
isinstance(stringifier, WebIDL.IDLMethod),
"Stringifier attribute should insert a method",
)
harness.ok(stringifier.isStringifier(), "Inserted method should be a stringifier")
parser = parser.reset()
parser.parse(
"""
interface TestStringifier {};
interface mixin TestStringifierMixin {
stringifier attribute DOMString foo;
};
TestStringifier includes TestStringifierMixin;
"""
)
results = parser.finish()
harness.ok(
isinstance(results[0].members[0], WebIDL.IDLAttribute),
"Stringifier attribute should be an attribute",
)
stringifier = results[0].members[1]
harness.ok(
isinstance(stringifier, WebIDL.IDLMethod),
"Stringifier attribute should insert a method",
)
harness.ok(stringifier.isStringifier(), "Inserted method should be a stringifier")
parser = parser.reset()
parser.parse(
"""
interface TestStringifier {
stringifier attribute USVString foo;
};
"""
)
results = parser.finish()
stringifier = results[0].members[1]
harness.ok(
stringifier.signatures()[0][0].isUSVString(),
"Stringifier attributes should allow USVString",
)
parser = parser.reset()
parser.parse(
"""
interface TestStringifier {
[Throws, NeedsSubjectPrincipal]
stringifier attribute USVString foo;
};
"""
)
results = parser.finish()
stringifier = results[0].members[1]
harness.ok(
stringifier.getExtendedAttribute("Throws"),
"Stringifier attributes should support [Throws]",
)
harness.ok(
stringifier.getExtendedAttribute("NeedsSubjectPrincipal"),
"Stringifier attributes should support [NeedsSubjectPrincipal]",
)
parser = parser.reset()
parser.parse(
"""
interface TestStringifier {
stringifier attribute UTF8String foo;
};
"""
)
results = parser.finish()
stringifier = results[0].members[1]
harness.ok(
stringifier.signatures()[0][0].isUTF8String(),
"Stringifier attributes should allow UTF8String",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestStringifier {
stringifier attribute ByteString foo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow ByteString")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestStringifier {
stringifier;
stringifier attribute DOMString foo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow a 'stringifier;' and a stringifier attribute")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface TestStringifier {
stringifier attribute DOMString foo;
stringifier attribute DOMString bar;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow multiple stringifier attributes")

312
third_party/WebIDL/tests/test_toJSON.py vendored Normal file
View File

@@ -0,0 +1,312 @@
import WebIDL
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse(
"""
interface Test {
object toJSON();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should allow a toJSON method.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Test {
object toJSON(object arg);
object toJSON(long arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow overloads of a toJSON method.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Test {
object toJSON(object arg);
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should not allow a toJSON method with arguments.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Test {
long toJSON();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(not threw, "Should allow a toJSON method with 'long' as return type.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Test {
[Default] object toJSON();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
not threw, "Should allow a default toJSON method with 'object' as return type."
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Test {
[Default] long toJSON();
};
"""
)
parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should not allow a default toJSON method with non-'object' as return type.",
)
JsonTypes = [
"byte",
"octet",
"short",
"unsigned short",
"long",
"unsigned long",
"long long",
"unsigned long long",
"float",
"unrestricted float",
"double",
"unrestricted double",
"boolean",
"DOMString",
"ByteString",
"UTF8String",
"USVString",
"Enum",
"InterfaceWithToJSON",
"object",
]
nonJsonTypes = [
"InterfaceWithoutToJSON",
"any",
"Int8Array",
"Int16Array",
"Int32Array",
"Uint8Array",
"Uint16Array",
"Uint32Array",
"Uint8ClampedArray",
"Float32Array",
"Float64Array",
"ArrayBuffer",
]
def doTest(testIDL, shouldThrow, description):
p = parser.reset()
threw = False
try:
p.parse(
testIDL
+ """
enum Enum { "a", "b", "c" };
interface InterfaceWithToJSON { long toJSON(); };
interface InterfaceWithoutToJSON {};
"""
)
p.finish()
except Exception as x:
threw = True
harness.ok(x.message == "toJSON method has non-JSON return type", x)
harness.check(threw, shouldThrow, description)
for type in JsonTypes:
doTest(
"interface Test { %s toJSON(); };" % type,
False,
"%s should be a JSON type" % type,
)
doTest(
"interface Test { sequence<%s> toJSON(); };" % type,
False,
"sequence<%s> should be a JSON type" % type,
)
doTest(
"dictionary Foo { %s foo; }; " "interface Test { Foo toJSON(); }; " % type,
False,
"dictionary containing only JSON type (%s) should be a JSON type" % type,
)
doTest(
"dictionary Foo { %s foo; }; dictionary Bar : Foo { }; "
"interface Test { Bar toJSON(); }; " % type,
False,
"dictionary whose ancestors only contain JSON types should be a JSON type",
)
doTest(
"dictionary Foo { any foo; }; dictionary Bar : Foo { %s bar; };"
"interface Test { Bar toJSON(); };" % type,
True,
"dictionary whose ancestors contain non-JSON types should not be a JSON type",
)
doTest(
"interface Test { record<DOMString, %s> toJSON(); };" % type,
False,
"record<DOMString, %s> should be a JSON type" % type,
)
doTest(
"interface Test { record<ByteString, %s> toJSON(); };" % type,
False,
"record<ByteString, %s> should be a JSON type" % type,
)
doTest(
"interface Test { record<UTF8String, %s> toJSON(); };" % type,
False,
"record<UTF8String, %s> should be a JSON type" % type,
)
doTest(
"interface Test { record<USVString, %s> toJSON(); };" % type,
False,
"record<USVString, %s> should be a JSON type" % type,
)
otherUnionType = "Foo" if type != "object" else "long"
doTest(
"interface Foo { object toJSON(); };"
"interface Test { (%s or %s) toJSON(); };" % (otherUnionType, type),
False,
"union containing only JSON types (%s or %s) should be a JSON type"
% (otherUnionType, type),
)
doTest(
"interface test { %s? toJSON(); };" % type,
False,
"Nullable type (%s) should be a JSON type" % type,
)
doTest(
"interface Foo : InterfaceWithoutToJSON { %s toJSON(); };"
"interface Test { Foo toJSON(); };" % type,
False,
"interface with toJSON should be a JSON type",
)
doTest(
"interface Foo : InterfaceWithToJSON { };" "interface Test { Foo toJSON(); };",
False,
"inherited interface with toJSON should be a JSON type",
)
for type in nonJsonTypes:
doTest(
"interface Test { %s toJSON(); };" % type,
True,
"%s should not be a JSON type" % type,
)
doTest(
"interface Test { sequence<%s> toJSON(); };" % type,
True,
"sequence<%s> should not be a JSON type" % type,
)
doTest(
"dictionary Foo { %s foo; }; " "interface Test { Foo toJSON(); }; " % type,
True,
"Dictionary containing a non-JSON type (%s) should not be a JSON type"
% type,
)
doTest(
"dictionary Foo { %s foo; }; dictionary Bar : Foo { }; "
"interface Test { Bar toJSON(); }; " % type,
True,
"dictionary whose ancestors only contain non-JSON types should not be a JSON type",
)
doTest(
"interface Test { record<DOMString, %s> toJSON(); };" % type,
True,
"record<DOMString, %s> should not be a JSON type" % type,
)
doTest(
"interface Test { record<ByteString, %s> toJSON(); };" % type,
True,
"record<ByteString, %s> should not be a JSON type" % type,
)
doTest(
"interface Test { record<USVString, %s> toJSON(); };" % type,
True,
"record<USVString, %s> should not be a JSON type" % type,
)
if type != "any":
doTest(
"interface Foo { object toJSON(); }; "
"interface Test { (Foo or %s) toJSON(); };" % type,
True,
"union containing a non-JSON type (%s) should not be a JSON type"
% type,
)
doTest(
"interface test { %s? toJSON(); };" % type,
True,
"Nullable type (%s) should not be a JSON type" % type,
)
doTest(
"dictionary Foo { long foo; any bar; };" "interface Test { Foo toJSON(); };",
True,
"dictionary containing a non-JSON type should not be a JSON type",
)
doTest(
"interface Foo : InterfaceWithoutToJSON { }; "
"interface Test { Foo toJSON(); };",
True,
"interface without toJSON should not be a JSON type",
)

View File

@@ -0,0 +1,80 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
[TreatNonCallableAsNull] callback Function = any(any... arguments);
interface TestTreatNonCallableAsNull1 {
attribute Function? onfoo;
attribute Function onbar;
};
"""
)
results = parser.finish()
iface = results[1]
attr = iface.members[0]
harness.check(attr.type.treatNonCallableAsNull(), True, "Got the expected value")
attr = iface.members[1]
harness.check(attr.type.treatNonCallableAsNull(), False, "Got the expected value")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
callback Function = any(any... arguments);
interface TestTreatNonCallableAsNull2 {
[TreatNonCallableAsNull] attribute Function onfoo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
callback Function = any(any... arguments);
[TreatNonCallableAsNull]
interface TestTreatNonCallableAsNull3 {
attribute Function onfoo;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
[TreatNonCallableAsNull, LegacyTreatNonObjectAsNull]
callback Function = any(any... arguments);
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@@ -0,0 +1,97 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse(
"""
typedef long mylong;
typedef long? mynullablelong;
interface Foo {
const mylong X = 5;
undefined foo(optional mynullablelong arg = 7);
undefined bar(optional mynullablelong arg = null);
undefined baz(mylong arg);
};
"""
)
results = parser.finish()
harness.check(
results[2].members[1].signatures()[0][1][0].type.name,
"LongOrNull",
"Should expand typedefs",
)
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef long? mynullablelong;
interface Foo {
undefined foo(mynullablelong? Y);
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on nullable inside nullable arg.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
typedef long? mynullablelong;
interface Foo {
const mynullablelong? X = 5;
};
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(threw, "Should have thrown on nullable inside nullable const.")
parser = parser.reset()
threw = False
try:
parser.parse(
"""
interface Foo {
const mynullablelong? X = 5;
};
typedef long? mynullablelong;
"""
)
results = parser.finish()
except WebIDL.WebIDLError:
threw = True
harness.ok(
threw,
"Should have thrown on nullable inside nullable const typedef "
"after interface.",
)
parser = parser.reset()
parser.parse(
"""
interface Foo {
const mylong X = 5;
};
typedef long mylong;
"""
)
results = parser.finish()
harness.check(
results[0].members[0].type.name,
"Long",
"Should expand typedefs that come before interface",
)

View File

@@ -0,0 +1,19 @@
def WebIDLTest(parser, harness):
exception = None
try:
parser.parse(
"""
typedef long foo;
typedef long foo;
"""
)
parser.finish()
except Exception as e:
exception = e
harness.ok(exception, "Should have thrown.")
harness.ok(
"Multiple unresolvable definitions of identifier 'foo'" in str(exception),
"Should have a sane exception message",
)

Some files were not shown because too many files have changed in this diff Show More