Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions _clients/servo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
platforms: [android]
display_order: 5
webview: true
---
Servo aims to empower developers with a lightweight, high-performance alternative for embedding web technologies in applications.
6 changes: 6 additions & 0 deletions _clients/wpe_minibrowser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
platforms: [linux]
display_order: 4
webview: true
---
WPE is an official port of WebKit for embedded and low-consumption devices.
3 changes: 3 additions & 0 deletions _data/nicenames.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ family:
webview2: "WebView2"
chrome_android: "Chrome Browser"
safari_ios: "Safari Browser"
wpe_minibrowser: "WPE MiniBrowser"
servo: "Servo"
platform:
ios: "iOS"
android: "Android"
windows: "Windows"
macos: "macOS"
linux: "Linux"
support:
supported: "Supported"
mitigated: "Partial support"
Expand Down
8 changes: 7 additions & 1 deletion _js/_settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ class Settings {
const settingsString = this.getLocalStorage();
if (settingsString && settingsString !== '') {
const settings = settingsString.split('&');
// Collect the client names that have an explicit saved state.
// Clients added after the settings were last saved will not be in this
// set and should keep their HTML defaults (checked if webview: true).
const savedNames = new Set(settings.map(s => s.split('=')[0]));
this.panel.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
checkbox.checked = false;
if (savedNames.has(checkbox.name)) {
checkbox.checked = false;
}
});
if (settings.length > 0) {
settings.forEach(setting => {
Expand Down
10 changes: 9 additions & 1 deletion _layouts/feature.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ <h3 class="data-family-name">
{% endif %}
</span>
</h3>
{% assign bcd-test-meta = site.data.bcd_test_meta[family-key] %}
<div class="data-client-list">
{% if family-values != nil %}
{% for platform in family-values %}
Expand Down Expand Up @@ -117,7 +118,14 @@ <h4 class="data-platform-name">
{% assign stat-class-name = '' -%}
{% endcase %}
<div class="data-version {{ stat-class-name }}">
<span class="data-version-number">{{ version-key }}</span>
{% if bcd-test-meta %}
<a class="data-version-number" href="{{ bcd-test-meta.source_url }}"
title="Support data from automated BCD compatibility tests"
target="_blank" rel="noopener noreferrer">{{ version-key }}</a>
{% else %}
<span class="data-version-number"
{% if site.data.bcd_version %}title="From BCD package version {{ site.data.bcd_version }}"{% endif %}>{{ version-key }}</span>
{% endif %}
<span class="data-version-badge {{ stat-class-name }}"
aria-label="{{ site.data.nicenames.support[stat-class-name] }}"></span>
{% if version-notes.size > 0 %}
Expand Down
127 changes: 110 additions & 17 deletions _plugins/generated_features.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'httparty'
require 'json'
require 'time'

module Generated
class FeaturesGenerator < Jekyll::Generator
Expand Down Expand Up @@ -85,14 +86,94 @@ def getVersions(feature, platform)
}
end

def generate_bcd_from_section(site, section, timestamp, category, appended_title = "")
section.keys.each do |title|
def parse_bcd_collector_results(parsed)
# Raw mdn-bcd-collector output: results is keyed by test URL → array of
# { exposure, name, result } entries. We only record Window-exposed results.
bcd_map = {}
return bcd_map unless parsed.key?('results')
parsed['results'].each do |_url, tests|
next unless tests.kind_of?(Array)
tests.each do |test|
next unless test['exposure'] == 'Window'
key = test['name']
next if bcd_map.key?(key)
bcd_map[key] = test['result'] == true ? 'y' : 'n'
end
end
bcd_map
end

def fetch_wpe_minibrowser_results
distilled_url = "https://wpewebkit.org/wptreport-distilled-data/bcd_results-wpewebkit_minibrowser-nightly-tot.json"
distilled = JSON.parse(HTTParty.get(distilled_url).body)
metadata = distilled['metadata'] || {}
source_urls = metadata['x_source_results_urls'] || []
collector_url = source_urls.first
return nil unless collector_url
collector = JSON.parse(HTTParty.get(collector_url).body)
version = if metadata['testrun_timestamp_end']
Time.at(metadata['testrun_timestamp_end']).utc.strftime('%Y-%m-%d')
else
'latest'
end
{
'results' => parse_bcd_collector_results(collector),
'version' => version,
'source_url' => 'https://wpewebkit.org/wpt-status/?src=BCD',
}
rescue => e
Jekyll.logger.warn "webview-bcd-results:", "Failed to fetch WPE results: #{e.message}"
nil
end

def extract_version_from_results(file_name)
# Date embedded in filename (e.g. latest-servo-2026-03-27.json)
file_name =~ /(\d{4}-\d{2}-\d{2})/ ? $1 : nil
end

def fetch_latest_webview_results
results = {}

# WPE MiniBrowser: fetch the distilled data published by the WPE project,
# then download the raw mdn-bcd-collector results it points to.
wpe = fetch_wpe_minibrowser_results
results['wpe_minibrowser'] = wpe if wpe

# Servo and any other latest-* files come from the WebView-CG repo.
api_url = "https://api.github.com/repos/WebView-CG/webview-bcd-results/contents/results"
response = HTTParty.get(api_url, headers: { "User-Agent" => "CanIWebView-build" })
files = JSON.parse(response.body)
files.each do |file|
name = file['name']
next unless name.start_with?('latest-servo')
begin
content = HTTParty.get(file['download_url']).body
parsed = JSON.parse(content)
version = extract_version_from_results(name) || 'latest'
results['servo'] = {
'results' => parse_bcd_collector_results(parsed),
'version' => version,
'source_url' => file['html_url'],
}
rescue => e
Jekyll.logger.warn "webview-bcd-results:", "Failed to process #{name}: #{e.message}"
end
end
results
rescue => e
Jekyll.logger.warn "webview-bcd-results:", "Failed to fetch latest results: #{e.message}"
{}
end

def generate_bcd_from_section(site, section, timestamp, category, appended_title = "", bcd_prefix = "", latest_results = {})
section.keys.each do |key|
# We skip potential special keys since we can iterate over sub sections
next unless title.index("__") != 0
next unless key.index("__") != 0

feature = section[title]
feature = section[key]

title = "#{appended_title}#{title}"
bcd_key = bcd_prefix.empty? ? key : "#{bcd_prefix}.#{key}"
title = "#{appended_title}#{key}"
slug = "mdn-#{title.downcase.strip.gsub('-', '').gsub(/[\_|\s]/, '-').gsub(':', '')}"
path = site.in_source_dir("_generated_features/#{slug}.md")
doc = Jekyll::Document.new(path, {
Expand All @@ -114,7 +195,7 @@ def generate_bcd_from_section(site, section, timestamp, category, appended_title
impl_urls = getImplUrls(feature)
doc.data['links'] = links.merge(impl_urls)
doc.data['has_impl_urls'] = !impl_urls.empty?
doc.data['stats'] = {
stats = {
"wkwebview" => {
"macos" => {
"*" => "u"
Expand All @@ -136,42 +217,54 @@ def generate_bcd_from_section(site, section, timestamp, category, appended_title
"ios" => getVersions(feature, "safari_ios")
},
}
latest_results.each do |client, data|
platform = client == "wpe_minibrowser" ? "linux" : "android"
support = data['results'].fetch(bcd_key, "u")
version = data['version']
stats[client] = { platform => { version => support } }
end
doc.data['stats'] = stats

site.collections['generated_features'].docs << doc
end
end

def generate_bcd(site)
version = File.read("bcd_version")
version = File.read("bcd_version").strip
bcd = HTTParty.get("http://unpkg.com/@mdn/browser-compat-data@#{version}/data.json").body
parsed_bcd = JSON.parse(bcd)

timestamp = parsed_bcd['__meta']['timestamp']
site.data['bcd_version'] = version

latest_results = fetch_latest_webview_results()

site.data['bcd_test_meta'] = latest_results

generate_bcd_from_section(site, parsed_bcd['api'],
timestamp, "js")
timestamp, "js", "", "api", latest_results)
generate_bcd_from_section(site, parsed_bcd['javascript']['builtins'],
timestamp, "js", "JavaScript built-in: ")
timestamp, "js", "JavaScript built-in: ", "javascript.builtins", latest_results)
generate_bcd_from_section(site, parsed_bcd['html']['elements'],
timestamp, "html", "HTML element: ")
timestamp, "html", "HTML element: ", "html.elements", latest_results)
generate_bcd_from_section(site, parsed_bcd['html']['global_attributes'],
timestamp, "html", "HTML attribute: ")
timestamp, "html", "HTML attribute: ", "html.global_attributes", latest_results)
generate_bcd_from_section(site, parsed_bcd['manifests']['webapp'],
timestamp, "html", "HTML manifest: ")
timestamp, "html", "HTML manifest: ", "manifests.webapp", latest_results)
generate_bcd_from_section(site, parsed_bcd['css']['selectors'],
timestamp, "css", "CSS selector: ")
timestamp, "css", "CSS selector: ", "css.selectors", latest_results)
generate_bcd_from_section(site, parsed_bcd['css']['properties'],
timestamp, "css", "CSS property: ")
timestamp, "css", "CSS property: ", "css.properties", latest_results)
generate_bcd_from_section(site, parsed_bcd['http']['headers'],
timestamp, "http", "HTTP header: ")
timestamp, "http", "HTTP header: ", "http.headers", latest_results)
generate_bcd_from_section(site, parsed_bcd['http']['status'],
timestamp, "http", "HTTP status code: ")
timestamp, "http", "HTTP status code: ", "http.status", latest_results)

# The css types can have sub fields so we iterate over these and then
# generate sections.
parsed_bcd['css']['types'].keys.each do |type|
generate_bcd_from_section(site, parsed_bcd['css']['types'][type],
timestamp, "css", "CSS type: #{type}: ")
timestamp, "css", "CSS type: #{type}: ", "css.types.#{type}", latest_results)
end
end

Expand Down